xref: /dpdk/drivers/net/virtio/virtio_pci.c (revision 6d890f8ab51295045a53f41c4d2654bb1f01cf38)
16c3169a3SBruce Richardson /*-
26c3169a3SBruce Richardson  *   BSD LICENSE
36c3169a3SBruce Richardson  *
46c3169a3SBruce Richardson  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
56c3169a3SBruce Richardson  *   All rights reserved.
66c3169a3SBruce Richardson  *
76c3169a3SBruce Richardson  *   Redistribution and use in source and binary forms, with or without
86c3169a3SBruce Richardson  *   modification, are permitted provided that the following conditions
96c3169a3SBruce Richardson  *   are met:
106c3169a3SBruce Richardson  *
116c3169a3SBruce Richardson  *     * Redistributions of source code must retain the above copyright
126c3169a3SBruce Richardson  *       notice, this list of conditions and the following disclaimer.
136c3169a3SBruce Richardson  *     * Redistributions in binary form must reproduce the above copyright
146c3169a3SBruce Richardson  *       notice, this list of conditions and the following disclaimer in
156c3169a3SBruce Richardson  *       the documentation and/or other materials provided with the
166c3169a3SBruce Richardson  *       distribution.
176c3169a3SBruce Richardson  *     * Neither the name of Intel Corporation nor the names of its
186c3169a3SBruce Richardson  *       contributors may be used to endorse or promote products derived
196c3169a3SBruce Richardson  *       from this software without specific prior written permission.
206c3169a3SBruce Richardson  *
216c3169a3SBruce Richardson  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
226c3169a3SBruce Richardson  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
236c3169a3SBruce Richardson  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
246c3169a3SBruce Richardson  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
256c3169a3SBruce Richardson  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
266c3169a3SBruce Richardson  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
276c3169a3SBruce Richardson  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
286c3169a3SBruce Richardson  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
296c3169a3SBruce Richardson  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
306c3169a3SBruce Richardson  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
316c3169a3SBruce Richardson  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
326c3169a3SBruce Richardson  */
336c3169a3SBruce Richardson #include <stdint.h>
346c3169a3SBruce Richardson 
35c52afa68SYuanhan Liu #ifdef RTE_EXEC_ENV_LINUXAPP
36c52afa68SYuanhan Liu  #include <dirent.h>
37c52afa68SYuanhan Liu  #include <fcntl.h>
38c52afa68SYuanhan Liu #endif
39c52afa68SYuanhan Liu 
406c3169a3SBruce Richardson #include "virtio_pci.h"
416c3169a3SBruce Richardson #include "virtio_logs.h"
42d5bbeefcSYuanhan Liu #include "virtqueue.h"
436c3169a3SBruce Richardson 
446ba1f63bSYuanhan Liu /*
456ba1f63bSYuanhan Liu  * Following macros are derived from linux/pci_regs.h, however,
466ba1f63bSYuanhan Liu  * we can't simply include that header here, as there is no such
476ba1f63bSYuanhan Liu  * file for non-Linux platform.
486ba1f63bSYuanhan Liu  */
496ba1f63bSYuanhan Liu #define PCI_CAPABILITY_LIST	0x34
506ba1f63bSYuanhan Liu #define PCI_CAP_ID_VNDR		0x09
516ba1f63bSYuanhan Liu 
52b8f04520SDavid Marchand /*
53b8f04520SDavid Marchand  * The remaining space is defined by each driver as the per-driver
54b8f04520SDavid Marchand  * configuration space.
55b8f04520SDavid Marchand  */
56b8f04520SDavid Marchand #define VIRTIO_PCI_CONFIG(hw) (((hw)->use_msix) ? 24 : 20)
57b86af7b1SYuanhan Liu 
58595454c5SJianfeng Tan static inline int
59595454c5SJianfeng Tan check_vq_phys_addr_ok(struct virtqueue *vq)
60595454c5SJianfeng Tan {
61595454c5SJianfeng Tan 	/* Virtio PCI device VIRTIO_PCI_QUEUE_PF register is 32bit,
62595454c5SJianfeng Tan 	 * and only accepts 32 bit page frame number.
63595454c5SJianfeng Tan 	 * Check if the allocated physical memory exceeds 16TB.
64595454c5SJianfeng Tan 	 */
65595454c5SJianfeng Tan 	if ((vq->vq_ring_mem + vq->vq_ring_size - 1) >>
66595454c5SJianfeng Tan 			(VIRTIO_PCI_QUEUE_ADDR_SHIFT + 32)) {
67595454c5SJianfeng Tan 		PMD_INIT_LOG(ERR, "vring address shouldn't be above 16TB!");
68595454c5SJianfeng Tan 		return 0;
69595454c5SJianfeng Tan 	}
70595454c5SJianfeng Tan 
71595454c5SJianfeng Tan 	return 1;
72595454c5SJianfeng Tan }
73595454c5SJianfeng Tan 
74281ccccbSDavid Marchand /*
75281ccccbSDavid Marchand  * Since we are in legacy mode:
76281ccccbSDavid Marchand  * http://ozlabs.org/~rusty/virtio-spec/virtio-0.9.5.pdf
77281ccccbSDavid Marchand  *
78281ccccbSDavid Marchand  * "Note that this is possible because while the virtio header is PCI (i.e.
79281ccccbSDavid Marchand  * little) endian, the device-specific region is encoded in the native endian of
80281ccccbSDavid Marchand  * the guest (where such distinction is applicable)."
81281ccccbSDavid Marchand  *
82281ccccbSDavid Marchand  * For powerpc which supports both, qemu supposes that cpu is big endian and
83281ccccbSDavid Marchand  * enforces this for the virtio-net stuff.
84281ccccbSDavid Marchand  */
85d5bbeefcSYuanhan Liu static void
86d5bbeefcSYuanhan Liu legacy_read_dev_config(struct virtio_hw *hw, size_t offset,
876c3169a3SBruce Richardson 		       void *dst, int length)
886c3169a3SBruce Richardson {
89281ccccbSDavid Marchand #ifdef RTE_ARCH_PPC_64
90281ccccbSDavid Marchand 	int size;
91281ccccbSDavid Marchand 
92281ccccbSDavid Marchand 	while (length > 0) {
93281ccccbSDavid Marchand 		if (length >= 4) {
94281ccccbSDavid Marchand 			size = 4;
951ca893f1SYuanhan Liu 			rte_eal_pci_ioport_read(VTPCI_IO(hw), dst, size,
96281ccccbSDavid Marchand 				VIRTIO_PCI_CONFIG(hw) + offset);
97281ccccbSDavid Marchand 			*(uint32_t *)dst = rte_be_to_cpu_32(*(uint32_t *)dst);
98281ccccbSDavid Marchand 		} else if (length >= 2) {
99281ccccbSDavid Marchand 			size = 2;
1001ca893f1SYuanhan Liu 			rte_eal_pci_ioport_read(VTPCI_IO(hw), dst, size,
101281ccccbSDavid Marchand 				VIRTIO_PCI_CONFIG(hw) + offset);
102281ccccbSDavid Marchand 			*(uint16_t *)dst = rte_be_to_cpu_16(*(uint16_t *)dst);
103281ccccbSDavid Marchand 		} else {
104281ccccbSDavid Marchand 			size = 1;
1051ca893f1SYuanhan Liu 			rte_eal_pci_ioport_read(VTPCI_IO(hw), dst, size,
106281ccccbSDavid Marchand 				VIRTIO_PCI_CONFIG(hw) + offset);
107281ccccbSDavid Marchand 		}
108281ccccbSDavid Marchand 
109281ccccbSDavid Marchand 		dst = (char *)dst + size;
110281ccccbSDavid Marchand 		offset += size;
111281ccccbSDavid Marchand 		length -= size;
112281ccccbSDavid Marchand 	}
113281ccccbSDavid Marchand #else
1141ca893f1SYuanhan Liu 	rte_eal_pci_ioport_read(VTPCI_IO(hw), dst, length,
115b8f04520SDavid Marchand 				VIRTIO_PCI_CONFIG(hw) + offset);
116281ccccbSDavid Marchand #endif
1176c3169a3SBruce Richardson }
1186c3169a3SBruce Richardson 
119d5bbeefcSYuanhan Liu static void
120d5bbeefcSYuanhan Liu legacy_write_dev_config(struct virtio_hw *hw, size_t offset,
121d5bbeefcSYuanhan Liu 			const void *src, int length)
1226c3169a3SBruce Richardson {
123281ccccbSDavid Marchand #ifdef RTE_ARCH_PPC_64
124281ccccbSDavid Marchand 	union {
125281ccccbSDavid Marchand 		uint32_t u32;
126281ccccbSDavid Marchand 		uint16_t u16;
127281ccccbSDavid Marchand 	} tmp;
128281ccccbSDavid Marchand 	int size;
129281ccccbSDavid Marchand 
130281ccccbSDavid Marchand 	while (length > 0) {
131281ccccbSDavid Marchand 		if (length >= 4) {
132281ccccbSDavid Marchand 			size = 4;
133281ccccbSDavid Marchand 			tmp.u32 = rte_cpu_to_be_32(*(const uint32_t *)src);
1341ca893f1SYuanhan Liu 			rte_eal_pci_ioport_write(VTPCI_IO(hw), &tmp.u32, size,
135281ccccbSDavid Marchand 				VIRTIO_PCI_CONFIG(hw) + offset);
136281ccccbSDavid Marchand 		} else if (length >= 2) {
137281ccccbSDavid Marchand 			size = 2;
138281ccccbSDavid Marchand 			tmp.u16 = rte_cpu_to_be_16(*(const uint16_t *)src);
1391ca893f1SYuanhan Liu 			rte_eal_pci_ioport_write(VTPCI_IO(hw), &tmp.u16, size,
140281ccccbSDavid Marchand 				VIRTIO_PCI_CONFIG(hw) + offset);
141281ccccbSDavid Marchand 		} else {
142281ccccbSDavid Marchand 			size = 1;
1431ca893f1SYuanhan Liu 			rte_eal_pci_ioport_write(VTPCI_IO(hw), src, size,
144281ccccbSDavid Marchand 				VIRTIO_PCI_CONFIG(hw) + offset);
145281ccccbSDavid Marchand 		}
146281ccccbSDavid Marchand 
147281ccccbSDavid Marchand 		src = (const char *)src + size;
148281ccccbSDavid Marchand 		offset += size;
149281ccccbSDavid Marchand 		length -= size;
150281ccccbSDavid Marchand 	}
151281ccccbSDavid Marchand #else
1521ca893f1SYuanhan Liu 	rte_eal_pci_ioport_write(VTPCI_IO(hw), src, length,
153b8f04520SDavid Marchand 				 VIRTIO_PCI_CONFIG(hw) + offset);
154281ccccbSDavid Marchand #endif
1556c3169a3SBruce Richardson }
1566c3169a3SBruce Richardson 
1573891f233SYuanhan Liu static uint64_t
158d5bbeefcSYuanhan Liu legacy_get_features(struct virtio_hw *hw)
159d5bbeefcSYuanhan Liu {
16036ea36efSYuanhan Liu 	uint32_t dst;
161b8f04520SDavid Marchand 
1621ca893f1SYuanhan Liu 	rte_eal_pci_ioport_read(VTPCI_IO(hw), &dst, 4,
1631ca893f1SYuanhan Liu 				VIRTIO_PCI_HOST_FEATURES);
164b8f04520SDavid Marchand 	return dst;
165d5bbeefcSYuanhan Liu }
166d5bbeefcSYuanhan Liu 
167d5bbeefcSYuanhan Liu static void
1683891f233SYuanhan Liu legacy_set_features(struct virtio_hw *hw, uint64_t features)
169d5bbeefcSYuanhan Liu {
1703891f233SYuanhan Liu 	if ((features >> 32) != 0) {
1713891f233SYuanhan Liu 		PMD_DRV_LOG(ERR,
1723891f233SYuanhan Liu 			"only 32 bit features are allowed for legacy virtio!");
1733891f233SYuanhan Liu 		return;
1743891f233SYuanhan Liu 	}
1751ca893f1SYuanhan Liu 	rte_eal_pci_ioport_write(VTPCI_IO(hw), &features, 4,
176b8f04520SDavid Marchand 				 VIRTIO_PCI_GUEST_FEATURES);
177d5bbeefcSYuanhan Liu }
178d5bbeefcSYuanhan Liu 
179d5bbeefcSYuanhan Liu static uint8_t
180d5bbeefcSYuanhan Liu legacy_get_status(struct virtio_hw *hw)
181d5bbeefcSYuanhan Liu {
182b8f04520SDavid Marchand 	uint8_t dst;
183b8f04520SDavid Marchand 
1841ca893f1SYuanhan Liu 	rte_eal_pci_ioport_read(VTPCI_IO(hw), &dst, 1, VIRTIO_PCI_STATUS);
185b8f04520SDavid Marchand 	return dst;
186d5bbeefcSYuanhan Liu }
187d5bbeefcSYuanhan Liu 
188d5bbeefcSYuanhan Liu static void
189d5bbeefcSYuanhan Liu legacy_set_status(struct virtio_hw *hw, uint8_t status)
190d5bbeefcSYuanhan Liu {
1911ca893f1SYuanhan Liu 	rte_eal_pci_ioport_write(VTPCI_IO(hw), &status, 1, VIRTIO_PCI_STATUS);
192d5bbeefcSYuanhan Liu }
193d5bbeefcSYuanhan Liu 
194d5bbeefcSYuanhan Liu static void
195d5bbeefcSYuanhan Liu legacy_reset(struct virtio_hw *hw)
196d5bbeefcSYuanhan Liu {
197d5bbeefcSYuanhan Liu 	legacy_set_status(hw, VIRTIO_CONFIG_STATUS_RESET);
198d5bbeefcSYuanhan Liu }
199d5bbeefcSYuanhan Liu 
200d5bbeefcSYuanhan Liu static uint8_t
201d5bbeefcSYuanhan Liu legacy_get_isr(struct virtio_hw *hw)
202d5bbeefcSYuanhan Liu {
203b8f04520SDavid Marchand 	uint8_t dst;
204b8f04520SDavid Marchand 
2051ca893f1SYuanhan Liu 	rte_eal_pci_ioport_read(VTPCI_IO(hw), &dst, 1, VIRTIO_PCI_ISR);
206b8f04520SDavid Marchand 	return dst;
207d5bbeefcSYuanhan Liu }
208d5bbeefcSYuanhan Liu 
209d5bbeefcSYuanhan Liu /* Enable one vector (0) for Link State Intrerrupt */
210d5bbeefcSYuanhan Liu static uint16_t
211d5bbeefcSYuanhan Liu legacy_set_config_irq(struct virtio_hw *hw, uint16_t vec)
212d5bbeefcSYuanhan Liu {
213b8f04520SDavid Marchand 	uint16_t dst;
214b8f04520SDavid Marchand 
2151ca893f1SYuanhan Liu 	rte_eal_pci_ioport_write(VTPCI_IO(hw), &vec, 2,
2161ca893f1SYuanhan Liu 				 VIRTIO_MSI_CONFIG_VECTOR);
2171ca893f1SYuanhan Liu 	rte_eal_pci_ioport_read(VTPCI_IO(hw), &dst, 2,
2181ca893f1SYuanhan Liu 				VIRTIO_MSI_CONFIG_VECTOR);
219b8f04520SDavid Marchand 	return dst;
220d5bbeefcSYuanhan Liu }
221d5bbeefcSYuanhan Liu 
222d5bbeefcSYuanhan Liu static uint16_t
223d5bbeefcSYuanhan Liu legacy_get_queue_num(struct virtio_hw *hw, uint16_t queue_id)
224d5bbeefcSYuanhan Liu {
225b8f04520SDavid Marchand 	uint16_t dst;
226b8f04520SDavid Marchand 
2271ca893f1SYuanhan Liu 	rte_eal_pci_ioport_write(VTPCI_IO(hw), &queue_id, 2,
2281ca893f1SYuanhan Liu 				 VIRTIO_PCI_QUEUE_SEL);
2291ca893f1SYuanhan Liu 	rte_eal_pci_ioport_read(VTPCI_IO(hw), &dst, 2, VIRTIO_PCI_QUEUE_NUM);
230b8f04520SDavid Marchand 	return dst;
231d5bbeefcSYuanhan Liu }
232d5bbeefcSYuanhan Liu 
233595454c5SJianfeng Tan static int
234d5bbeefcSYuanhan Liu legacy_setup_queue(struct virtio_hw *hw, struct virtqueue *vq)
235d5bbeefcSYuanhan Liu {
236b8f04520SDavid Marchand 	uint32_t src;
237d5bbeefcSYuanhan Liu 
238595454c5SJianfeng Tan 	if (!check_vq_phys_addr_ok(vq))
239595454c5SJianfeng Tan 		return -1;
240595454c5SJianfeng Tan 
2411ca893f1SYuanhan Liu 	rte_eal_pci_ioport_write(VTPCI_IO(hw), &vq->vq_queue_index, 2,
242b8f04520SDavid Marchand 			 VIRTIO_PCI_QUEUE_SEL);
24301ad44fdSHuawei Xie 	src = vq->vq_ring_mem >> VIRTIO_PCI_QUEUE_ADDR_SHIFT;
2441ca893f1SYuanhan Liu 	rte_eal_pci_ioport_write(VTPCI_IO(hw), &src, 4, VIRTIO_PCI_QUEUE_PFN);
245595454c5SJianfeng Tan 
246595454c5SJianfeng Tan 	return 0;
247d5bbeefcSYuanhan Liu }
248d5bbeefcSYuanhan Liu 
249d5bbeefcSYuanhan Liu static void
250d5bbeefcSYuanhan Liu legacy_del_queue(struct virtio_hw *hw, struct virtqueue *vq)
251d5bbeefcSYuanhan Liu {
252b8f04520SDavid Marchand 	uint32_t src = 0;
253d5bbeefcSYuanhan Liu 
2541ca893f1SYuanhan Liu 	rte_eal_pci_ioport_write(VTPCI_IO(hw), &vq->vq_queue_index, 2,
255b8f04520SDavid Marchand 			 VIRTIO_PCI_QUEUE_SEL);
2561ca893f1SYuanhan Liu 	rte_eal_pci_ioport_write(VTPCI_IO(hw), &src, 4, VIRTIO_PCI_QUEUE_PFN);
257d5bbeefcSYuanhan Liu }
258d5bbeefcSYuanhan Liu 
259d5bbeefcSYuanhan Liu static void
260d5bbeefcSYuanhan Liu legacy_notify_queue(struct virtio_hw *hw, struct virtqueue *vq)
261d5bbeefcSYuanhan Liu {
2621ca893f1SYuanhan Liu 	rte_eal_pci_ioport_write(VTPCI_IO(hw), &vq->vq_queue_index, 2,
263b8f04520SDavid Marchand 			 VIRTIO_PCI_QUEUE_NOTIFY);
264d5bbeefcSYuanhan Liu }
265d5bbeefcSYuanhan Liu 
266c52afa68SYuanhan Liu #ifdef RTE_EXEC_ENV_LINUXAPP
267c52afa68SYuanhan Liu static int
268c52afa68SYuanhan Liu legacy_virtio_has_msix(const struct rte_pci_addr *loc)
269c52afa68SYuanhan Liu {
270c52afa68SYuanhan Liu 	DIR *d;
271c52afa68SYuanhan Liu 	char dirname[PATH_MAX];
272c52afa68SYuanhan Liu 
273c52afa68SYuanhan Liu 	snprintf(dirname, sizeof(dirname),
27453c3c30cSJan Viktorin 		     "%s/" PCI_PRI_FMT "/msi_irqs", pci_get_sysfs_path(),
275c52afa68SYuanhan Liu 		     loc->domain, loc->bus, loc->devid, loc->function);
276c52afa68SYuanhan Liu 
277c52afa68SYuanhan Liu 	d = opendir(dirname);
278c52afa68SYuanhan Liu 	if (d)
279c52afa68SYuanhan Liu 		closedir(d);
280c52afa68SYuanhan Liu 
281693f715dSHuawei Xie 	return d != NULL;
282c52afa68SYuanhan Liu }
283c52afa68SYuanhan Liu #else
284c52afa68SYuanhan Liu static int
28525294cd3SDavid Marchand legacy_virtio_has_msix(const struct rte_pci_addr *loc __rte_unused)
286c52afa68SYuanhan Liu {
287c52afa68SYuanhan Liu 	/* nic_uio does not enable interrupts, return 0 (false). */
288c52afa68SYuanhan Liu 	return 0;
289c52afa68SYuanhan Liu }
290b8f04520SDavid Marchand #endif
291c52afa68SYuanhan Liu 
292c52afa68SYuanhan Liu static int
293b8f04520SDavid Marchand legacy_virtio_resource_init(struct rte_pci_device *pci_dev,
29462a785a6SJianfeng Tan 			    struct virtio_hw *hw, uint32_t *dev_flags)
295c52afa68SYuanhan Liu {
2961ca893f1SYuanhan Liu 	if (rte_eal_pci_ioport_map(pci_dev, 0, VTPCI_IO(hw)) < 0)
297b8f04520SDavid Marchand 		return -1;
298b8f04520SDavid Marchand 
299b8f04520SDavid Marchand 	if (pci_dev->intr_handle.type != RTE_INTR_HANDLE_UNKNOWN)
30062a785a6SJianfeng Tan 		*dev_flags |= RTE_ETH_DEV_INTR_LSC;
301b8f04520SDavid Marchand 	else
30262a785a6SJianfeng Tan 		*dev_flags &= ~RTE_ETH_DEV_INTR_LSC;
303b8f04520SDavid Marchand 
304c52afa68SYuanhan Liu 	return 0;
305c52afa68SYuanhan Liu }
306d5bbeefcSYuanhan Liu 
307*6d890f8aSYuanhan Liu const struct virtio_pci_ops legacy_ops = {
308d5bbeefcSYuanhan Liu 	.read_dev_cfg	= legacy_read_dev_config,
309d5bbeefcSYuanhan Liu 	.write_dev_cfg	= legacy_write_dev_config,
310d5bbeefcSYuanhan Liu 	.reset		= legacy_reset,
311d5bbeefcSYuanhan Liu 	.get_status	= legacy_get_status,
312d5bbeefcSYuanhan Liu 	.set_status	= legacy_set_status,
313d5bbeefcSYuanhan Liu 	.get_features	= legacy_get_features,
314d5bbeefcSYuanhan Liu 	.set_features	= legacy_set_features,
315d5bbeefcSYuanhan Liu 	.get_isr	= legacy_get_isr,
316d5bbeefcSYuanhan Liu 	.set_config_irq	= legacy_set_config_irq,
317d5bbeefcSYuanhan Liu 	.get_queue_num	= legacy_get_queue_num,
318d5bbeefcSYuanhan Liu 	.setup_queue	= legacy_setup_queue,
319d5bbeefcSYuanhan Liu 	.del_queue	= legacy_del_queue,
320d5bbeefcSYuanhan Liu 	.notify_queue	= legacy_notify_queue,
321d5bbeefcSYuanhan Liu };
322d5bbeefcSYuanhan Liu 
323d5bbeefcSYuanhan Liu 
3246ba1f63bSYuanhan Liu static inline uint8_t
3256ba1f63bSYuanhan Liu io_read8(uint8_t *addr)
3266ba1f63bSYuanhan Liu {
3276ba1f63bSYuanhan Liu 	return *(volatile uint8_t *)addr;
3286ba1f63bSYuanhan Liu }
3296ba1f63bSYuanhan Liu 
3306ba1f63bSYuanhan Liu static inline void
3316ba1f63bSYuanhan Liu io_write8(uint8_t val, uint8_t *addr)
3326ba1f63bSYuanhan Liu {
3336ba1f63bSYuanhan Liu 	*(volatile uint8_t *)addr = val;
3346ba1f63bSYuanhan Liu }
3356ba1f63bSYuanhan Liu 
3366ba1f63bSYuanhan Liu static inline uint16_t
3376ba1f63bSYuanhan Liu io_read16(uint16_t *addr)
3386ba1f63bSYuanhan Liu {
3396ba1f63bSYuanhan Liu 	return *(volatile uint16_t *)addr;
3406ba1f63bSYuanhan Liu }
3416ba1f63bSYuanhan Liu 
3426ba1f63bSYuanhan Liu static inline void
3436ba1f63bSYuanhan Liu io_write16(uint16_t val, uint16_t *addr)
3446ba1f63bSYuanhan Liu {
3456ba1f63bSYuanhan Liu 	*(volatile uint16_t *)addr = val;
3466ba1f63bSYuanhan Liu }
3476ba1f63bSYuanhan Liu 
3486ba1f63bSYuanhan Liu static inline uint32_t
3496ba1f63bSYuanhan Liu io_read32(uint32_t *addr)
3506ba1f63bSYuanhan Liu {
3516ba1f63bSYuanhan Liu 	return *(volatile uint32_t *)addr;
3526ba1f63bSYuanhan Liu }
3536ba1f63bSYuanhan Liu 
3546ba1f63bSYuanhan Liu static inline void
3556ba1f63bSYuanhan Liu io_write32(uint32_t val, uint32_t *addr)
3566ba1f63bSYuanhan Liu {
3576ba1f63bSYuanhan Liu 	*(volatile uint32_t *)addr = val;
3586ba1f63bSYuanhan Liu }
3596ba1f63bSYuanhan Liu 
3606ba1f63bSYuanhan Liu static inline void
3616ba1f63bSYuanhan Liu io_write64_twopart(uint64_t val, uint32_t *lo, uint32_t *hi)
3626ba1f63bSYuanhan Liu {
3636ba1f63bSYuanhan Liu 	io_write32(val & ((1ULL << 32) - 1), lo);
3646ba1f63bSYuanhan Liu 	io_write32(val >> 32,		     hi);
3656ba1f63bSYuanhan Liu }
3666ba1f63bSYuanhan Liu 
3676ba1f63bSYuanhan Liu static void
3686ba1f63bSYuanhan Liu modern_read_dev_config(struct virtio_hw *hw, size_t offset,
3696ba1f63bSYuanhan Liu 		       void *dst, int length)
3706ba1f63bSYuanhan Liu {
3716ba1f63bSYuanhan Liu 	int i;
3726ba1f63bSYuanhan Liu 	uint8_t *p;
3736ba1f63bSYuanhan Liu 	uint8_t old_gen, new_gen;
3746ba1f63bSYuanhan Liu 
3756ba1f63bSYuanhan Liu 	do {
3766ba1f63bSYuanhan Liu 		old_gen = io_read8(&hw->common_cfg->config_generation);
3776ba1f63bSYuanhan Liu 
3786ba1f63bSYuanhan Liu 		p = dst;
3796ba1f63bSYuanhan Liu 		for (i = 0;  i < length; i++)
3806ba1f63bSYuanhan Liu 			*p++ = io_read8((uint8_t *)hw->dev_cfg + offset + i);
3816ba1f63bSYuanhan Liu 
3826ba1f63bSYuanhan Liu 		new_gen = io_read8(&hw->common_cfg->config_generation);
3836ba1f63bSYuanhan Liu 	} while (old_gen != new_gen);
3846ba1f63bSYuanhan Liu }
3856ba1f63bSYuanhan Liu 
3866ba1f63bSYuanhan Liu static void
3876ba1f63bSYuanhan Liu modern_write_dev_config(struct virtio_hw *hw, size_t offset,
3886ba1f63bSYuanhan Liu 			const void *src, int length)
3896ba1f63bSYuanhan Liu {
3906ba1f63bSYuanhan Liu 	int i;
3916ba1f63bSYuanhan Liu 	const uint8_t *p = src;
3926ba1f63bSYuanhan Liu 
3936ba1f63bSYuanhan Liu 	for (i = 0;  i < length; i++)
3946ba1f63bSYuanhan Liu 		io_write8(*p++, (uint8_t *)hw->dev_cfg + offset + i);
3956ba1f63bSYuanhan Liu }
3966ba1f63bSYuanhan Liu 
3976ba1f63bSYuanhan Liu static uint64_t
3986ba1f63bSYuanhan Liu modern_get_features(struct virtio_hw *hw)
3996ba1f63bSYuanhan Liu {
4006ba1f63bSYuanhan Liu 	uint32_t features_lo, features_hi;
4016ba1f63bSYuanhan Liu 
4026ba1f63bSYuanhan Liu 	io_write32(0, &hw->common_cfg->device_feature_select);
4036ba1f63bSYuanhan Liu 	features_lo = io_read32(&hw->common_cfg->device_feature);
4046ba1f63bSYuanhan Liu 
4056ba1f63bSYuanhan Liu 	io_write32(1, &hw->common_cfg->device_feature_select);
4066ba1f63bSYuanhan Liu 	features_hi = io_read32(&hw->common_cfg->device_feature);
4076ba1f63bSYuanhan Liu 
4086ba1f63bSYuanhan Liu 	return ((uint64_t)features_hi << 32) | features_lo;
4096ba1f63bSYuanhan Liu }
4106ba1f63bSYuanhan Liu 
4116ba1f63bSYuanhan Liu static void
4126ba1f63bSYuanhan Liu modern_set_features(struct virtio_hw *hw, uint64_t features)
4136ba1f63bSYuanhan Liu {
4146ba1f63bSYuanhan Liu 	io_write32(0, &hw->common_cfg->guest_feature_select);
4156ba1f63bSYuanhan Liu 	io_write32(features & ((1ULL << 32) - 1),
4166ba1f63bSYuanhan Liu 		&hw->common_cfg->guest_feature);
4176ba1f63bSYuanhan Liu 
4186ba1f63bSYuanhan Liu 	io_write32(1, &hw->common_cfg->guest_feature_select);
4196ba1f63bSYuanhan Liu 	io_write32(features >> 32,
4206ba1f63bSYuanhan Liu 		&hw->common_cfg->guest_feature);
4216ba1f63bSYuanhan Liu }
4226ba1f63bSYuanhan Liu 
4236ba1f63bSYuanhan Liu static uint8_t
4246ba1f63bSYuanhan Liu modern_get_status(struct virtio_hw *hw)
4256ba1f63bSYuanhan Liu {
4266ba1f63bSYuanhan Liu 	return io_read8(&hw->common_cfg->device_status);
4276ba1f63bSYuanhan Liu }
4286ba1f63bSYuanhan Liu 
4296ba1f63bSYuanhan Liu static void
4306ba1f63bSYuanhan Liu modern_set_status(struct virtio_hw *hw, uint8_t status)
4316ba1f63bSYuanhan Liu {
4326ba1f63bSYuanhan Liu 	io_write8(status, &hw->common_cfg->device_status);
4336ba1f63bSYuanhan Liu }
4346ba1f63bSYuanhan Liu 
4356ba1f63bSYuanhan Liu static void
4366ba1f63bSYuanhan Liu modern_reset(struct virtio_hw *hw)
4376ba1f63bSYuanhan Liu {
4386ba1f63bSYuanhan Liu 	modern_set_status(hw, VIRTIO_CONFIG_STATUS_RESET);
4396ba1f63bSYuanhan Liu 	modern_get_status(hw);
4406ba1f63bSYuanhan Liu }
4416ba1f63bSYuanhan Liu 
4426ba1f63bSYuanhan Liu static uint8_t
4436ba1f63bSYuanhan Liu modern_get_isr(struct virtio_hw *hw)
4446ba1f63bSYuanhan Liu {
4456ba1f63bSYuanhan Liu 	return io_read8(hw->isr);
4466ba1f63bSYuanhan Liu }
4476ba1f63bSYuanhan Liu 
4486ba1f63bSYuanhan Liu static uint16_t
4496ba1f63bSYuanhan Liu modern_set_config_irq(struct virtio_hw *hw, uint16_t vec)
4506ba1f63bSYuanhan Liu {
4516ba1f63bSYuanhan Liu 	io_write16(vec, &hw->common_cfg->msix_config);
4526ba1f63bSYuanhan Liu 	return io_read16(&hw->common_cfg->msix_config);
4536ba1f63bSYuanhan Liu }
4546ba1f63bSYuanhan Liu 
4556ba1f63bSYuanhan Liu static uint16_t
4566ba1f63bSYuanhan Liu modern_get_queue_num(struct virtio_hw *hw, uint16_t queue_id)
4576ba1f63bSYuanhan Liu {
4586ba1f63bSYuanhan Liu 	io_write16(queue_id, &hw->common_cfg->queue_select);
4596ba1f63bSYuanhan Liu 	return io_read16(&hw->common_cfg->queue_size);
4606ba1f63bSYuanhan Liu }
4616ba1f63bSYuanhan Liu 
462595454c5SJianfeng Tan static int
4636ba1f63bSYuanhan Liu modern_setup_queue(struct virtio_hw *hw, struct virtqueue *vq)
4646ba1f63bSYuanhan Liu {
4656ba1f63bSYuanhan Liu 	uint64_t desc_addr, avail_addr, used_addr;
4666ba1f63bSYuanhan Liu 	uint16_t notify_off;
4676ba1f63bSYuanhan Liu 
468595454c5SJianfeng Tan 	if (!check_vq_phys_addr_ok(vq))
469595454c5SJianfeng Tan 		return -1;
470595454c5SJianfeng Tan 
47101ad44fdSHuawei Xie 	desc_addr = vq->vq_ring_mem;
4726ba1f63bSYuanhan Liu 	avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc);
4736ba1f63bSYuanhan Liu 	used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail,
4746ba1f63bSYuanhan Liu 							 ring[vq->vq_nentries]),
4756ba1f63bSYuanhan Liu 				   VIRTIO_PCI_VRING_ALIGN);
4766ba1f63bSYuanhan Liu 
4776ba1f63bSYuanhan Liu 	io_write16(vq->vq_queue_index, &hw->common_cfg->queue_select);
4786ba1f63bSYuanhan Liu 
4796ba1f63bSYuanhan Liu 	io_write64_twopart(desc_addr, &hw->common_cfg->queue_desc_lo,
4806ba1f63bSYuanhan Liu 				      &hw->common_cfg->queue_desc_hi);
4816ba1f63bSYuanhan Liu 	io_write64_twopart(avail_addr, &hw->common_cfg->queue_avail_lo,
4826ba1f63bSYuanhan Liu 				       &hw->common_cfg->queue_avail_hi);
4836ba1f63bSYuanhan Liu 	io_write64_twopart(used_addr, &hw->common_cfg->queue_used_lo,
4846ba1f63bSYuanhan Liu 				      &hw->common_cfg->queue_used_hi);
4856ba1f63bSYuanhan Liu 
4866ba1f63bSYuanhan Liu 	notify_off = io_read16(&hw->common_cfg->queue_notify_off);
4876ba1f63bSYuanhan Liu 	vq->notify_addr = (void *)((uint8_t *)hw->notify_base +
4886ba1f63bSYuanhan Liu 				notify_off * hw->notify_off_multiplier);
4896ba1f63bSYuanhan Liu 
4906ba1f63bSYuanhan Liu 	io_write16(1, &hw->common_cfg->queue_enable);
4916ba1f63bSYuanhan Liu 
4926ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "queue %u addresses:", vq->vq_queue_index);
4936ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t desc_addr: %" PRIx64, desc_addr);
4946ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t aval_addr: %" PRIx64, avail_addr);
4956ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t used_addr: %" PRIx64, used_addr);
4966ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t notify addr: %p (notify offset: %u)",
4976ba1f63bSYuanhan Liu 		vq->notify_addr, notify_off);
498595454c5SJianfeng Tan 
499595454c5SJianfeng Tan 	return 0;
5006ba1f63bSYuanhan Liu }
5016ba1f63bSYuanhan Liu 
5026ba1f63bSYuanhan Liu static void
5036ba1f63bSYuanhan Liu modern_del_queue(struct virtio_hw *hw, struct virtqueue *vq)
5046ba1f63bSYuanhan Liu {
5056ba1f63bSYuanhan Liu 	io_write16(vq->vq_queue_index, &hw->common_cfg->queue_select);
5066ba1f63bSYuanhan Liu 
5076ba1f63bSYuanhan Liu 	io_write64_twopart(0, &hw->common_cfg->queue_desc_lo,
5086ba1f63bSYuanhan Liu 				  &hw->common_cfg->queue_desc_hi);
5096ba1f63bSYuanhan Liu 	io_write64_twopart(0, &hw->common_cfg->queue_avail_lo,
5106ba1f63bSYuanhan Liu 				  &hw->common_cfg->queue_avail_hi);
5116ba1f63bSYuanhan Liu 	io_write64_twopart(0, &hw->common_cfg->queue_used_lo,
5126ba1f63bSYuanhan Liu 				  &hw->common_cfg->queue_used_hi);
5136ba1f63bSYuanhan Liu 
5146ba1f63bSYuanhan Liu 	io_write16(0, &hw->common_cfg->queue_enable);
5156ba1f63bSYuanhan Liu }
5166ba1f63bSYuanhan Liu 
5176ba1f63bSYuanhan Liu static void
5186ba1f63bSYuanhan Liu modern_notify_queue(struct virtio_hw *hw __rte_unused, struct virtqueue *vq)
5196ba1f63bSYuanhan Liu {
5206ba1f63bSYuanhan Liu 	io_write16(1, vq->notify_addr);
5216ba1f63bSYuanhan Liu }
5226ba1f63bSYuanhan Liu 
523*6d890f8aSYuanhan Liu const struct virtio_pci_ops modern_ops = {
5246ba1f63bSYuanhan Liu 	.read_dev_cfg	= modern_read_dev_config,
5256ba1f63bSYuanhan Liu 	.write_dev_cfg	= modern_write_dev_config,
5266ba1f63bSYuanhan Liu 	.reset		= modern_reset,
5276ba1f63bSYuanhan Liu 	.get_status	= modern_get_status,
5286ba1f63bSYuanhan Liu 	.set_status	= modern_set_status,
5296ba1f63bSYuanhan Liu 	.get_features	= modern_get_features,
5306ba1f63bSYuanhan Liu 	.set_features	= modern_set_features,
5316ba1f63bSYuanhan Liu 	.get_isr	= modern_get_isr,
5326ba1f63bSYuanhan Liu 	.set_config_irq	= modern_set_config_irq,
5336ba1f63bSYuanhan Liu 	.get_queue_num	= modern_get_queue_num,
5346ba1f63bSYuanhan Liu 	.setup_queue	= modern_setup_queue,
5356ba1f63bSYuanhan Liu 	.del_queue	= modern_del_queue,
5366ba1f63bSYuanhan Liu 	.notify_queue	= modern_notify_queue,
5376ba1f63bSYuanhan Liu };
5386ba1f63bSYuanhan Liu 
5396ba1f63bSYuanhan Liu 
540d5bbeefcSYuanhan Liu void
541d5bbeefcSYuanhan Liu vtpci_read_dev_config(struct virtio_hw *hw, size_t offset,
542d5bbeefcSYuanhan Liu 		      void *dst, int length)
543d5bbeefcSYuanhan Liu {
544553f4593SYuanhan Liu 	VTPCI_OPS(hw)->read_dev_cfg(hw, offset, dst, length);
545d5bbeefcSYuanhan Liu }
546d5bbeefcSYuanhan Liu 
547d5bbeefcSYuanhan Liu void
548d5bbeefcSYuanhan Liu vtpci_write_dev_config(struct virtio_hw *hw, size_t offset,
549d5bbeefcSYuanhan Liu 		       const void *src, int length)
550d5bbeefcSYuanhan Liu {
551553f4593SYuanhan Liu 	VTPCI_OPS(hw)->write_dev_cfg(hw, offset, src, length);
552d5bbeefcSYuanhan Liu }
553d5bbeefcSYuanhan Liu 
5543891f233SYuanhan Liu uint64_t
5553891f233SYuanhan Liu vtpci_negotiate_features(struct virtio_hw *hw, uint64_t host_features)
5566c3169a3SBruce Richardson {
5573891f233SYuanhan Liu 	uint64_t features;
558d5bbeefcSYuanhan Liu 
5596c3169a3SBruce Richardson 	/*
5606c3169a3SBruce Richardson 	 * Limit negotiated features to what the driver, virtqueue, and
5616c3169a3SBruce Richardson 	 * host all support.
5626c3169a3SBruce Richardson 	 */
5636c3169a3SBruce Richardson 	features = host_features & hw->guest_features;
564553f4593SYuanhan Liu 	VTPCI_OPS(hw)->set_features(hw, features);
5656c3169a3SBruce Richardson 
5666c3169a3SBruce Richardson 	return features;
5676c3169a3SBruce Richardson }
5686c3169a3SBruce Richardson 
5696c3169a3SBruce Richardson void
5706c3169a3SBruce Richardson vtpci_reset(struct virtio_hw *hw)
5716c3169a3SBruce Richardson {
572553f4593SYuanhan Liu 	VTPCI_OPS(hw)->set_status(hw, VIRTIO_CONFIG_STATUS_RESET);
573d5bbeefcSYuanhan Liu 	/* flush status write */
574553f4593SYuanhan Liu 	VTPCI_OPS(hw)->get_status(hw);
5756c3169a3SBruce Richardson }
5766c3169a3SBruce Richardson 
5776c3169a3SBruce Richardson void
5786c3169a3SBruce Richardson vtpci_reinit_complete(struct virtio_hw *hw)
5796c3169a3SBruce Richardson {
5806c3169a3SBruce Richardson 	vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK);
5816c3169a3SBruce Richardson }
5826c3169a3SBruce Richardson 
5836c3169a3SBruce Richardson void
5846c3169a3SBruce Richardson vtpci_set_status(struct virtio_hw *hw, uint8_t status)
5856c3169a3SBruce Richardson {
5866c3169a3SBruce Richardson 	if (status != VIRTIO_CONFIG_STATUS_RESET)
587553f4593SYuanhan Liu 		status |= VTPCI_OPS(hw)->get_status(hw);
5886c3169a3SBruce Richardson 
589553f4593SYuanhan Liu 	VTPCI_OPS(hw)->set_status(hw, status);
5906c3169a3SBruce Richardson }
5916c3169a3SBruce Richardson 
5926c3169a3SBruce Richardson uint8_t
5936ba1f63bSYuanhan Liu vtpci_get_status(struct virtio_hw *hw)
5946ba1f63bSYuanhan Liu {
595553f4593SYuanhan Liu 	return VTPCI_OPS(hw)->get_status(hw);
5966ba1f63bSYuanhan Liu }
5976ba1f63bSYuanhan Liu 
5986ba1f63bSYuanhan Liu uint8_t
5996c3169a3SBruce Richardson vtpci_isr(struct virtio_hw *hw)
6006c3169a3SBruce Richardson {
601553f4593SYuanhan Liu 	return VTPCI_OPS(hw)->get_isr(hw);
6026c3169a3SBruce Richardson }
6036c3169a3SBruce Richardson 
6046c3169a3SBruce Richardson 
6056c3169a3SBruce Richardson /* Enable one vector (0) for Link State Intrerrupt */
6066c3169a3SBruce Richardson uint16_t
6076c3169a3SBruce Richardson vtpci_irq_config(struct virtio_hw *hw, uint16_t vec)
6086c3169a3SBruce Richardson {
609553f4593SYuanhan Liu 	return VTPCI_OPS(hw)->set_config_irq(hw, vec);
610d5bbeefcSYuanhan Liu }
611d5bbeefcSYuanhan Liu 
6126ba1f63bSYuanhan Liu static void *
6136ba1f63bSYuanhan Liu get_cfg_addr(struct rte_pci_device *dev, struct virtio_pci_cap *cap)
6146ba1f63bSYuanhan Liu {
6156ba1f63bSYuanhan Liu 	uint8_t  bar    = cap->bar;
6166ba1f63bSYuanhan Liu 	uint32_t length = cap->length;
6176ba1f63bSYuanhan Liu 	uint32_t offset = cap->offset;
6186ba1f63bSYuanhan Liu 	uint8_t *base;
6196ba1f63bSYuanhan Liu 
6206ba1f63bSYuanhan Liu 	if (bar > 5) {
6216ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR, "invalid bar: %u", bar);
6226ba1f63bSYuanhan Liu 		return NULL;
6236ba1f63bSYuanhan Liu 	}
6246ba1f63bSYuanhan Liu 
6256ba1f63bSYuanhan Liu 	if (offset + length < offset) {
6266ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR, "offset(%u) + length(%u) overflows",
6276ba1f63bSYuanhan Liu 			offset, length);
6286ba1f63bSYuanhan Liu 		return NULL;
6296ba1f63bSYuanhan Liu 	}
6306ba1f63bSYuanhan Liu 
6316ba1f63bSYuanhan Liu 	if (offset + length > dev->mem_resource[bar].len) {
6326ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR,
6336ba1f63bSYuanhan Liu 			"invalid cap: overflows bar space: %u > %" PRIu64,
6346ba1f63bSYuanhan Liu 			offset + length, dev->mem_resource[bar].len);
6356ba1f63bSYuanhan Liu 		return NULL;
6366ba1f63bSYuanhan Liu 	}
6376ba1f63bSYuanhan Liu 
6386ba1f63bSYuanhan Liu 	base = dev->mem_resource[bar].addr;
6396ba1f63bSYuanhan Liu 	if (base == NULL) {
6406ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR, "bar %u base addr is NULL", bar);
6416ba1f63bSYuanhan Liu 		return NULL;
6426ba1f63bSYuanhan Liu 	}
6436ba1f63bSYuanhan Liu 
6446ba1f63bSYuanhan Liu 	return base + offset;
6456ba1f63bSYuanhan Liu }
6466ba1f63bSYuanhan Liu 
6476ba1f63bSYuanhan Liu static int
6486ba1f63bSYuanhan Liu virtio_read_caps(struct rte_pci_device *dev, struct virtio_hw *hw)
6496ba1f63bSYuanhan Liu {
6506ba1f63bSYuanhan Liu 	uint8_t pos;
6516ba1f63bSYuanhan Liu 	struct virtio_pci_cap cap;
6526ba1f63bSYuanhan Liu 	int ret;
6536ba1f63bSYuanhan Liu 
6547a66c72dSDavid Marchand 	if (rte_eal_pci_map_device(dev)) {
6556ba1f63bSYuanhan Liu 		PMD_INIT_LOG(DEBUG, "failed to map pci device!");
6566ba1f63bSYuanhan Liu 		return -1;
6576ba1f63bSYuanhan Liu 	}
6586ba1f63bSYuanhan Liu 
6596ba1f63bSYuanhan Liu 	ret = rte_eal_pci_read_config(dev, &pos, 1, PCI_CAPABILITY_LIST);
6606ba1f63bSYuanhan Liu 	if (ret < 0) {
6616ba1f63bSYuanhan Liu 		PMD_INIT_LOG(DEBUG, "failed to read pci capability list");
6626ba1f63bSYuanhan Liu 		return -1;
6636ba1f63bSYuanhan Liu 	}
6646ba1f63bSYuanhan Liu 
6656ba1f63bSYuanhan Liu 	while (pos) {
6666ba1f63bSYuanhan Liu 		ret = rte_eal_pci_read_config(dev, &cap, sizeof(cap), pos);
6676ba1f63bSYuanhan Liu 		if (ret < 0) {
6686ba1f63bSYuanhan Liu 			PMD_INIT_LOG(ERR,
6696ba1f63bSYuanhan Liu 				"failed to read pci cap at pos: %x", pos);
6706ba1f63bSYuanhan Liu 			break;
6716ba1f63bSYuanhan Liu 		}
6726ba1f63bSYuanhan Liu 
6736ba1f63bSYuanhan Liu 		if (cap.cap_vndr != PCI_CAP_ID_VNDR) {
6746ba1f63bSYuanhan Liu 			PMD_INIT_LOG(DEBUG,
6756ba1f63bSYuanhan Liu 				"[%2x] skipping non VNDR cap id: %02x",
6766ba1f63bSYuanhan Liu 				pos, cap.cap_vndr);
6776ba1f63bSYuanhan Liu 			goto next;
6786ba1f63bSYuanhan Liu 		}
6796ba1f63bSYuanhan Liu 
6806ba1f63bSYuanhan Liu 		PMD_INIT_LOG(DEBUG,
6816ba1f63bSYuanhan Liu 			"[%2x] cfg type: %u, bar: %u, offset: %04x, len: %u",
6826ba1f63bSYuanhan Liu 			pos, cap.cfg_type, cap.bar, cap.offset, cap.length);
6836ba1f63bSYuanhan Liu 
6846ba1f63bSYuanhan Liu 		switch (cap.cfg_type) {
6856ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_COMMON_CFG:
6866ba1f63bSYuanhan Liu 			hw->common_cfg = get_cfg_addr(dev, &cap);
6876ba1f63bSYuanhan Liu 			break;
6886ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_NOTIFY_CFG:
6896ba1f63bSYuanhan Liu 			rte_eal_pci_read_config(dev, &hw->notify_off_multiplier,
6906ba1f63bSYuanhan Liu 						4, pos + sizeof(cap));
6916ba1f63bSYuanhan Liu 			hw->notify_base = get_cfg_addr(dev, &cap);
6926ba1f63bSYuanhan Liu 			break;
6936ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_DEVICE_CFG:
6946ba1f63bSYuanhan Liu 			hw->dev_cfg = get_cfg_addr(dev, &cap);
6956ba1f63bSYuanhan Liu 			break;
6966ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_ISR_CFG:
6976ba1f63bSYuanhan Liu 			hw->isr = get_cfg_addr(dev, &cap);
6986ba1f63bSYuanhan Liu 			break;
6996ba1f63bSYuanhan Liu 		}
7006ba1f63bSYuanhan Liu 
7016ba1f63bSYuanhan Liu next:
7026ba1f63bSYuanhan Liu 		pos = cap.cap_next;
7036ba1f63bSYuanhan Liu 	}
7046ba1f63bSYuanhan Liu 
7056ba1f63bSYuanhan Liu 	if (hw->common_cfg == NULL || hw->notify_base == NULL ||
7066ba1f63bSYuanhan Liu 	    hw->dev_cfg == NULL    || hw->isr == NULL) {
7076ba1f63bSYuanhan Liu 		PMD_INIT_LOG(INFO, "no modern virtio pci device found.");
7086ba1f63bSYuanhan Liu 		return -1;
7096ba1f63bSYuanhan Liu 	}
7106ba1f63bSYuanhan Liu 
7116ba1f63bSYuanhan Liu 	PMD_INIT_LOG(INFO, "found modern virtio pci device.");
7126ba1f63bSYuanhan Liu 
7136ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "common cfg mapped at: %p", hw->common_cfg);
7146ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "device cfg mapped at: %p", hw->dev_cfg);
7156ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "isr cfg mapped at: %p", hw->isr);
7166ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "notify base: %p, notify off multiplier: %u",
7176ba1f63bSYuanhan Liu 		hw->notify_base, hw->notify_off_multiplier);
7186ba1f63bSYuanhan Liu 
7196ba1f63bSYuanhan Liu 	return 0;
7206ba1f63bSYuanhan Liu }
7216ba1f63bSYuanhan Liu 
722ac5e1d83SHuawei Xie /*
723ac5e1d83SHuawei Xie  * Return -1:
724ac5e1d83SHuawei Xie  *   if there is error mapping with VFIO/UIO.
725ac5e1d83SHuawei Xie  *   if port map error when driver type is KDRV_NONE.
7267e40200cSHuawei Xie  *   if whitelisted but driver type is KDRV_UNKNOWN.
727ac5e1d83SHuawei Xie  * Return 1 if kernel driver is managing the device.
728ac5e1d83SHuawei Xie  * Return 0 on success.
729ac5e1d83SHuawei Xie  */
730d5bbeefcSYuanhan Liu int
73162a785a6SJianfeng Tan vtpci_init(struct rte_pci_device *dev, struct virtio_hw *hw,
73262a785a6SJianfeng Tan 	   uint32_t *dev_flags)
733d5bbeefcSYuanhan Liu {
7346ba1f63bSYuanhan Liu 	hw->dev = dev;
735d5bbeefcSYuanhan Liu 
7366ba1f63bSYuanhan Liu 	/*
7376ba1f63bSYuanhan Liu 	 * Try if we can succeed reading virtio pci caps, which exists
7386ba1f63bSYuanhan Liu 	 * only on modern pci device. If failed, we fallback to legacy
7396ba1f63bSYuanhan Liu 	 * virtio handling.
7406ba1f63bSYuanhan Liu 	 */
7416ba1f63bSYuanhan Liu 	if (virtio_read_caps(dev, hw) == 0) {
7426ba1f63bSYuanhan Liu 		PMD_INIT_LOG(INFO, "modern virtio pci detected.");
743553f4593SYuanhan Liu 		virtio_hw_internal[hw->port_id].vtpci_ops = &modern_ops;
7446ba1f63bSYuanhan Liu 		hw->modern = 1;
74562a785a6SJianfeng Tan 		*dev_flags |= RTE_ETH_DEV_INTR_LSC;
7466ba1f63bSYuanhan Liu 		return 0;
7476ba1f63bSYuanhan Liu 	}
7486ba1f63bSYuanhan Liu 
7496ba1f63bSYuanhan Liu 	PMD_INIT_LOG(INFO, "trying with legacy virtio pci.");
75062a785a6SJianfeng Tan 	if (legacy_virtio_resource_init(dev, hw, dev_flags) < 0) {
751ac5e1d83SHuawei Xie 		if (dev->kdrv == RTE_KDRV_UNKNOWN &&
75213a1317dSJan Viktorin 		    (!dev->device.devargs ||
75313a1317dSJan Viktorin 		     dev->device.devargs->type !=
75413a1317dSJan Viktorin 			RTE_DEVTYPE_WHITELISTED_PCI)) {
755ac5e1d83SHuawei Xie 			PMD_INIT_LOG(INFO,
756ac5e1d83SHuawei Xie 				"skip kernel managed virtio device.");
757ac5e1d83SHuawei Xie 			return 1;
758ac5e1d83SHuawei Xie 		}
759c52afa68SYuanhan Liu 		return -1;
760ac5e1d83SHuawei Xie 	}
7616ba1f63bSYuanhan Liu 
762553f4593SYuanhan Liu 	virtio_hw_internal[hw->port_id].vtpci_ops = &legacy_ops;
763c52afa68SYuanhan Liu 	hw->use_msix = legacy_virtio_has_msix(&dev->addr);
7646ba1f63bSYuanhan Liu 	hw->modern   = 0;
765c52afa68SYuanhan Liu 
766d5bbeefcSYuanhan Liu 	return 0;
7676c3169a3SBruce Richardson }
768