xref: /dpdk/drivers/net/virtio/virtio_pci.c (revision 7bb1168d984aaa7e204c52d13c4701eac0f82989)
15566a3e3SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
25566a3e3SBruce Richardson  * Copyright(c) 2010-2014 Intel Corporation
36c3169a3SBruce Richardson  */
46c3169a3SBruce Richardson #include <stdint.h>
56c3169a3SBruce Richardson 
6742bde12SBruce Richardson #ifdef RTE_EXEC_ENV_LINUX
7c52afa68SYuanhan Liu  #include <dirent.h>
8c52afa68SYuanhan Liu  #include <fcntl.h>
9c52afa68SYuanhan Liu #endif
10c52afa68SYuanhan Liu 
11631d4ee4SSantosh Shukla #include <rte_io.h>
12a04322f6SDavid Marchand #include <bus_driver.h>
13631d4ee4SSantosh Shukla 
146c3169a3SBruce Richardson #include "virtio_pci.h"
156c3169a3SBruce Richardson #include "virtio_logs.h"
16d5bbeefcSYuanhan Liu #include "virtqueue.h"
176c3169a3SBruce Richardson 
186ba1f63bSYuanhan Liu /*
19b8f04520SDavid Marchand  * The remaining space is defined by each driver as the per-driver
20b8f04520SDavid Marchand  * configuration space.
21b8f04520SDavid Marchand  */
226a504290SMaxime Coquelin #define VIRTIO_PCI_CONFIG(dev) \
236a504290SMaxime Coquelin 		(((dev)->msix_status == VIRTIO_MSIX_ENABLED) ? 24 : 20)
24b86af7b1SYuanhan Liu 
25c8d4b02fSMaxime Coquelin struct virtio_pci_internal virtio_pci_internal[RTE_MAX_ETHPORTS];
26c8d4b02fSMaxime Coquelin 
277793d293SMaxime Coquelin static enum virtio_msix_status
vtpci_msix_detect(struct rte_pci_device * dev)287793d293SMaxime Coquelin vtpci_msix_detect(struct rte_pci_device *dev)
297793d293SMaxime Coquelin {
307793d293SMaxime Coquelin 	uint16_t flags;
31a10b6e53SDavid Marchand 	off_t pos;
327793d293SMaxime Coquelin 
33baa9c550SDavid Marchand 	pos = rte_pci_find_capability(dev, RTE_PCI_CAP_ID_MSIX);
34a10b6e53SDavid Marchand 	if (pos > 0 && rte_pci_read_config(dev, &flags, sizeof(flags),
35*7bb1168dSDavid Marchand 			pos + RTE_PCI_MSIX_FLAGS) == sizeof(flags)) {
36*7bb1168dSDavid Marchand 		if (flags & RTE_PCI_MSIX_FLAGS_ENABLE)
377793d293SMaxime Coquelin 			return VIRTIO_MSIX_ENABLED;
387793d293SMaxime Coquelin 		else
397793d293SMaxime Coquelin 			return VIRTIO_MSIX_DISABLED;
407793d293SMaxime Coquelin 	}
417793d293SMaxime Coquelin 
427793d293SMaxime Coquelin 	return VIRTIO_MSIX_NONE;
437793d293SMaxime Coquelin }
447793d293SMaxime Coquelin 
45281ccccbSDavid Marchand /*
46281ccccbSDavid Marchand  * Since we are in legacy mode:
47281ccccbSDavid Marchand  * http://ozlabs.org/~rusty/virtio-spec/virtio-0.9.5.pdf
48281ccccbSDavid Marchand  *
49281ccccbSDavid Marchand  * "Note that this is possible because while the virtio header is PCI (i.e.
50281ccccbSDavid Marchand  * little) endian, the device-specific region is encoded in the native endian of
51281ccccbSDavid Marchand  * the guest (where such distinction is applicable)."
52281ccccbSDavid Marchand  *
53281ccccbSDavid Marchand  * For powerpc which supports both, qemu supposes that cpu is big endian and
54281ccccbSDavid Marchand  * enforces this for the virtio-net stuff.
55281ccccbSDavid Marchand  */
56d5bbeefcSYuanhan Liu static void
legacy_read_dev_config(struct virtio_hw * hw,size_t offset,void * dst,int length)57d5bbeefcSYuanhan Liu legacy_read_dev_config(struct virtio_hw *hw, size_t offset,
586c3169a3SBruce Richardson 		       void *dst, int length)
596c3169a3SBruce Richardson {
606a504290SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
61281ccccbSDavid Marchand #ifdef RTE_ARCH_PPC_64
62281ccccbSDavid Marchand 	int size;
63281ccccbSDavid Marchand 
64281ccccbSDavid Marchand 	while (length > 0) {
65281ccccbSDavid Marchand 		if (length >= 4) {
66281ccccbSDavid Marchand 			size = 4;
673dcfe039SThomas Monjalon 			rte_pci_ioport_read(VTPCI_IO(hw), dst, size,
686a504290SMaxime Coquelin 				VIRTIO_PCI_CONFIG(dev) + offset);
69281ccccbSDavid Marchand 			*(uint32_t *)dst = rte_be_to_cpu_32(*(uint32_t *)dst);
70281ccccbSDavid Marchand 		} else if (length >= 2) {
71281ccccbSDavid Marchand 			size = 2;
723dcfe039SThomas Monjalon 			rte_pci_ioport_read(VTPCI_IO(hw), dst, size,
736a504290SMaxime Coquelin 				VIRTIO_PCI_CONFIG(dev) + offset);
74281ccccbSDavid Marchand 			*(uint16_t *)dst = rte_be_to_cpu_16(*(uint16_t *)dst);
75281ccccbSDavid Marchand 		} else {
76281ccccbSDavid Marchand 			size = 1;
773dcfe039SThomas Monjalon 			rte_pci_ioport_read(VTPCI_IO(hw), dst, size,
786a504290SMaxime Coquelin 				VIRTIO_PCI_CONFIG(dev) + offset);
79281ccccbSDavid Marchand 		}
80281ccccbSDavid Marchand 
81281ccccbSDavid Marchand 		dst = (char *)dst + size;
82281ccccbSDavid Marchand 		offset += size;
83281ccccbSDavid Marchand 		length -= size;
84281ccccbSDavid Marchand 	}
85281ccccbSDavid Marchand #else
863dcfe039SThomas Monjalon 	rte_pci_ioport_read(VTPCI_IO(hw), dst, length,
876a504290SMaxime Coquelin 		VIRTIO_PCI_CONFIG(dev) + offset);
88281ccccbSDavid Marchand #endif
896c3169a3SBruce Richardson }
906c3169a3SBruce Richardson 
91d5bbeefcSYuanhan Liu static void
legacy_write_dev_config(struct virtio_hw * hw,size_t offset,const void * src,int length)92d5bbeefcSYuanhan Liu legacy_write_dev_config(struct virtio_hw *hw, size_t offset,
93d5bbeefcSYuanhan Liu 			const void *src, int length)
946c3169a3SBruce Richardson {
956a504290SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
96281ccccbSDavid Marchand #ifdef RTE_ARCH_PPC_64
97281ccccbSDavid Marchand 	union {
98281ccccbSDavid Marchand 		uint32_t u32;
99281ccccbSDavid Marchand 		uint16_t u16;
100281ccccbSDavid Marchand 	} tmp;
101281ccccbSDavid Marchand 	int size;
102281ccccbSDavid Marchand 
103281ccccbSDavid Marchand 	while (length > 0) {
104281ccccbSDavid Marchand 		if (length >= 4) {
105281ccccbSDavid Marchand 			size = 4;
106281ccccbSDavid Marchand 			tmp.u32 = rte_cpu_to_be_32(*(const uint32_t *)src);
1073dcfe039SThomas Monjalon 			rte_pci_ioport_write(VTPCI_IO(hw), &tmp.u32, size,
1086a504290SMaxime Coquelin 				VIRTIO_PCI_CONFIG(dev) + offset);
109281ccccbSDavid Marchand 		} else if (length >= 2) {
110281ccccbSDavid Marchand 			size = 2;
111281ccccbSDavid Marchand 			tmp.u16 = rte_cpu_to_be_16(*(const uint16_t *)src);
1123dcfe039SThomas Monjalon 			rte_pci_ioport_write(VTPCI_IO(hw), &tmp.u16, size,
1136a504290SMaxime Coquelin 				VIRTIO_PCI_CONFIG(dev) + offset);
114281ccccbSDavid Marchand 		} else {
115281ccccbSDavid Marchand 			size = 1;
1163dcfe039SThomas Monjalon 			rte_pci_ioport_write(VTPCI_IO(hw), src, size,
1176a504290SMaxime Coquelin 				VIRTIO_PCI_CONFIG(dev) + offset);
118281ccccbSDavid Marchand 		}
119281ccccbSDavid Marchand 
120281ccccbSDavid Marchand 		src = (const char *)src + size;
121281ccccbSDavid Marchand 		offset += size;
122281ccccbSDavid Marchand 		length -= size;
123281ccccbSDavid Marchand 	}
124281ccccbSDavid Marchand #else
1253dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), src, length,
1266a504290SMaxime Coquelin 		VIRTIO_PCI_CONFIG(dev) + offset);
127281ccccbSDavid Marchand #endif
1286c3169a3SBruce Richardson }
1296c3169a3SBruce Richardson 
1303891f233SYuanhan Liu static uint64_t
legacy_get_features(struct virtio_hw * hw)131d5bbeefcSYuanhan Liu legacy_get_features(struct virtio_hw *hw)
132d5bbeefcSYuanhan Liu {
13336ea36efSYuanhan Liu 	uint32_t dst;
134b8f04520SDavid Marchand 
1353dcfe039SThomas Monjalon 	rte_pci_ioport_read(VTPCI_IO(hw), &dst, 4, VIRTIO_PCI_HOST_FEATURES);
136b8f04520SDavid Marchand 	return dst;
137d5bbeefcSYuanhan Liu }
138d5bbeefcSYuanhan Liu 
139d5bbeefcSYuanhan Liu static void
legacy_set_features(struct virtio_hw * hw,uint64_t features)1403891f233SYuanhan Liu legacy_set_features(struct virtio_hw *hw, uint64_t features)
141d5bbeefcSYuanhan Liu {
1423891f233SYuanhan Liu 	if ((features >> 32) != 0) {
1433891f233SYuanhan Liu 		PMD_DRV_LOG(ERR,
1443891f233SYuanhan Liu 			"only 32 bit features are allowed for legacy virtio!");
1453891f233SYuanhan Liu 		return;
1463891f233SYuanhan Liu 	}
1473dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &features, 4,
148b8f04520SDavid Marchand 		VIRTIO_PCI_GUEST_FEATURES);
149d5bbeefcSYuanhan Liu }
150d5bbeefcSYuanhan Liu 
151cbb135b3SMaxime Coquelin static int
legacy_features_ok(struct virtio_hw * hw __rte_unused)152cbb135b3SMaxime Coquelin legacy_features_ok(struct virtio_hw *hw __rte_unused)
153cbb135b3SMaxime Coquelin {
154cbb135b3SMaxime Coquelin 	return 0;
155cbb135b3SMaxime Coquelin }
156cbb135b3SMaxime Coquelin 
157d5bbeefcSYuanhan Liu static uint8_t
legacy_get_status(struct virtio_hw * hw)158d5bbeefcSYuanhan Liu legacy_get_status(struct virtio_hw *hw)
159d5bbeefcSYuanhan Liu {
160b8f04520SDavid Marchand 	uint8_t dst;
161b8f04520SDavid Marchand 
1623dcfe039SThomas Monjalon 	rte_pci_ioport_read(VTPCI_IO(hw), &dst, 1, VIRTIO_PCI_STATUS);
163b8f04520SDavid Marchand 	return dst;
164d5bbeefcSYuanhan Liu }
165d5bbeefcSYuanhan Liu 
166d5bbeefcSYuanhan Liu static void
legacy_set_status(struct virtio_hw * hw,uint8_t status)167d5bbeefcSYuanhan Liu legacy_set_status(struct virtio_hw *hw, uint8_t status)
168d5bbeefcSYuanhan Liu {
1693dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &status, 1, VIRTIO_PCI_STATUS);
170d5bbeefcSYuanhan Liu }
171d5bbeefcSYuanhan Liu 
172d5bbeefcSYuanhan Liu static uint8_t
legacy_get_isr(struct virtio_hw * hw)173d5bbeefcSYuanhan Liu legacy_get_isr(struct virtio_hw *hw)
174d5bbeefcSYuanhan Liu {
175b8f04520SDavid Marchand 	uint8_t dst;
176b8f04520SDavid Marchand 
1773dcfe039SThomas Monjalon 	rte_pci_ioport_read(VTPCI_IO(hw), &dst, 1, VIRTIO_PCI_ISR);
178b8f04520SDavid Marchand 	return dst;
179d5bbeefcSYuanhan Liu }
180d5bbeefcSYuanhan Liu 
1817be78d02SJosh Soref /* Enable one vector (0) for Link State Interrupt */
182d5bbeefcSYuanhan Liu static uint16_t
legacy_set_config_irq(struct virtio_hw * hw,uint16_t vec)183d5bbeefcSYuanhan Liu legacy_set_config_irq(struct virtio_hw *hw, uint16_t vec)
184d5bbeefcSYuanhan Liu {
185b8f04520SDavid Marchand 	uint16_t dst;
186b8f04520SDavid Marchand 
1873dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &vec, 2, VIRTIO_MSI_CONFIG_VECTOR);
1883dcfe039SThomas Monjalon 	rte_pci_ioport_read(VTPCI_IO(hw), &dst, 2, VIRTIO_MSI_CONFIG_VECTOR);
189b8f04520SDavid Marchand 	return dst;
190d5bbeefcSYuanhan Liu }
191d5bbeefcSYuanhan Liu 
192d5bbeefcSYuanhan Liu static uint16_t
legacy_set_queue_irq(struct virtio_hw * hw,struct virtqueue * vq,uint16_t vec)193c49526acSJianfeng Tan legacy_set_queue_irq(struct virtio_hw *hw, struct virtqueue *vq, uint16_t vec)
194c49526acSJianfeng Tan {
195c49526acSJianfeng Tan 	uint16_t dst;
196c49526acSJianfeng Tan 
1973dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &vq->vq_queue_index, 2,
198c49526acSJianfeng Tan 		VIRTIO_PCI_QUEUE_SEL);
1993dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &vec, 2, VIRTIO_MSI_QUEUE_VECTOR);
2003dcfe039SThomas Monjalon 	rte_pci_ioport_read(VTPCI_IO(hw), &dst, 2, VIRTIO_MSI_QUEUE_VECTOR);
201c49526acSJianfeng Tan 	return dst;
202c49526acSJianfeng Tan }
203c49526acSJianfeng Tan 
204c49526acSJianfeng Tan static uint16_t
legacy_get_queue_num(struct virtio_hw * hw,uint16_t queue_id)205d5bbeefcSYuanhan Liu legacy_get_queue_num(struct virtio_hw *hw, uint16_t queue_id)
206d5bbeefcSYuanhan Liu {
207b8f04520SDavid Marchand 	uint16_t dst;
208b8f04520SDavid Marchand 
2093dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &queue_id, 2, VIRTIO_PCI_QUEUE_SEL);
2103dcfe039SThomas Monjalon 	rte_pci_ioport_read(VTPCI_IO(hw), &dst, 2, VIRTIO_PCI_QUEUE_NUM);
211b8f04520SDavid Marchand 	return dst;
212d5bbeefcSYuanhan Liu }
213d5bbeefcSYuanhan Liu 
214595454c5SJianfeng Tan static int
legacy_setup_queue(struct virtio_hw * hw,struct virtqueue * vq)215d5bbeefcSYuanhan Liu legacy_setup_queue(struct virtio_hw *hw, struct virtqueue *vq)
216d5bbeefcSYuanhan Liu {
217b8f04520SDavid Marchand 	uint32_t src;
218d5bbeefcSYuanhan Liu 
2198b76b3a0SDavid Marchand 	/* Virtio PCI device VIRTIO_PCI_QUEUE_PFN register is 32bit,
2208b76b3a0SDavid Marchand 	 * and only accepts 32 bit page frame number.
2218b76b3a0SDavid Marchand 	 * Check if the allocated physical memory exceeds 16TB.
2228b76b3a0SDavid Marchand 	 */
2238b76b3a0SDavid Marchand 	if ((vq->vq_ring_mem + vq->vq_ring_size - 1) >>
2248b76b3a0SDavid Marchand 			(VIRTIO_PCI_QUEUE_ADDR_SHIFT + 32)) {
2258b76b3a0SDavid Marchand 		PMD_INIT_LOG(ERR, "vring address shouldn't be above 16TB!");
226595454c5SJianfeng Tan 		return -1;
2278b76b3a0SDavid Marchand 	}
228595454c5SJianfeng Tan 
2293dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &vq->vq_queue_index, 2,
230b8f04520SDavid Marchand 		VIRTIO_PCI_QUEUE_SEL);
23101ad44fdSHuawei Xie 	src = vq->vq_ring_mem >> VIRTIO_PCI_QUEUE_ADDR_SHIFT;
2323dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &src, 4, VIRTIO_PCI_QUEUE_PFN);
233595454c5SJianfeng Tan 
234595454c5SJianfeng Tan 	return 0;
235d5bbeefcSYuanhan Liu }
236d5bbeefcSYuanhan Liu 
237d5bbeefcSYuanhan Liu static void
legacy_del_queue(struct virtio_hw * hw,struct virtqueue * vq)238d5bbeefcSYuanhan Liu legacy_del_queue(struct virtio_hw *hw, struct virtqueue *vq)
239d5bbeefcSYuanhan Liu {
240b8f04520SDavid Marchand 	uint32_t src = 0;
241d5bbeefcSYuanhan Liu 
2423dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &vq->vq_queue_index, 2,
243b8f04520SDavid Marchand 		VIRTIO_PCI_QUEUE_SEL);
2443dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &src, 4, VIRTIO_PCI_QUEUE_PFN);
245d5bbeefcSYuanhan Liu }
246d5bbeefcSYuanhan Liu 
247d5bbeefcSYuanhan Liu static void
legacy_notify_queue(struct virtio_hw * hw,struct virtqueue * vq)248d5bbeefcSYuanhan Liu legacy_notify_queue(struct virtio_hw *hw, struct virtqueue *vq)
249d5bbeefcSYuanhan Liu {
2503dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &vq->vq_queue_index, 2,
251b8f04520SDavid Marchand 		VIRTIO_PCI_QUEUE_NOTIFY);
252d5bbeefcSYuanhan Liu }
253d5bbeefcSYuanhan Liu 
2547793d293SMaxime Coquelin static void
legacy_intr_detect(struct virtio_hw * hw)2557793d293SMaxime Coquelin legacy_intr_detect(struct virtio_hw *hw)
2567793d293SMaxime Coquelin {
257c8d4b02fSMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
258c8d4b02fSMaxime Coquelin 
259aa0d4b8aSMaxime Coquelin 	dev->msix_status = vtpci_msix_detect(VTPCI_DEV(hw));
2606a504290SMaxime Coquelin 	hw->intr_lsc = !!dev->msix_status;
2617793d293SMaxime Coquelin }
2627793d293SMaxime Coquelin 
263f12908c8SMaxime Coquelin static int
legacy_dev_close(struct virtio_hw * hw)264f12908c8SMaxime Coquelin legacy_dev_close(struct virtio_hw *hw)
265f12908c8SMaxime Coquelin {
266aa0d4b8aSMaxime Coquelin 	rte_pci_unmap_device(VTPCI_DEV(hw));
267f12908c8SMaxime Coquelin 	rte_pci_ioport_unmap(VTPCI_IO(hw));
268f12908c8SMaxime Coquelin 
269f12908c8SMaxime Coquelin 	return 0;
270f12908c8SMaxime Coquelin }
271f12908c8SMaxime Coquelin 
272f8b60756SMaxime Coquelin const struct virtio_ops legacy_ops = {
273d5bbeefcSYuanhan Liu 	.read_dev_cfg	= legacy_read_dev_config,
274d5bbeefcSYuanhan Liu 	.write_dev_cfg	= legacy_write_dev_config,
275d5bbeefcSYuanhan Liu 	.get_status	= legacy_get_status,
276d5bbeefcSYuanhan Liu 	.set_status	= legacy_set_status,
277d5bbeefcSYuanhan Liu 	.get_features	= legacy_get_features,
278d5bbeefcSYuanhan Liu 	.set_features	= legacy_set_features,
279cbb135b3SMaxime Coquelin 	.features_ok	= legacy_features_ok,
280d5bbeefcSYuanhan Liu 	.get_isr	= legacy_get_isr,
281d5bbeefcSYuanhan Liu 	.set_config_irq	= legacy_set_config_irq,
282c49526acSJianfeng Tan 	.set_queue_irq  = legacy_set_queue_irq,
283d5bbeefcSYuanhan Liu 	.get_queue_num	= legacy_get_queue_num,
284d5bbeefcSYuanhan Liu 	.setup_queue	= legacy_setup_queue,
285d5bbeefcSYuanhan Liu 	.del_queue	= legacy_del_queue,
286d5bbeefcSYuanhan Liu 	.notify_queue	= legacy_notify_queue,
2877793d293SMaxime Coquelin 	.intr_detect	= legacy_intr_detect,
288f12908c8SMaxime Coquelin 	.dev_close	= legacy_dev_close,
289d5bbeefcSYuanhan Liu };
290d5bbeefcSYuanhan Liu 
2916ba1f63bSYuanhan Liu static inline void
io_write64_twopart(uint64_t val,uint32_t * lo,uint32_t * hi)2926ba1f63bSYuanhan Liu io_write64_twopart(uint64_t val, uint32_t *lo, uint32_t *hi)
2936ba1f63bSYuanhan Liu {
294631d4ee4SSantosh Shukla 	rte_write32(val & ((1ULL << 32) - 1), lo);
295631d4ee4SSantosh Shukla 	rte_write32(val >> 32,		     hi);
2966ba1f63bSYuanhan Liu }
2976ba1f63bSYuanhan Liu 
2986ba1f63bSYuanhan Liu static void
modern_read_dev_config(struct virtio_hw * hw,size_t offset,void * dst,int length)2996ba1f63bSYuanhan Liu modern_read_dev_config(struct virtio_hw *hw, size_t offset,
3006ba1f63bSYuanhan Liu 		       void *dst, int length)
3016ba1f63bSYuanhan Liu {
302266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
3036ba1f63bSYuanhan Liu 	int i;
3046ba1f63bSYuanhan Liu 	uint8_t *p;
3056ba1f63bSYuanhan Liu 	uint8_t old_gen, new_gen;
3066ba1f63bSYuanhan Liu 
3076ba1f63bSYuanhan Liu 	do {
308266ece29SMaxime Coquelin 		old_gen = rte_read8(&dev->common_cfg->config_generation);
3096ba1f63bSYuanhan Liu 
3106ba1f63bSYuanhan Liu 		p = dst;
3116ba1f63bSYuanhan Liu 		for (i = 0;  i < length; i++)
312266ece29SMaxime Coquelin 			*p++ = rte_read8((uint8_t *)dev->dev_cfg + offset + i);
3136ba1f63bSYuanhan Liu 
314266ece29SMaxime Coquelin 		new_gen = rte_read8(&dev->common_cfg->config_generation);
3156ba1f63bSYuanhan Liu 	} while (old_gen != new_gen);
3166ba1f63bSYuanhan Liu }
3176ba1f63bSYuanhan Liu 
3186ba1f63bSYuanhan Liu static void
modern_write_dev_config(struct virtio_hw * hw,size_t offset,const void * src,int length)3196ba1f63bSYuanhan Liu modern_write_dev_config(struct virtio_hw *hw, size_t offset,
3206ba1f63bSYuanhan Liu 			const void *src, int length)
3216ba1f63bSYuanhan Liu {
322266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
3236ba1f63bSYuanhan Liu 	int i;
3246ba1f63bSYuanhan Liu 	const uint8_t *p = src;
3256ba1f63bSYuanhan Liu 
3266ba1f63bSYuanhan Liu 	for (i = 0;  i < length; i++)
327266ece29SMaxime Coquelin 		rte_write8((*p++), (((uint8_t *)dev->dev_cfg) + offset + i));
3286ba1f63bSYuanhan Liu }
3296ba1f63bSYuanhan Liu 
3306ba1f63bSYuanhan Liu static uint64_t
modern_get_features(struct virtio_hw * hw)3316ba1f63bSYuanhan Liu modern_get_features(struct virtio_hw *hw)
3326ba1f63bSYuanhan Liu {
333266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
3346ba1f63bSYuanhan Liu 	uint32_t features_lo, features_hi;
3356ba1f63bSYuanhan Liu 
336266ece29SMaxime Coquelin 	rte_write32(0, &dev->common_cfg->device_feature_select);
337266ece29SMaxime Coquelin 	features_lo = rte_read32(&dev->common_cfg->device_feature);
3386ba1f63bSYuanhan Liu 
339266ece29SMaxime Coquelin 	rte_write32(1, &dev->common_cfg->device_feature_select);
340266ece29SMaxime Coquelin 	features_hi = rte_read32(&dev->common_cfg->device_feature);
3416ba1f63bSYuanhan Liu 
3426ba1f63bSYuanhan Liu 	return ((uint64_t)features_hi << 32) | features_lo;
3436ba1f63bSYuanhan Liu }
3446ba1f63bSYuanhan Liu 
3456ba1f63bSYuanhan Liu static void
modern_set_features(struct virtio_hw * hw,uint64_t features)3466ba1f63bSYuanhan Liu modern_set_features(struct virtio_hw *hw, uint64_t features)
3476ba1f63bSYuanhan Liu {
348266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
3496ba1f63bSYuanhan Liu 
350266ece29SMaxime Coquelin 	rte_write32(0, &dev->common_cfg->guest_feature_select);
351266ece29SMaxime Coquelin 	rte_write32(features & ((1ULL << 32) - 1),
352266ece29SMaxime Coquelin 		    &dev->common_cfg->guest_feature);
353266ece29SMaxime Coquelin 
354266ece29SMaxime Coquelin 	rte_write32(1, &dev->common_cfg->guest_feature_select);
355631d4ee4SSantosh Shukla 	rte_write32(features >> 32,
356266ece29SMaxime Coquelin 		    &dev->common_cfg->guest_feature);
3576ba1f63bSYuanhan Liu }
3586ba1f63bSYuanhan Liu 
359cbb135b3SMaxime Coquelin static int
modern_features_ok(struct virtio_hw * hw)360cbb135b3SMaxime Coquelin modern_features_ok(struct virtio_hw *hw)
361cbb135b3SMaxime Coquelin {
362b4f9a45aSMaxime Coquelin 	if (!virtio_with_feature(hw, VIRTIO_F_VERSION_1)) {
363f3854ebaSThomas Monjalon 		PMD_INIT_LOG(ERR, "Version 1+ required with modern devices");
364cbb135b3SMaxime Coquelin 		return -1;
365cbb135b3SMaxime Coquelin 	}
366cbb135b3SMaxime Coquelin 
367cbb135b3SMaxime Coquelin 	return 0;
368cbb135b3SMaxime Coquelin }
369cbb135b3SMaxime Coquelin 
3706ba1f63bSYuanhan Liu static uint8_t
modern_get_status(struct virtio_hw * hw)3716ba1f63bSYuanhan Liu modern_get_status(struct virtio_hw *hw)
3726ba1f63bSYuanhan Liu {
373266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
374266ece29SMaxime Coquelin 
375266ece29SMaxime Coquelin 	return rte_read8(&dev->common_cfg->device_status);
3766ba1f63bSYuanhan Liu }
3776ba1f63bSYuanhan Liu 
3786ba1f63bSYuanhan Liu static void
modern_set_status(struct virtio_hw * hw,uint8_t status)3796ba1f63bSYuanhan Liu modern_set_status(struct virtio_hw *hw, uint8_t status)
3806ba1f63bSYuanhan Liu {
381266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
382266ece29SMaxime Coquelin 
383266ece29SMaxime Coquelin 	rte_write8(status, &dev->common_cfg->device_status);
3846ba1f63bSYuanhan Liu }
3856ba1f63bSYuanhan Liu 
3866ba1f63bSYuanhan Liu static uint8_t
modern_get_isr(struct virtio_hw * hw)3876ba1f63bSYuanhan Liu modern_get_isr(struct virtio_hw *hw)
3886ba1f63bSYuanhan Liu {
389266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
390266ece29SMaxime Coquelin 
391266ece29SMaxime Coquelin 	return rte_read8(dev->isr);
3926ba1f63bSYuanhan Liu }
3936ba1f63bSYuanhan Liu 
3946ba1f63bSYuanhan Liu static uint16_t
modern_set_config_irq(struct virtio_hw * hw,uint16_t vec)3956ba1f63bSYuanhan Liu modern_set_config_irq(struct virtio_hw *hw, uint16_t vec)
3966ba1f63bSYuanhan Liu {
397266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
398266ece29SMaxime Coquelin 
399266ece29SMaxime Coquelin 	rte_write16(vec, &dev->common_cfg->msix_config);
400266ece29SMaxime Coquelin 	return rte_read16(&dev->common_cfg->msix_config);
4016ba1f63bSYuanhan Liu }
4026ba1f63bSYuanhan Liu 
4036ba1f63bSYuanhan Liu static uint16_t
modern_set_queue_irq(struct virtio_hw * hw,struct virtqueue * vq,uint16_t vec)404c49526acSJianfeng Tan modern_set_queue_irq(struct virtio_hw *hw, struct virtqueue *vq, uint16_t vec)
405c49526acSJianfeng Tan {
406266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
407266ece29SMaxime Coquelin 
408266ece29SMaxime Coquelin 	rte_write16(vq->vq_queue_index, &dev->common_cfg->queue_select);
409266ece29SMaxime Coquelin 	rte_write16(vec, &dev->common_cfg->queue_msix_vector);
410266ece29SMaxime Coquelin 	return rte_read16(&dev->common_cfg->queue_msix_vector);
411c49526acSJianfeng Tan }
412c49526acSJianfeng Tan 
413c49526acSJianfeng Tan static uint16_t
modern_get_queue_num(struct virtio_hw * hw,uint16_t queue_id)4146ba1f63bSYuanhan Liu modern_get_queue_num(struct virtio_hw *hw, uint16_t queue_id)
4156ba1f63bSYuanhan Liu {
416266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
417266ece29SMaxime Coquelin 
418266ece29SMaxime Coquelin 	rte_write16(queue_id, &dev->common_cfg->queue_select);
419266ece29SMaxime Coquelin 	return rte_read16(&dev->common_cfg->queue_size);
4206ba1f63bSYuanhan Liu }
4216ba1f63bSYuanhan Liu 
422595454c5SJianfeng Tan static int
modern_setup_queue(struct virtio_hw * hw,struct virtqueue * vq)4236ba1f63bSYuanhan Liu modern_setup_queue(struct virtio_hw *hw, struct virtqueue *vq)
4246ba1f63bSYuanhan Liu {
425266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
4266ba1f63bSYuanhan Liu 	uint64_t desc_addr, avail_addr, used_addr;
4276ba1f63bSYuanhan Liu 	uint16_t notify_off;
4286ba1f63bSYuanhan Liu 
42901ad44fdSHuawei Xie 	desc_addr = vq->vq_ring_mem;
4306ba1f63bSYuanhan Liu 	avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc);
4316ba1f63bSYuanhan Liu 	used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail,
4326ba1f63bSYuanhan Liu 							 ring[vq->vq_nentries]),
433df968842SMaxime Coquelin 				   VIRTIO_VRING_ALIGN);
4346ba1f63bSYuanhan Liu 
435266ece29SMaxime Coquelin 	rte_write16(vq->vq_queue_index, &dev->common_cfg->queue_select);
4366ba1f63bSYuanhan Liu 
437266ece29SMaxime Coquelin 	io_write64_twopart(desc_addr, &dev->common_cfg->queue_desc_lo,
438266ece29SMaxime Coquelin 				      &dev->common_cfg->queue_desc_hi);
439266ece29SMaxime Coquelin 	io_write64_twopart(avail_addr, &dev->common_cfg->queue_avail_lo,
440266ece29SMaxime Coquelin 				       &dev->common_cfg->queue_avail_hi);
441266ece29SMaxime Coquelin 	io_write64_twopart(used_addr, &dev->common_cfg->queue_used_lo,
442266ece29SMaxime Coquelin 				      &dev->common_cfg->queue_used_hi);
4436ba1f63bSYuanhan Liu 
444266ece29SMaxime Coquelin 	notify_off = rte_read16(&dev->common_cfg->queue_notify_off);
445266ece29SMaxime Coquelin 	vq->notify_addr = (void *)((uint8_t *)dev->notify_base +
446266ece29SMaxime Coquelin 				notify_off * dev->notify_off_multiplier);
4476ba1f63bSYuanhan Liu 
448266ece29SMaxime Coquelin 	rte_write16(1, &dev->common_cfg->queue_enable);
4496ba1f63bSYuanhan Liu 
4506ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "queue %u addresses:", vq->vq_queue_index);
4516ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t desc_addr: %" PRIx64, desc_addr);
4526ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t aval_addr: %" PRIx64, avail_addr);
4536ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t used_addr: %" PRIx64, used_addr);
4546ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t notify addr: %p (notify offset: %u)",
4556ba1f63bSYuanhan Liu 		vq->notify_addr, notify_off);
456595454c5SJianfeng Tan 
457595454c5SJianfeng Tan 	return 0;
4586ba1f63bSYuanhan Liu }
4596ba1f63bSYuanhan Liu 
4606ba1f63bSYuanhan Liu static void
modern_del_queue(struct virtio_hw * hw,struct virtqueue * vq)4616ba1f63bSYuanhan Liu modern_del_queue(struct virtio_hw *hw, struct virtqueue *vq)
4626ba1f63bSYuanhan Liu {
463266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
4646ba1f63bSYuanhan Liu 
465266ece29SMaxime Coquelin 	rte_write16(vq->vq_queue_index, &dev->common_cfg->queue_select);
4666ba1f63bSYuanhan Liu 
467266ece29SMaxime Coquelin 	io_write64_twopart(0, &dev->common_cfg->queue_desc_lo,
468266ece29SMaxime Coquelin 				  &dev->common_cfg->queue_desc_hi);
469266ece29SMaxime Coquelin 	io_write64_twopart(0, &dev->common_cfg->queue_avail_lo,
470266ece29SMaxime Coquelin 				  &dev->common_cfg->queue_avail_hi);
471266ece29SMaxime Coquelin 	io_write64_twopart(0, &dev->common_cfg->queue_used_lo,
472266ece29SMaxime Coquelin 				  &dev->common_cfg->queue_used_hi);
473266ece29SMaxime Coquelin 
474266ece29SMaxime Coquelin 	rte_write16(0, &dev->common_cfg->queue_enable);
4756ba1f63bSYuanhan Liu }
4766ba1f63bSYuanhan Liu 
4776ba1f63bSYuanhan Liu static void
modern_notify_queue(struct virtio_hw * hw,struct virtqueue * vq)4787e72f3ecSCheng Jiang modern_notify_queue(struct virtio_hw *hw, struct virtqueue *vq)
4796ba1f63bSYuanhan Liu {
4807e72f3ecSCheng Jiang 	uint32_t notify_data;
4817e72f3ecSCheng Jiang 
482b4f9a45aSMaxime Coquelin 	if (!virtio_with_feature(hw, VIRTIO_F_NOTIFICATION_DATA)) {
483518208f3SXiao Wang 		rte_write16(vq->vq_queue_index, vq->notify_addr);
4847e72f3ecSCheng Jiang 		return;
4857e72f3ecSCheng Jiang 	}
4867e72f3ecSCheng Jiang 
487b4f9a45aSMaxime Coquelin 	if (virtio_with_packed_queue(hw)) {
4887e72f3ecSCheng Jiang 		/*
4897e72f3ecSCheng Jiang 		 * Bit[0:15]: vq queue index
4907e72f3ecSCheng Jiang 		 * Bit[16:30]: avail index
4917e72f3ecSCheng Jiang 		 * Bit[31]: avail wrap counter
4927e72f3ecSCheng Jiang 		 */
4937e72f3ecSCheng Jiang 		notify_data = ((uint32_t)(!!(vq->vq_packed.cached_flags &
4947e72f3ecSCheng Jiang 				VRING_PACKED_DESC_F_AVAIL)) << 31) |
4957e72f3ecSCheng Jiang 				((uint32_t)vq->vq_avail_idx << 16) |
4967e72f3ecSCheng Jiang 				vq->vq_queue_index;
4977e72f3ecSCheng Jiang 	} else {
4987e72f3ecSCheng Jiang 		/*
4997e72f3ecSCheng Jiang 		 * Bit[0:15]: vq queue index
5007e72f3ecSCheng Jiang 		 * Bit[16:31]: avail index
5017e72f3ecSCheng Jiang 		 */
5027e72f3ecSCheng Jiang 		notify_data = ((uint32_t)vq->vq_avail_idx << 16) |
5037e72f3ecSCheng Jiang 				vq->vq_queue_index;
5047e72f3ecSCheng Jiang 	}
5057e72f3ecSCheng Jiang 	rte_write32(notify_data, vq->notify_addr);
5066ba1f63bSYuanhan Liu }
5076ba1f63bSYuanhan Liu 
5087793d293SMaxime Coquelin 
5097793d293SMaxime Coquelin 
5107793d293SMaxime Coquelin static void
modern_intr_detect(struct virtio_hw * hw)5117793d293SMaxime Coquelin modern_intr_detect(struct virtio_hw *hw)
5127793d293SMaxime Coquelin {
513c8d4b02fSMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
514c8d4b02fSMaxime Coquelin 
515aa0d4b8aSMaxime Coquelin 	dev->msix_status = vtpci_msix_detect(VTPCI_DEV(hw));
5166a504290SMaxime Coquelin 	hw->intr_lsc = !!dev->msix_status;
5177793d293SMaxime Coquelin }
5187793d293SMaxime Coquelin 
519f12908c8SMaxime Coquelin static int
modern_dev_close(struct virtio_hw * hw)520f12908c8SMaxime Coquelin modern_dev_close(struct virtio_hw *hw)
521f12908c8SMaxime Coquelin {
522aa0d4b8aSMaxime Coquelin 	rte_pci_unmap_device(VTPCI_DEV(hw));
523f12908c8SMaxime Coquelin 
524f12908c8SMaxime Coquelin 	return 0;
525f12908c8SMaxime Coquelin }
526f12908c8SMaxime Coquelin 
527f8b60756SMaxime Coquelin const struct virtio_ops modern_ops = {
5286ba1f63bSYuanhan Liu 	.read_dev_cfg	= modern_read_dev_config,
5296ba1f63bSYuanhan Liu 	.write_dev_cfg	= modern_write_dev_config,
5306ba1f63bSYuanhan Liu 	.get_status	= modern_get_status,
5316ba1f63bSYuanhan Liu 	.set_status	= modern_set_status,
5326ba1f63bSYuanhan Liu 	.get_features	= modern_get_features,
5336ba1f63bSYuanhan Liu 	.set_features	= modern_set_features,
534cbb135b3SMaxime Coquelin 	.features_ok	= modern_features_ok,
5356ba1f63bSYuanhan Liu 	.get_isr	= modern_get_isr,
5366ba1f63bSYuanhan Liu 	.set_config_irq	= modern_set_config_irq,
537c49526acSJianfeng Tan 	.set_queue_irq  = modern_set_queue_irq,
5386ba1f63bSYuanhan Liu 	.get_queue_num	= modern_get_queue_num,
5396ba1f63bSYuanhan Liu 	.setup_queue	= modern_setup_queue,
5406ba1f63bSYuanhan Liu 	.del_queue	= modern_del_queue,
5416ba1f63bSYuanhan Liu 	.notify_queue	= modern_notify_queue,
5427793d293SMaxime Coquelin 	.intr_detect	= modern_intr_detect,
543f12908c8SMaxime Coquelin 	.dev_close	= modern_dev_close,
5446ba1f63bSYuanhan Liu };
5456ba1f63bSYuanhan Liu 
5466ba1f63bSYuanhan Liu static void *
get_cfg_addr(struct rte_pci_device * dev,struct virtio_pci_cap * cap)5476ba1f63bSYuanhan Liu get_cfg_addr(struct rte_pci_device *dev, struct virtio_pci_cap *cap)
5486ba1f63bSYuanhan Liu {
5496ba1f63bSYuanhan Liu 	uint8_t  bar    = cap->bar;
5506ba1f63bSYuanhan Liu 	uint32_t length = cap->length;
5516ba1f63bSYuanhan Liu 	uint32_t offset = cap->offset;
5526ba1f63bSYuanhan Liu 	uint8_t *base;
5536ba1f63bSYuanhan Liu 
5540373ab9bSZhiyong Yang 	if (bar >= PCI_MAX_RESOURCE) {
5556ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR, "invalid bar: %u", bar);
5566ba1f63bSYuanhan Liu 		return NULL;
5576ba1f63bSYuanhan Liu 	}
5586ba1f63bSYuanhan Liu 
5596ba1f63bSYuanhan Liu 	if (offset + length < offset) {
5606ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR, "offset(%u) + length(%u) overflows",
5616ba1f63bSYuanhan Liu 			offset, length);
5626ba1f63bSYuanhan Liu 		return NULL;
5636ba1f63bSYuanhan Liu 	}
5646ba1f63bSYuanhan Liu 
5656ba1f63bSYuanhan Liu 	if (offset + length > dev->mem_resource[bar].len) {
5666ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR,
5676ba1f63bSYuanhan Liu 			"invalid cap: overflows bar space: %u > %" PRIu64,
5686ba1f63bSYuanhan Liu 			offset + length, dev->mem_resource[bar].len);
5696ba1f63bSYuanhan Liu 		return NULL;
5706ba1f63bSYuanhan Liu 	}
5716ba1f63bSYuanhan Liu 
5726ba1f63bSYuanhan Liu 	base = dev->mem_resource[bar].addr;
5736ba1f63bSYuanhan Liu 	if (base == NULL) {
5746ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR, "bar %u base addr is NULL", bar);
5756ba1f63bSYuanhan Liu 		return NULL;
5766ba1f63bSYuanhan Liu 	}
5776ba1f63bSYuanhan Liu 
5786ba1f63bSYuanhan Liu 	return base + offset;
5796ba1f63bSYuanhan Liu }
5806ba1f63bSYuanhan Liu 
5816ba1f63bSYuanhan Liu static int
virtio_read_caps(struct rte_pci_device * pci_dev,struct virtio_hw * hw)582266ece29SMaxime Coquelin virtio_read_caps(struct rte_pci_device *pci_dev, struct virtio_hw *hw)
5836ba1f63bSYuanhan Liu {
584266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
5856ba1f63bSYuanhan Liu 	struct virtio_pci_cap cap;
586a10b6e53SDavid Marchand 	off_t pos;
5876ba1f63bSYuanhan Liu 	int ret;
5886ba1f63bSYuanhan Liu 
589266ece29SMaxime Coquelin 	if (rte_pci_map_device(pci_dev)) {
5906ba1f63bSYuanhan Liu 		PMD_INIT_LOG(DEBUG, "failed to map pci device!");
5916ba1f63bSYuanhan Liu 		return -1;
5926ba1f63bSYuanhan Liu 	}
5936ba1f63bSYuanhan Liu 
594a10b6e53SDavid Marchand 	/*
595a10b6e53SDavid Marchand 	 * Transitional devices would also have this capability,
596cb482cb3SJianfeng Tan 	 * that's why we also check if msix is enabled.
597cb482cb3SJianfeng Tan 	 */
598a10b6e53SDavid Marchand 	dev->msix_status = vtpci_msix_detect(pci_dev);
59949bb1f7aSBrian Russell 
600baa9c550SDavid Marchand 	pos = rte_pci_find_capability(pci_dev, RTE_PCI_CAP_ID_VNDR);
601a10b6e53SDavid Marchand 	while (pos > 0) {
602a10b6e53SDavid Marchand 		if (rte_pci_read_config(pci_dev, &cap, sizeof(cap), pos) != sizeof(cap))
60349bb1f7aSBrian Russell 			break;
6046ba1f63bSYuanhan Liu 		PMD_INIT_LOG(DEBUG,
6056ba1f63bSYuanhan Liu 			"[%2x] cfg type: %u, bar: %u, offset: %04x, len: %u",
606a10b6e53SDavid Marchand 			(unsigned int)pos, cap.cfg_type, cap.bar, cap.offset, cap.length);
6076ba1f63bSYuanhan Liu 
6086ba1f63bSYuanhan Liu 		switch (cap.cfg_type) {
6096ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_COMMON_CFG:
610266ece29SMaxime Coquelin 			dev->common_cfg = get_cfg_addr(pci_dev, &cap);
6116ba1f63bSYuanhan Liu 			break;
6126ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_NOTIFY_CFG:
613a10b6e53SDavid Marchand 			ret = rte_pci_read_config(pci_dev, &dev->notify_off_multiplier,
6146ba1f63bSYuanhan Liu 				4, pos + sizeof(cap));
615ecfae151STiwei Bie 			if (ret != 4)
616ecfae151STiwei Bie 				PMD_INIT_LOG(DEBUG,
617ecfae151STiwei Bie 					"failed to read notify_off_multiplier, ret %d",
618ecfae151STiwei Bie 					ret);
619ecfae151STiwei Bie 			else
620266ece29SMaxime Coquelin 				dev->notify_base = get_cfg_addr(pci_dev, &cap);
6216ba1f63bSYuanhan Liu 			break;
6226ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_DEVICE_CFG:
623266ece29SMaxime Coquelin 			dev->dev_cfg = get_cfg_addr(pci_dev, &cap);
6246ba1f63bSYuanhan Liu 			break;
6256ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_ISR_CFG:
626266ece29SMaxime Coquelin 			dev->isr = get_cfg_addr(pci_dev, &cap);
6276ba1f63bSYuanhan Liu 			break;
6286ba1f63bSYuanhan Liu 		}
6296ba1f63bSYuanhan Liu 
630baa9c550SDavid Marchand 		pos = rte_pci_find_next_capability(pci_dev, RTE_PCI_CAP_ID_VNDR, pos);
6316ba1f63bSYuanhan Liu 	}
6326ba1f63bSYuanhan Liu 
633266ece29SMaxime Coquelin 	if (dev->common_cfg == NULL || dev->notify_base == NULL ||
634266ece29SMaxime Coquelin 	    dev->dev_cfg == NULL    || dev->isr == NULL) {
6356ba1f63bSYuanhan Liu 		PMD_INIT_LOG(INFO, "no modern virtio pci device found.");
6366ba1f63bSYuanhan Liu 		return -1;
6376ba1f63bSYuanhan Liu 	}
6386ba1f63bSYuanhan Liu 
6396ba1f63bSYuanhan Liu 	PMD_INIT_LOG(INFO, "found modern virtio pci device.");
6406ba1f63bSYuanhan Liu 
641266ece29SMaxime Coquelin 	PMD_INIT_LOG(DEBUG, "common cfg mapped at: %p", dev->common_cfg);
642266ece29SMaxime Coquelin 	PMD_INIT_LOG(DEBUG, "device cfg mapped at: %p", dev->dev_cfg);
643266ece29SMaxime Coquelin 	PMD_INIT_LOG(DEBUG, "isr cfg mapped at: %p", dev->isr);
6446ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "notify base: %p, notify off multiplier: %u",
645266ece29SMaxime Coquelin 		dev->notify_base, dev->notify_off_multiplier);
6466ba1f63bSYuanhan Liu 
6476ba1f63bSYuanhan Liu 	return 0;
6486ba1f63bSYuanhan Liu }
6496ba1f63bSYuanhan Liu 
650ac5e1d83SHuawei Xie /*
651ac5e1d83SHuawei Xie  * Return -1:
652ac5e1d83SHuawei Xie  *   if there is error mapping with VFIO/UIO.
653ac5e1d83SHuawei Xie  *   if port map error when driver type is KDRV_NONE.
654a65a34a8SStephen Hemminger  *   if marked as allowed but driver type is KDRV_UNKNOWN.
655ac5e1d83SHuawei Xie  * Return 1 if kernel driver is managing the device.
656ac5e1d83SHuawei Xie  * Return 0 on success.
657ac5e1d83SHuawei Xie  */
658d5bbeefcSYuanhan Liu int
vtpci_init(struct rte_pci_device * pci_dev,struct virtio_pci_dev * dev)6591ac79346SMaxime Coquelin vtpci_init(struct rte_pci_device *pci_dev, struct virtio_pci_dev *dev)
660d5bbeefcSYuanhan Liu {
6611ac79346SMaxime Coquelin 	struct virtio_hw *hw = &dev->hw;
6621ac79346SMaxime Coquelin 
663f305ecbbSMaxime Coquelin 	RTE_BUILD_BUG_ON(offsetof(struct virtio_pci_dev, hw) != 0);
664f305ecbbSMaxime Coquelin 
6656ba1f63bSYuanhan Liu 	/*
6666ba1f63bSYuanhan Liu 	 * Try if we can succeed reading virtio pci caps, which exists
6676ba1f63bSYuanhan Liu 	 * only on modern pci device. If failed, we fallback to legacy
6686ba1f63bSYuanhan Liu 	 * virtio handling.
6696ba1f63bSYuanhan Liu 	 */
6701ac79346SMaxime Coquelin 	if (virtio_read_caps(pci_dev, hw) == 0) {
6716ba1f63bSYuanhan Liu 		PMD_INIT_LOG(INFO, "modern virtio pci detected.");
672f8b60756SMaxime Coquelin 		VIRTIO_OPS(hw) = &modern_ops;
6731ac79346SMaxime Coquelin 		dev->modern = true;
6747793d293SMaxime Coquelin 		goto msix_detect;
6756ba1f63bSYuanhan Liu 	}
6766ba1f63bSYuanhan Liu 
6776ba1f63bSYuanhan Liu 	PMD_INIT_LOG(INFO, "trying with legacy virtio pci.");
6781ac79346SMaxime Coquelin 	if (rte_pci_ioport_map(pci_dev, 0, VTPCI_IO(hw)) < 0) {
6791ac79346SMaxime Coquelin 		rte_pci_unmap_device(pci_dev);
6801ac79346SMaxime Coquelin 		if (pci_dev->kdrv == RTE_PCI_KDRV_UNKNOWN &&
6811ac79346SMaxime Coquelin 		    (!pci_dev->device.devargs ||
6821ac79346SMaxime Coquelin 		     pci_dev->device.devargs->bus !=
6832b0e39c1SGaetan Rivet 		     rte_bus_find_by_name("pci"))) {
684ac5e1d83SHuawei Xie 			PMD_INIT_LOG(INFO,
685ac5e1d83SHuawei Xie 				"skip kernel managed virtio device.");
686ac5e1d83SHuawei Xie 			return 1;
687ac5e1d83SHuawei Xie 		}
688c52afa68SYuanhan Liu 		return -1;
689ac5e1d83SHuawei Xie 	}
6906ba1f63bSYuanhan Liu 
691f8b60756SMaxime Coquelin 	VIRTIO_OPS(hw) = &legacy_ops;
6921ac79346SMaxime Coquelin 	dev->modern = false;
693c52afa68SYuanhan Liu 
6947793d293SMaxime Coquelin msix_detect:
695f8b60756SMaxime Coquelin 	VIRTIO_OPS(hw)->intr_detect(hw);
6967793d293SMaxime Coquelin 
697d5bbeefcSYuanhan Liu 	return 0;
6986c3169a3SBruce Richardson }
699fe19d49cSZhiyong Yang 
vtpci_legacy_ioport_unmap(struct virtio_hw * hw)700c8d4b02fSMaxime Coquelin void vtpci_legacy_ioport_unmap(struct virtio_hw *hw)
701c8d4b02fSMaxime Coquelin {
702c8d4b02fSMaxime Coquelin 	rte_pci_ioport_unmap(VTPCI_IO(hw));
703c8d4b02fSMaxime Coquelin }
704c8d4b02fSMaxime Coquelin 
vtpci_legacy_ioport_map(struct virtio_hw * hw)705c8d4b02fSMaxime Coquelin int vtpci_legacy_ioport_map(struct virtio_hw *hw)
706c8d4b02fSMaxime Coquelin {
707aa0d4b8aSMaxime Coquelin 	return rte_pci_ioport_map(VTPCI_DEV(hw), 0, VTPCI_IO(hw));
708c8d4b02fSMaxime Coquelin }
709