xref: /dpdk/drivers/net/virtio/virtio_pci.c (revision 266ece29c12c65381edea9bbdc10f18248fb6aac)
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>
122b0e39c1SGaetan Rivet #include <rte_bus.h>
13631d4ee4SSantosh Shukla 
146c3169a3SBruce Richardson #include "virtio_pci.h"
156c3169a3SBruce Richardson #include "virtio_logs.h"
16d5bbeefcSYuanhan Liu #include "virtqueue.h"
176c3169a3SBruce Richardson 
186ba1f63bSYuanhan Liu /*
196ba1f63bSYuanhan Liu  * Following macros are derived from linux/pci_regs.h, however,
206ba1f63bSYuanhan Liu  * we can't simply include that header here, as there is no such
216ba1f63bSYuanhan Liu  * file for non-Linux platform.
226ba1f63bSYuanhan Liu  */
236ba1f63bSYuanhan Liu #define PCI_CAPABILITY_LIST	0x34
246ba1f63bSYuanhan Liu #define PCI_CAP_ID_VNDR		0x09
25554b6d3eSJianfeng Tan #define PCI_CAP_ID_MSIX		0x11
266ba1f63bSYuanhan Liu 
27b8f04520SDavid Marchand /*
28b8f04520SDavid Marchand  * The remaining space is defined by each driver as the per-driver
29b8f04520SDavid Marchand  * configuration space.
30b8f04520SDavid Marchand  */
31fe19d49cSZhiyong Yang #define VIRTIO_PCI_CONFIG(hw) \
32fe19d49cSZhiyong Yang 		(((hw)->use_msix == VIRTIO_MSIX_ENABLED) ? 24 : 20)
33b86af7b1SYuanhan Liu 
34595454c5SJianfeng Tan static inline int
35595454c5SJianfeng Tan check_vq_phys_addr_ok(struct virtqueue *vq)
36595454c5SJianfeng Tan {
37595454c5SJianfeng Tan 	/* Virtio PCI device VIRTIO_PCI_QUEUE_PF register is 32bit,
38595454c5SJianfeng Tan 	 * and only accepts 32 bit page frame number.
39595454c5SJianfeng Tan 	 * Check if the allocated physical memory exceeds 16TB.
40595454c5SJianfeng Tan 	 */
41595454c5SJianfeng Tan 	if ((vq->vq_ring_mem + vq->vq_ring_size - 1) >>
42595454c5SJianfeng Tan 			(VIRTIO_PCI_QUEUE_ADDR_SHIFT + 32)) {
43595454c5SJianfeng Tan 		PMD_INIT_LOG(ERR, "vring address shouldn't be above 16TB!");
44595454c5SJianfeng Tan 		return 0;
45595454c5SJianfeng Tan 	}
46595454c5SJianfeng Tan 
47595454c5SJianfeng Tan 	return 1;
48595454c5SJianfeng Tan }
49595454c5SJianfeng Tan 
507793d293SMaxime Coquelin #define PCI_MSIX_ENABLE 0x8000
517793d293SMaxime Coquelin 
527793d293SMaxime Coquelin static enum virtio_msix_status
537793d293SMaxime Coquelin vtpci_msix_detect(struct rte_pci_device *dev)
547793d293SMaxime Coquelin {
557793d293SMaxime Coquelin 	uint8_t pos;
567793d293SMaxime Coquelin 	int ret;
577793d293SMaxime Coquelin 
587793d293SMaxime Coquelin 	ret = rte_pci_read_config(dev, &pos, 1, PCI_CAPABILITY_LIST);
597793d293SMaxime Coquelin 	if (ret != 1) {
607793d293SMaxime Coquelin 		PMD_INIT_LOG(DEBUG,
617793d293SMaxime Coquelin 			     "failed to read pci capability list, ret %d", ret);
627793d293SMaxime Coquelin 		return VIRTIO_MSIX_NONE;
637793d293SMaxime Coquelin 	}
647793d293SMaxime Coquelin 
657793d293SMaxime Coquelin 	while (pos) {
667793d293SMaxime Coquelin 		uint8_t cap[2];
677793d293SMaxime Coquelin 
687793d293SMaxime Coquelin 		ret = rte_pci_read_config(dev, cap, sizeof(cap), pos);
697793d293SMaxime Coquelin 		if (ret != sizeof(cap)) {
707793d293SMaxime Coquelin 			PMD_INIT_LOG(DEBUG,
717793d293SMaxime Coquelin 				     "failed to read pci cap at pos: %x ret %d",
727793d293SMaxime Coquelin 				     pos, ret);
737793d293SMaxime Coquelin 			break;
747793d293SMaxime Coquelin 		}
757793d293SMaxime Coquelin 
767793d293SMaxime Coquelin 		if (cap[0] == PCI_CAP_ID_MSIX) {
777793d293SMaxime Coquelin 			uint16_t flags;
787793d293SMaxime Coquelin 
797793d293SMaxime Coquelin 			ret = rte_pci_read_config(dev, &flags, sizeof(flags),
807793d293SMaxime Coquelin 					pos + sizeof(cap));
817793d293SMaxime Coquelin 			if (ret != sizeof(flags)) {
827793d293SMaxime Coquelin 				PMD_INIT_LOG(DEBUG,
837793d293SMaxime Coquelin 					     "failed to read pci cap at pos:"
847793d293SMaxime Coquelin 					     " %x ret %d", pos + 2, ret);
857793d293SMaxime Coquelin 				break;
867793d293SMaxime Coquelin 			}
877793d293SMaxime Coquelin 
887793d293SMaxime Coquelin 			if (flags & PCI_MSIX_ENABLE)
897793d293SMaxime Coquelin 				return VIRTIO_MSIX_ENABLED;
907793d293SMaxime Coquelin 			else
917793d293SMaxime Coquelin 				return VIRTIO_MSIX_DISABLED;
927793d293SMaxime Coquelin 		}
937793d293SMaxime Coquelin 
947793d293SMaxime Coquelin 		pos = cap[1];
957793d293SMaxime Coquelin 	}
967793d293SMaxime Coquelin 
977793d293SMaxime Coquelin 	return VIRTIO_MSIX_NONE;
987793d293SMaxime Coquelin }
997793d293SMaxime Coquelin 
100281ccccbSDavid Marchand /*
101281ccccbSDavid Marchand  * Since we are in legacy mode:
102281ccccbSDavid Marchand  * http://ozlabs.org/~rusty/virtio-spec/virtio-0.9.5.pdf
103281ccccbSDavid Marchand  *
104281ccccbSDavid Marchand  * "Note that this is possible because while the virtio header is PCI (i.e.
105281ccccbSDavid Marchand  * little) endian, the device-specific region is encoded in the native endian of
106281ccccbSDavid Marchand  * the guest (where such distinction is applicable)."
107281ccccbSDavid Marchand  *
108281ccccbSDavid Marchand  * For powerpc which supports both, qemu supposes that cpu is big endian and
109281ccccbSDavid Marchand  * enforces this for the virtio-net stuff.
110281ccccbSDavid Marchand  */
111d5bbeefcSYuanhan Liu static void
112d5bbeefcSYuanhan Liu legacy_read_dev_config(struct virtio_hw *hw, size_t offset,
1136c3169a3SBruce Richardson 		       void *dst, int length)
1146c3169a3SBruce Richardson {
115281ccccbSDavid Marchand #ifdef RTE_ARCH_PPC_64
116281ccccbSDavid Marchand 	int size;
117281ccccbSDavid Marchand 
118281ccccbSDavid Marchand 	while (length > 0) {
119281ccccbSDavid Marchand 		if (length >= 4) {
120281ccccbSDavid Marchand 			size = 4;
1213dcfe039SThomas Monjalon 			rte_pci_ioport_read(VTPCI_IO(hw), dst, size,
122281ccccbSDavid Marchand 				VIRTIO_PCI_CONFIG(hw) + offset);
123281ccccbSDavid Marchand 			*(uint32_t *)dst = rte_be_to_cpu_32(*(uint32_t *)dst);
124281ccccbSDavid Marchand 		} else if (length >= 2) {
125281ccccbSDavid Marchand 			size = 2;
1263dcfe039SThomas Monjalon 			rte_pci_ioport_read(VTPCI_IO(hw), dst, size,
127281ccccbSDavid Marchand 				VIRTIO_PCI_CONFIG(hw) + offset);
128281ccccbSDavid Marchand 			*(uint16_t *)dst = rte_be_to_cpu_16(*(uint16_t *)dst);
129281ccccbSDavid Marchand 		} else {
130281ccccbSDavid Marchand 			size = 1;
1313dcfe039SThomas Monjalon 			rte_pci_ioport_read(VTPCI_IO(hw), dst, size,
132281ccccbSDavid Marchand 				VIRTIO_PCI_CONFIG(hw) + offset);
133281ccccbSDavid Marchand 		}
134281ccccbSDavid Marchand 
135281ccccbSDavid Marchand 		dst = (char *)dst + size;
136281ccccbSDavid Marchand 		offset += size;
137281ccccbSDavid Marchand 		length -= size;
138281ccccbSDavid Marchand 	}
139281ccccbSDavid Marchand #else
1403dcfe039SThomas Monjalon 	rte_pci_ioport_read(VTPCI_IO(hw), dst, length,
141b8f04520SDavid Marchand 		VIRTIO_PCI_CONFIG(hw) + offset);
142281ccccbSDavid Marchand #endif
1436c3169a3SBruce Richardson }
1446c3169a3SBruce Richardson 
145d5bbeefcSYuanhan Liu static void
146d5bbeefcSYuanhan Liu legacy_write_dev_config(struct virtio_hw *hw, size_t offset,
147d5bbeefcSYuanhan Liu 			const void *src, int length)
1486c3169a3SBruce Richardson {
149281ccccbSDavid Marchand #ifdef RTE_ARCH_PPC_64
150281ccccbSDavid Marchand 	union {
151281ccccbSDavid Marchand 		uint32_t u32;
152281ccccbSDavid Marchand 		uint16_t u16;
153281ccccbSDavid Marchand 	} tmp;
154281ccccbSDavid Marchand 	int size;
155281ccccbSDavid Marchand 
156281ccccbSDavid Marchand 	while (length > 0) {
157281ccccbSDavid Marchand 		if (length >= 4) {
158281ccccbSDavid Marchand 			size = 4;
159281ccccbSDavid Marchand 			tmp.u32 = rte_cpu_to_be_32(*(const uint32_t *)src);
1603dcfe039SThomas Monjalon 			rte_pci_ioport_write(VTPCI_IO(hw), &tmp.u32, size,
161281ccccbSDavid Marchand 				VIRTIO_PCI_CONFIG(hw) + offset);
162281ccccbSDavid Marchand 		} else if (length >= 2) {
163281ccccbSDavid Marchand 			size = 2;
164281ccccbSDavid Marchand 			tmp.u16 = rte_cpu_to_be_16(*(const uint16_t *)src);
1653dcfe039SThomas Monjalon 			rte_pci_ioport_write(VTPCI_IO(hw), &tmp.u16, size,
166281ccccbSDavid Marchand 				VIRTIO_PCI_CONFIG(hw) + offset);
167281ccccbSDavid Marchand 		} else {
168281ccccbSDavid Marchand 			size = 1;
1693dcfe039SThomas Monjalon 			rte_pci_ioport_write(VTPCI_IO(hw), src, size,
170281ccccbSDavid Marchand 				VIRTIO_PCI_CONFIG(hw) + offset);
171281ccccbSDavid Marchand 		}
172281ccccbSDavid Marchand 
173281ccccbSDavid Marchand 		src = (const char *)src + size;
174281ccccbSDavid Marchand 		offset += size;
175281ccccbSDavid Marchand 		length -= size;
176281ccccbSDavid Marchand 	}
177281ccccbSDavid Marchand #else
1783dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), src, length,
179b8f04520SDavid Marchand 		VIRTIO_PCI_CONFIG(hw) + offset);
180281ccccbSDavid Marchand #endif
1816c3169a3SBruce Richardson }
1826c3169a3SBruce Richardson 
1833891f233SYuanhan Liu static uint64_t
184d5bbeefcSYuanhan Liu legacy_get_features(struct virtio_hw *hw)
185d5bbeefcSYuanhan Liu {
18636ea36efSYuanhan Liu 	uint32_t dst;
187b8f04520SDavid Marchand 
1883dcfe039SThomas Monjalon 	rte_pci_ioport_read(VTPCI_IO(hw), &dst, 4, VIRTIO_PCI_HOST_FEATURES);
189b8f04520SDavid Marchand 	return dst;
190d5bbeefcSYuanhan Liu }
191d5bbeefcSYuanhan Liu 
192d5bbeefcSYuanhan Liu static void
1933891f233SYuanhan Liu legacy_set_features(struct virtio_hw *hw, uint64_t features)
194d5bbeefcSYuanhan Liu {
1953891f233SYuanhan Liu 	if ((features >> 32) != 0) {
1963891f233SYuanhan Liu 		PMD_DRV_LOG(ERR,
1973891f233SYuanhan Liu 			"only 32 bit features are allowed for legacy virtio!");
1983891f233SYuanhan Liu 		return;
1993891f233SYuanhan Liu 	}
2003dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &features, 4,
201b8f04520SDavid Marchand 		VIRTIO_PCI_GUEST_FEATURES);
202d5bbeefcSYuanhan Liu }
203d5bbeefcSYuanhan Liu 
204cbb135b3SMaxime Coquelin static int
205cbb135b3SMaxime Coquelin legacy_features_ok(struct virtio_hw *hw __rte_unused)
206cbb135b3SMaxime Coquelin {
207cbb135b3SMaxime Coquelin 	return 0;
208cbb135b3SMaxime Coquelin }
209cbb135b3SMaxime Coquelin 
210d5bbeefcSYuanhan Liu static uint8_t
211d5bbeefcSYuanhan Liu legacy_get_status(struct virtio_hw *hw)
212d5bbeefcSYuanhan Liu {
213b8f04520SDavid Marchand 	uint8_t dst;
214b8f04520SDavid Marchand 
2153dcfe039SThomas Monjalon 	rte_pci_ioport_read(VTPCI_IO(hw), &dst, 1, VIRTIO_PCI_STATUS);
216b8f04520SDavid Marchand 	return dst;
217d5bbeefcSYuanhan Liu }
218d5bbeefcSYuanhan Liu 
219d5bbeefcSYuanhan Liu static void
220d5bbeefcSYuanhan Liu legacy_set_status(struct virtio_hw *hw, uint8_t status)
221d5bbeefcSYuanhan Liu {
2223dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &status, 1, VIRTIO_PCI_STATUS);
223d5bbeefcSYuanhan Liu }
224d5bbeefcSYuanhan Liu 
225d5bbeefcSYuanhan Liu static uint8_t
226d5bbeefcSYuanhan Liu legacy_get_isr(struct virtio_hw *hw)
227d5bbeefcSYuanhan Liu {
228b8f04520SDavid Marchand 	uint8_t dst;
229b8f04520SDavid Marchand 
2303dcfe039SThomas Monjalon 	rte_pci_ioport_read(VTPCI_IO(hw), &dst, 1, VIRTIO_PCI_ISR);
231b8f04520SDavid Marchand 	return dst;
232d5bbeefcSYuanhan Liu }
233d5bbeefcSYuanhan Liu 
234d5bbeefcSYuanhan Liu /* Enable one vector (0) for Link State Intrerrupt */
235d5bbeefcSYuanhan Liu static uint16_t
236d5bbeefcSYuanhan Liu legacy_set_config_irq(struct virtio_hw *hw, uint16_t vec)
237d5bbeefcSYuanhan Liu {
238b8f04520SDavid Marchand 	uint16_t dst;
239b8f04520SDavid Marchand 
2403dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &vec, 2, VIRTIO_MSI_CONFIG_VECTOR);
2413dcfe039SThomas Monjalon 	rte_pci_ioport_read(VTPCI_IO(hw), &dst, 2, VIRTIO_MSI_CONFIG_VECTOR);
242b8f04520SDavid Marchand 	return dst;
243d5bbeefcSYuanhan Liu }
244d5bbeefcSYuanhan Liu 
245d5bbeefcSYuanhan Liu static uint16_t
246c49526acSJianfeng Tan legacy_set_queue_irq(struct virtio_hw *hw, struct virtqueue *vq, uint16_t vec)
247c49526acSJianfeng Tan {
248c49526acSJianfeng Tan 	uint16_t dst;
249c49526acSJianfeng Tan 
2503dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &vq->vq_queue_index, 2,
251c49526acSJianfeng Tan 		VIRTIO_PCI_QUEUE_SEL);
2523dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &vec, 2, VIRTIO_MSI_QUEUE_VECTOR);
2533dcfe039SThomas Monjalon 	rte_pci_ioport_read(VTPCI_IO(hw), &dst, 2, VIRTIO_MSI_QUEUE_VECTOR);
254c49526acSJianfeng Tan 	return dst;
255c49526acSJianfeng Tan }
256c49526acSJianfeng Tan 
257c49526acSJianfeng Tan static uint16_t
258d5bbeefcSYuanhan Liu legacy_get_queue_num(struct virtio_hw *hw, uint16_t queue_id)
259d5bbeefcSYuanhan Liu {
260b8f04520SDavid Marchand 	uint16_t dst;
261b8f04520SDavid Marchand 
2623dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &queue_id, 2, VIRTIO_PCI_QUEUE_SEL);
2633dcfe039SThomas Monjalon 	rte_pci_ioport_read(VTPCI_IO(hw), &dst, 2, VIRTIO_PCI_QUEUE_NUM);
264b8f04520SDavid Marchand 	return dst;
265d5bbeefcSYuanhan Liu }
266d5bbeefcSYuanhan Liu 
267595454c5SJianfeng Tan static int
268d5bbeefcSYuanhan Liu legacy_setup_queue(struct virtio_hw *hw, struct virtqueue *vq)
269d5bbeefcSYuanhan Liu {
270b8f04520SDavid Marchand 	uint32_t src;
271d5bbeefcSYuanhan Liu 
272595454c5SJianfeng Tan 	if (!check_vq_phys_addr_ok(vq))
273595454c5SJianfeng Tan 		return -1;
274595454c5SJianfeng Tan 
2753dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &vq->vq_queue_index, 2,
276b8f04520SDavid Marchand 		VIRTIO_PCI_QUEUE_SEL);
27701ad44fdSHuawei Xie 	src = vq->vq_ring_mem >> VIRTIO_PCI_QUEUE_ADDR_SHIFT;
2783dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &src, 4, VIRTIO_PCI_QUEUE_PFN);
279595454c5SJianfeng Tan 
280595454c5SJianfeng Tan 	return 0;
281d5bbeefcSYuanhan Liu }
282d5bbeefcSYuanhan Liu 
283d5bbeefcSYuanhan Liu static void
284d5bbeefcSYuanhan Liu legacy_del_queue(struct virtio_hw *hw, struct virtqueue *vq)
285d5bbeefcSYuanhan Liu {
286b8f04520SDavid Marchand 	uint32_t src = 0;
287d5bbeefcSYuanhan Liu 
2883dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &vq->vq_queue_index, 2,
289b8f04520SDavid Marchand 		VIRTIO_PCI_QUEUE_SEL);
2903dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &src, 4, VIRTIO_PCI_QUEUE_PFN);
291d5bbeefcSYuanhan Liu }
292d5bbeefcSYuanhan Liu 
293d5bbeefcSYuanhan Liu static void
294d5bbeefcSYuanhan Liu legacy_notify_queue(struct virtio_hw *hw, struct virtqueue *vq)
295d5bbeefcSYuanhan Liu {
2963dcfe039SThomas Monjalon 	rte_pci_ioport_write(VTPCI_IO(hw), &vq->vq_queue_index, 2,
297b8f04520SDavid Marchand 		VIRTIO_PCI_QUEUE_NOTIFY);
298d5bbeefcSYuanhan Liu }
299d5bbeefcSYuanhan Liu 
3007793d293SMaxime Coquelin static void
3017793d293SMaxime Coquelin legacy_intr_detect(struct virtio_hw *hw)
3027793d293SMaxime Coquelin {
3037793d293SMaxime Coquelin 	hw->use_msix = vtpci_msix_detect(VTPCI_DEV(hw));
3047793d293SMaxime Coquelin }
3057793d293SMaxime Coquelin 
306f12908c8SMaxime Coquelin static int
307f12908c8SMaxime Coquelin legacy_dev_close(struct virtio_hw *hw)
308f12908c8SMaxime Coquelin {
309f12908c8SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
310f12908c8SMaxime Coquelin 
311f12908c8SMaxime Coquelin 	rte_pci_unmap_device(dev->pci_dev);
312f12908c8SMaxime Coquelin 	rte_pci_ioport_unmap(VTPCI_IO(hw));
313f12908c8SMaxime Coquelin 
314f12908c8SMaxime Coquelin 	return 0;
315f12908c8SMaxime Coquelin }
316f12908c8SMaxime Coquelin 
3176d890f8aSYuanhan Liu const struct virtio_pci_ops legacy_ops = {
318d5bbeefcSYuanhan Liu 	.read_dev_cfg	= legacy_read_dev_config,
319d5bbeefcSYuanhan Liu 	.write_dev_cfg	= legacy_write_dev_config,
320d5bbeefcSYuanhan Liu 	.get_status	= legacy_get_status,
321d5bbeefcSYuanhan Liu 	.set_status	= legacy_set_status,
322d5bbeefcSYuanhan Liu 	.get_features	= legacy_get_features,
323d5bbeefcSYuanhan Liu 	.set_features	= legacy_set_features,
324cbb135b3SMaxime Coquelin 	.features_ok	= legacy_features_ok,
325d5bbeefcSYuanhan Liu 	.get_isr	= legacy_get_isr,
326d5bbeefcSYuanhan Liu 	.set_config_irq	= legacy_set_config_irq,
327c49526acSJianfeng Tan 	.set_queue_irq  = legacy_set_queue_irq,
328d5bbeefcSYuanhan Liu 	.get_queue_num	= legacy_get_queue_num,
329d5bbeefcSYuanhan Liu 	.setup_queue	= legacy_setup_queue,
330d5bbeefcSYuanhan Liu 	.del_queue	= legacy_del_queue,
331d5bbeefcSYuanhan Liu 	.notify_queue	= legacy_notify_queue,
3327793d293SMaxime Coquelin 	.intr_detect	= legacy_intr_detect,
333f12908c8SMaxime Coquelin 	.dev_close	= legacy_dev_close,
334d5bbeefcSYuanhan Liu };
335d5bbeefcSYuanhan Liu 
3366ba1f63bSYuanhan Liu static inline void
3376ba1f63bSYuanhan Liu io_write64_twopart(uint64_t val, uint32_t *lo, uint32_t *hi)
3386ba1f63bSYuanhan Liu {
339631d4ee4SSantosh Shukla 	rte_write32(val & ((1ULL << 32) - 1), lo);
340631d4ee4SSantosh Shukla 	rte_write32(val >> 32,		     hi);
3416ba1f63bSYuanhan Liu }
3426ba1f63bSYuanhan Liu 
3436ba1f63bSYuanhan Liu static void
3446ba1f63bSYuanhan Liu modern_read_dev_config(struct virtio_hw *hw, size_t offset,
3456ba1f63bSYuanhan Liu 		       void *dst, int length)
3466ba1f63bSYuanhan Liu {
347*266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
3486ba1f63bSYuanhan Liu 	int i;
3496ba1f63bSYuanhan Liu 	uint8_t *p;
3506ba1f63bSYuanhan Liu 	uint8_t old_gen, new_gen;
3516ba1f63bSYuanhan Liu 
3526ba1f63bSYuanhan Liu 	do {
353*266ece29SMaxime Coquelin 		old_gen = rte_read8(&dev->common_cfg->config_generation);
3546ba1f63bSYuanhan Liu 
3556ba1f63bSYuanhan Liu 		p = dst;
3566ba1f63bSYuanhan Liu 		for (i = 0;  i < length; i++)
357*266ece29SMaxime Coquelin 			*p++ = rte_read8((uint8_t *)dev->dev_cfg + offset + i);
3586ba1f63bSYuanhan Liu 
359*266ece29SMaxime Coquelin 		new_gen = rte_read8(&dev->common_cfg->config_generation);
3606ba1f63bSYuanhan Liu 	} while (old_gen != new_gen);
3616ba1f63bSYuanhan Liu }
3626ba1f63bSYuanhan Liu 
3636ba1f63bSYuanhan Liu static void
3646ba1f63bSYuanhan Liu modern_write_dev_config(struct virtio_hw *hw, size_t offset,
3656ba1f63bSYuanhan Liu 			const void *src, int length)
3666ba1f63bSYuanhan Liu {
367*266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
3686ba1f63bSYuanhan Liu 	int i;
3696ba1f63bSYuanhan Liu 	const uint8_t *p = src;
3706ba1f63bSYuanhan Liu 
3716ba1f63bSYuanhan Liu 	for (i = 0;  i < length; i++)
372*266ece29SMaxime Coquelin 		rte_write8((*p++), (((uint8_t *)dev->dev_cfg) + offset + i));
3736ba1f63bSYuanhan Liu }
3746ba1f63bSYuanhan Liu 
3756ba1f63bSYuanhan Liu static uint64_t
3766ba1f63bSYuanhan Liu modern_get_features(struct virtio_hw *hw)
3776ba1f63bSYuanhan Liu {
378*266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
3796ba1f63bSYuanhan Liu 	uint32_t features_lo, features_hi;
3806ba1f63bSYuanhan Liu 
381*266ece29SMaxime Coquelin 	rte_write32(0, &dev->common_cfg->device_feature_select);
382*266ece29SMaxime Coquelin 	features_lo = rte_read32(&dev->common_cfg->device_feature);
3836ba1f63bSYuanhan Liu 
384*266ece29SMaxime Coquelin 	rte_write32(1, &dev->common_cfg->device_feature_select);
385*266ece29SMaxime Coquelin 	features_hi = rte_read32(&dev->common_cfg->device_feature);
3866ba1f63bSYuanhan Liu 
3876ba1f63bSYuanhan Liu 	return ((uint64_t)features_hi << 32) | features_lo;
3886ba1f63bSYuanhan Liu }
3896ba1f63bSYuanhan Liu 
3906ba1f63bSYuanhan Liu static void
3916ba1f63bSYuanhan Liu modern_set_features(struct virtio_hw *hw, uint64_t features)
3926ba1f63bSYuanhan Liu {
393*266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
3946ba1f63bSYuanhan Liu 
395*266ece29SMaxime Coquelin 	rte_write32(0, &dev->common_cfg->guest_feature_select);
396*266ece29SMaxime Coquelin 	rte_write32(features & ((1ULL << 32) - 1),
397*266ece29SMaxime Coquelin 		    &dev->common_cfg->guest_feature);
398*266ece29SMaxime Coquelin 
399*266ece29SMaxime Coquelin 	rte_write32(1, &dev->common_cfg->guest_feature_select);
400631d4ee4SSantosh Shukla 	rte_write32(features >> 32,
401*266ece29SMaxime Coquelin 		    &dev->common_cfg->guest_feature);
4026ba1f63bSYuanhan Liu }
4036ba1f63bSYuanhan Liu 
404cbb135b3SMaxime Coquelin static int
405cbb135b3SMaxime Coquelin modern_features_ok(struct virtio_hw *hw)
406cbb135b3SMaxime Coquelin {
407cbb135b3SMaxime Coquelin 	if (!vtpci_with_feature(hw, VIRTIO_F_VERSION_1)) {
408cbb135b3SMaxime Coquelin 		PMD_INIT_LOG(ERR, "Version 1+ required with modern devices\n");
409cbb135b3SMaxime Coquelin 		return -1;
410cbb135b3SMaxime Coquelin 	}
411cbb135b3SMaxime Coquelin 
412cbb135b3SMaxime Coquelin 	return 0;
413cbb135b3SMaxime Coquelin }
414cbb135b3SMaxime Coquelin 
4156ba1f63bSYuanhan Liu static uint8_t
4166ba1f63bSYuanhan Liu modern_get_status(struct virtio_hw *hw)
4176ba1f63bSYuanhan Liu {
418*266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
419*266ece29SMaxime Coquelin 
420*266ece29SMaxime Coquelin 	return rte_read8(&dev->common_cfg->device_status);
4216ba1f63bSYuanhan Liu }
4226ba1f63bSYuanhan Liu 
4236ba1f63bSYuanhan Liu static void
4246ba1f63bSYuanhan Liu modern_set_status(struct virtio_hw *hw, uint8_t status)
4256ba1f63bSYuanhan Liu {
426*266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
427*266ece29SMaxime Coquelin 
428*266ece29SMaxime Coquelin 	rte_write8(status, &dev->common_cfg->device_status);
4296ba1f63bSYuanhan Liu }
4306ba1f63bSYuanhan Liu 
4316ba1f63bSYuanhan Liu static uint8_t
4326ba1f63bSYuanhan Liu modern_get_isr(struct virtio_hw *hw)
4336ba1f63bSYuanhan Liu {
434*266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
435*266ece29SMaxime Coquelin 
436*266ece29SMaxime Coquelin 	return rte_read8(dev->isr);
4376ba1f63bSYuanhan Liu }
4386ba1f63bSYuanhan Liu 
4396ba1f63bSYuanhan Liu static uint16_t
4406ba1f63bSYuanhan Liu modern_set_config_irq(struct virtio_hw *hw, uint16_t vec)
4416ba1f63bSYuanhan Liu {
442*266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
443*266ece29SMaxime Coquelin 
444*266ece29SMaxime Coquelin 	rte_write16(vec, &dev->common_cfg->msix_config);
445*266ece29SMaxime Coquelin 	return rte_read16(&dev->common_cfg->msix_config);
4466ba1f63bSYuanhan Liu }
4476ba1f63bSYuanhan Liu 
4486ba1f63bSYuanhan Liu static uint16_t
449c49526acSJianfeng Tan modern_set_queue_irq(struct virtio_hw *hw, struct virtqueue *vq, uint16_t vec)
450c49526acSJianfeng Tan {
451*266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
452*266ece29SMaxime Coquelin 
453*266ece29SMaxime Coquelin 	rte_write16(vq->vq_queue_index, &dev->common_cfg->queue_select);
454*266ece29SMaxime Coquelin 	rte_write16(vec, &dev->common_cfg->queue_msix_vector);
455*266ece29SMaxime Coquelin 	return rte_read16(&dev->common_cfg->queue_msix_vector);
456c49526acSJianfeng Tan }
457c49526acSJianfeng Tan 
458c49526acSJianfeng Tan static uint16_t
4596ba1f63bSYuanhan Liu modern_get_queue_num(struct virtio_hw *hw, uint16_t queue_id)
4606ba1f63bSYuanhan Liu {
461*266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
462*266ece29SMaxime Coquelin 
463*266ece29SMaxime Coquelin 	rte_write16(queue_id, &dev->common_cfg->queue_select);
464*266ece29SMaxime Coquelin 	return rte_read16(&dev->common_cfg->queue_size);
4656ba1f63bSYuanhan Liu }
4666ba1f63bSYuanhan Liu 
467595454c5SJianfeng Tan static int
4686ba1f63bSYuanhan Liu modern_setup_queue(struct virtio_hw *hw, struct virtqueue *vq)
4696ba1f63bSYuanhan Liu {
470*266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
4716ba1f63bSYuanhan Liu 	uint64_t desc_addr, avail_addr, used_addr;
4726ba1f63bSYuanhan Liu 	uint16_t notify_off;
4736ba1f63bSYuanhan Liu 
474595454c5SJianfeng Tan 	if (!check_vq_phys_addr_ok(vq))
475595454c5SJianfeng Tan 		return -1;
476595454c5SJianfeng Tan 
47701ad44fdSHuawei Xie 	desc_addr = vq->vq_ring_mem;
4786ba1f63bSYuanhan Liu 	avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc);
4796ba1f63bSYuanhan Liu 	used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail,
4806ba1f63bSYuanhan Liu 							 ring[vq->vq_nentries]),
4816ba1f63bSYuanhan Liu 				   VIRTIO_PCI_VRING_ALIGN);
4826ba1f63bSYuanhan Liu 
483*266ece29SMaxime Coquelin 	rte_write16(vq->vq_queue_index, &dev->common_cfg->queue_select);
4846ba1f63bSYuanhan Liu 
485*266ece29SMaxime Coquelin 	io_write64_twopart(desc_addr, &dev->common_cfg->queue_desc_lo,
486*266ece29SMaxime Coquelin 				      &dev->common_cfg->queue_desc_hi);
487*266ece29SMaxime Coquelin 	io_write64_twopart(avail_addr, &dev->common_cfg->queue_avail_lo,
488*266ece29SMaxime Coquelin 				       &dev->common_cfg->queue_avail_hi);
489*266ece29SMaxime Coquelin 	io_write64_twopart(used_addr, &dev->common_cfg->queue_used_lo,
490*266ece29SMaxime Coquelin 				      &dev->common_cfg->queue_used_hi);
4916ba1f63bSYuanhan Liu 
492*266ece29SMaxime Coquelin 	notify_off = rte_read16(&dev->common_cfg->queue_notify_off);
493*266ece29SMaxime Coquelin 	vq->notify_addr = (void *)((uint8_t *)dev->notify_base +
494*266ece29SMaxime Coquelin 				notify_off * dev->notify_off_multiplier);
4956ba1f63bSYuanhan Liu 
496*266ece29SMaxime Coquelin 	rte_write16(1, &dev->common_cfg->queue_enable);
4976ba1f63bSYuanhan Liu 
4986ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "queue %u addresses:", vq->vq_queue_index);
4996ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t desc_addr: %" PRIx64, desc_addr);
5006ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t aval_addr: %" PRIx64, avail_addr);
5016ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t used_addr: %" PRIx64, used_addr);
5026ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t notify addr: %p (notify offset: %u)",
5036ba1f63bSYuanhan Liu 		vq->notify_addr, notify_off);
504595454c5SJianfeng Tan 
505595454c5SJianfeng Tan 	return 0;
5066ba1f63bSYuanhan Liu }
5076ba1f63bSYuanhan Liu 
5086ba1f63bSYuanhan Liu static void
5096ba1f63bSYuanhan Liu modern_del_queue(struct virtio_hw *hw, struct virtqueue *vq)
5106ba1f63bSYuanhan Liu {
511*266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
5126ba1f63bSYuanhan Liu 
513*266ece29SMaxime Coquelin 	rte_write16(vq->vq_queue_index, &dev->common_cfg->queue_select);
5146ba1f63bSYuanhan Liu 
515*266ece29SMaxime Coquelin 	io_write64_twopart(0, &dev->common_cfg->queue_desc_lo,
516*266ece29SMaxime Coquelin 				  &dev->common_cfg->queue_desc_hi);
517*266ece29SMaxime Coquelin 	io_write64_twopart(0, &dev->common_cfg->queue_avail_lo,
518*266ece29SMaxime Coquelin 				  &dev->common_cfg->queue_avail_hi);
519*266ece29SMaxime Coquelin 	io_write64_twopart(0, &dev->common_cfg->queue_used_lo,
520*266ece29SMaxime Coquelin 				  &dev->common_cfg->queue_used_hi);
521*266ece29SMaxime Coquelin 
522*266ece29SMaxime Coquelin 	rte_write16(0, &dev->common_cfg->queue_enable);
5236ba1f63bSYuanhan Liu }
5246ba1f63bSYuanhan Liu 
5256ba1f63bSYuanhan Liu static void
5267e72f3ecSCheng Jiang modern_notify_queue(struct virtio_hw *hw, struct virtqueue *vq)
5276ba1f63bSYuanhan Liu {
5287e72f3ecSCheng Jiang 	uint32_t notify_data;
5297e72f3ecSCheng Jiang 
5307e72f3ecSCheng Jiang 	if (!vtpci_with_feature(hw, VIRTIO_F_NOTIFICATION_DATA)) {
531518208f3SXiao Wang 		rte_write16(vq->vq_queue_index, vq->notify_addr);
5327e72f3ecSCheng Jiang 		return;
5337e72f3ecSCheng Jiang 	}
5347e72f3ecSCheng Jiang 
5357e72f3ecSCheng Jiang 	if (vtpci_with_feature(hw, VIRTIO_F_RING_PACKED)) {
5367e72f3ecSCheng Jiang 		/*
5377e72f3ecSCheng Jiang 		 * Bit[0:15]: vq queue index
5387e72f3ecSCheng Jiang 		 * Bit[16:30]: avail index
5397e72f3ecSCheng Jiang 		 * Bit[31]: avail wrap counter
5407e72f3ecSCheng Jiang 		 */
5417e72f3ecSCheng Jiang 		notify_data = ((uint32_t)(!!(vq->vq_packed.cached_flags &
5427e72f3ecSCheng Jiang 				VRING_PACKED_DESC_F_AVAIL)) << 31) |
5437e72f3ecSCheng Jiang 				((uint32_t)vq->vq_avail_idx << 16) |
5447e72f3ecSCheng Jiang 				vq->vq_queue_index;
5457e72f3ecSCheng Jiang 	} else {
5467e72f3ecSCheng Jiang 		/*
5477e72f3ecSCheng Jiang 		 * Bit[0:15]: vq queue index
5487e72f3ecSCheng Jiang 		 * Bit[16:31]: avail index
5497e72f3ecSCheng Jiang 		 */
5507e72f3ecSCheng Jiang 		notify_data = ((uint32_t)vq->vq_avail_idx << 16) |
5517e72f3ecSCheng Jiang 				vq->vq_queue_index;
5527e72f3ecSCheng Jiang 	}
5537e72f3ecSCheng Jiang 	rte_write32(notify_data, vq->notify_addr);
5546ba1f63bSYuanhan Liu }
5556ba1f63bSYuanhan Liu 
5567793d293SMaxime Coquelin 
5577793d293SMaxime Coquelin 
5587793d293SMaxime Coquelin static void
5597793d293SMaxime Coquelin modern_intr_detect(struct virtio_hw *hw)
5607793d293SMaxime Coquelin {
5617793d293SMaxime Coquelin 	hw->use_msix = vtpci_msix_detect(VTPCI_DEV(hw));
5627793d293SMaxime Coquelin }
5637793d293SMaxime Coquelin 
564f12908c8SMaxime Coquelin static int
565f12908c8SMaxime Coquelin modern_dev_close(struct virtio_hw *hw)
566f12908c8SMaxime Coquelin {
567f12908c8SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
568f12908c8SMaxime Coquelin 
569f12908c8SMaxime Coquelin 	rte_pci_unmap_device(dev->pci_dev);
570f12908c8SMaxime Coquelin 
571f12908c8SMaxime Coquelin 	return 0;
572f12908c8SMaxime Coquelin }
573f12908c8SMaxime Coquelin 
5746d890f8aSYuanhan Liu const struct virtio_pci_ops modern_ops = {
5756ba1f63bSYuanhan Liu 	.read_dev_cfg	= modern_read_dev_config,
5766ba1f63bSYuanhan Liu 	.write_dev_cfg	= modern_write_dev_config,
5776ba1f63bSYuanhan Liu 	.get_status	= modern_get_status,
5786ba1f63bSYuanhan Liu 	.set_status	= modern_set_status,
5796ba1f63bSYuanhan Liu 	.get_features	= modern_get_features,
5806ba1f63bSYuanhan Liu 	.set_features	= modern_set_features,
581cbb135b3SMaxime Coquelin 	.features_ok	= modern_features_ok,
5826ba1f63bSYuanhan Liu 	.get_isr	= modern_get_isr,
5836ba1f63bSYuanhan Liu 	.set_config_irq	= modern_set_config_irq,
584c49526acSJianfeng Tan 	.set_queue_irq  = modern_set_queue_irq,
5856ba1f63bSYuanhan Liu 	.get_queue_num	= modern_get_queue_num,
5866ba1f63bSYuanhan Liu 	.setup_queue	= modern_setup_queue,
5876ba1f63bSYuanhan Liu 	.del_queue	= modern_del_queue,
5886ba1f63bSYuanhan Liu 	.notify_queue	= modern_notify_queue,
5897793d293SMaxime Coquelin 	.intr_detect	= modern_intr_detect,
590f12908c8SMaxime Coquelin 	.dev_close	= modern_dev_close,
5916ba1f63bSYuanhan Liu };
5926ba1f63bSYuanhan Liu 
5936ba1f63bSYuanhan Liu 
594d5bbeefcSYuanhan Liu void
595d5bbeefcSYuanhan Liu vtpci_read_dev_config(struct virtio_hw *hw, size_t offset,
596d5bbeefcSYuanhan Liu 		      void *dst, int length)
597d5bbeefcSYuanhan Liu {
598553f4593SYuanhan Liu 	VTPCI_OPS(hw)->read_dev_cfg(hw, offset, dst, length);
599d5bbeefcSYuanhan Liu }
600d5bbeefcSYuanhan Liu 
601d5bbeefcSYuanhan Liu void
602d5bbeefcSYuanhan Liu vtpci_write_dev_config(struct virtio_hw *hw, size_t offset,
603d5bbeefcSYuanhan Liu 		       const void *src, int length)
604d5bbeefcSYuanhan Liu {
605553f4593SYuanhan Liu 	VTPCI_OPS(hw)->write_dev_cfg(hw, offset, src, length);
606d5bbeefcSYuanhan Liu }
607d5bbeefcSYuanhan Liu 
6083891f233SYuanhan Liu uint64_t
6093891f233SYuanhan Liu vtpci_negotiate_features(struct virtio_hw *hw, uint64_t host_features)
6106c3169a3SBruce Richardson {
6113891f233SYuanhan Liu 	uint64_t features;
612d5bbeefcSYuanhan Liu 
6136c3169a3SBruce Richardson 	/*
6146c3169a3SBruce Richardson 	 * Limit negotiated features to what the driver, virtqueue, and
6156c3169a3SBruce Richardson 	 * host all support.
6166c3169a3SBruce Richardson 	 */
6176c3169a3SBruce Richardson 	features = host_features & hw->guest_features;
618553f4593SYuanhan Liu 	VTPCI_OPS(hw)->set_features(hw, features);
6196c3169a3SBruce Richardson 
6206c3169a3SBruce Richardson 	return features;
6216c3169a3SBruce Richardson }
6226c3169a3SBruce Richardson 
6236c3169a3SBruce Richardson void
6246c3169a3SBruce Richardson vtpci_reset(struct virtio_hw *hw)
6256c3169a3SBruce Richardson {
626553f4593SYuanhan Liu 	VTPCI_OPS(hw)->set_status(hw, VIRTIO_CONFIG_STATUS_RESET);
627d5bbeefcSYuanhan Liu 	/* flush status write */
628553f4593SYuanhan Liu 	VTPCI_OPS(hw)->get_status(hw);
6296c3169a3SBruce Richardson }
6306c3169a3SBruce Richardson 
6316c3169a3SBruce Richardson void
6326c3169a3SBruce Richardson vtpci_reinit_complete(struct virtio_hw *hw)
6336c3169a3SBruce Richardson {
6346c3169a3SBruce Richardson 	vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK);
6356c3169a3SBruce Richardson }
6366c3169a3SBruce Richardson 
6376c3169a3SBruce Richardson void
6386c3169a3SBruce Richardson vtpci_set_status(struct virtio_hw *hw, uint8_t status)
6396c3169a3SBruce Richardson {
6406c3169a3SBruce Richardson 	if (status != VIRTIO_CONFIG_STATUS_RESET)
641553f4593SYuanhan Liu 		status |= VTPCI_OPS(hw)->get_status(hw);
6426c3169a3SBruce Richardson 
643553f4593SYuanhan Liu 	VTPCI_OPS(hw)->set_status(hw, status);
6446c3169a3SBruce Richardson }
6456c3169a3SBruce Richardson 
6466c3169a3SBruce Richardson uint8_t
6476ba1f63bSYuanhan Liu vtpci_get_status(struct virtio_hw *hw)
6486ba1f63bSYuanhan Liu {
649553f4593SYuanhan Liu 	return VTPCI_OPS(hw)->get_status(hw);
6506ba1f63bSYuanhan Liu }
6516ba1f63bSYuanhan Liu 
6526ba1f63bSYuanhan Liu uint8_t
6536c3169a3SBruce Richardson vtpci_isr(struct virtio_hw *hw)
6546c3169a3SBruce Richardson {
655553f4593SYuanhan Liu 	return VTPCI_OPS(hw)->get_isr(hw);
6566c3169a3SBruce Richardson }
6576c3169a3SBruce Richardson 
6586ba1f63bSYuanhan Liu static void *
6596ba1f63bSYuanhan Liu get_cfg_addr(struct rte_pci_device *dev, struct virtio_pci_cap *cap)
6606ba1f63bSYuanhan Liu {
6616ba1f63bSYuanhan Liu 	uint8_t  bar    = cap->bar;
6626ba1f63bSYuanhan Liu 	uint32_t length = cap->length;
6636ba1f63bSYuanhan Liu 	uint32_t offset = cap->offset;
6646ba1f63bSYuanhan Liu 	uint8_t *base;
6656ba1f63bSYuanhan Liu 
6660373ab9bSZhiyong Yang 	if (bar >= PCI_MAX_RESOURCE) {
6676ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR, "invalid bar: %u", bar);
6686ba1f63bSYuanhan Liu 		return NULL;
6696ba1f63bSYuanhan Liu 	}
6706ba1f63bSYuanhan Liu 
6716ba1f63bSYuanhan Liu 	if (offset + length < offset) {
6726ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR, "offset(%u) + length(%u) overflows",
6736ba1f63bSYuanhan Liu 			offset, length);
6746ba1f63bSYuanhan Liu 		return NULL;
6756ba1f63bSYuanhan Liu 	}
6766ba1f63bSYuanhan Liu 
6776ba1f63bSYuanhan Liu 	if (offset + length > dev->mem_resource[bar].len) {
6786ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR,
6796ba1f63bSYuanhan Liu 			"invalid cap: overflows bar space: %u > %" PRIu64,
6806ba1f63bSYuanhan Liu 			offset + length, dev->mem_resource[bar].len);
6816ba1f63bSYuanhan Liu 		return NULL;
6826ba1f63bSYuanhan Liu 	}
6836ba1f63bSYuanhan Liu 
6846ba1f63bSYuanhan Liu 	base = dev->mem_resource[bar].addr;
6856ba1f63bSYuanhan Liu 	if (base == NULL) {
6866ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR, "bar %u base addr is NULL", bar);
6876ba1f63bSYuanhan Liu 		return NULL;
6886ba1f63bSYuanhan Liu 	}
6896ba1f63bSYuanhan Liu 
6906ba1f63bSYuanhan Liu 	return base + offset;
6916ba1f63bSYuanhan Liu }
6926ba1f63bSYuanhan Liu 
6936ba1f63bSYuanhan Liu static int
694*266ece29SMaxime Coquelin virtio_read_caps(struct rte_pci_device *pci_dev, struct virtio_hw *hw)
6956ba1f63bSYuanhan Liu {
696*266ece29SMaxime Coquelin 	struct virtio_pci_dev *dev = virtio_pci_get_dev(hw);
6976ba1f63bSYuanhan Liu 	uint8_t pos;
6986ba1f63bSYuanhan Liu 	struct virtio_pci_cap cap;
6996ba1f63bSYuanhan Liu 	int ret;
7006ba1f63bSYuanhan Liu 
701*266ece29SMaxime Coquelin 	if (rte_pci_map_device(pci_dev)) {
7026ba1f63bSYuanhan Liu 		PMD_INIT_LOG(DEBUG, "failed to map pci device!");
7036ba1f63bSYuanhan Liu 		return -1;
7046ba1f63bSYuanhan Liu 	}
7056ba1f63bSYuanhan Liu 
706*266ece29SMaxime Coquelin 	ret = rte_pci_read_config(pci_dev, &pos, 1, PCI_CAPABILITY_LIST);
70749bb1f7aSBrian Russell 	if (ret != 1) {
70849bb1f7aSBrian Russell 		PMD_INIT_LOG(DEBUG,
70949bb1f7aSBrian Russell 			     "failed to read pci capability list, ret %d", ret);
7106ba1f63bSYuanhan Liu 		return -1;
7116ba1f63bSYuanhan Liu 	}
7126ba1f63bSYuanhan Liu 
7136ba1f63bSYuanhan Liu 	while (pos) {
714*266ece29SMaxime Coquelin 		ret = rte_pci_read_config(pci_dev, &cap, 2, pos);
71549bb1f7aSBrian Russell 		if (ret != 2) {
71649bb1f7aSBrian Russell 			PMD_INIT_LOG(DEBUG,
71749bb1f7aSBrian Russell 				     "failed to read pci cap at pos: %x ret %d",
71849bb1f7aSBrian Russell 				     pos, ret);
7196ba1f63bSYuanhan Liu 			break;
7206ba1f63bSYuanhan Liu 		}
7216ba1f63bSYuanhan Liu 
722cb482cb3SJianfeng Tan 		if (cap.cap_vndr == PCI_CAP_ID_MSIX) {
723cb482cb3SJianfeng Tan 			/* Transitional devices would also have this capability,
724cb482cb3SJianfeng Tan 			 * that's why we also check if msix is enabled.
725cb482cb3SJianfeng Tan 			 * 1st byte is cap ID; 2nd byte is the position of next
726cb482cb3SJianfeng Tan 			 * cap; next two bytes are the flags.
727cb482cb3SJianfeng Tan 			 */
72849bb1f7aSBrian Russell 			uint16_t flags;
72949bb1f7aSBrian Russell 
730*266ece29SMaxime Coquelin 			ret = rte_pci_read_config(pci_dev, &flags, sizeof(flags),
73149bb1f7aSBrian Russell 					pos + 2);
73249bb1f7aSBrian Russell 			if (ret != sizeof(flags)) {
73349bb1f7aSBrian Russell 				PMD_INIT_LOG(DEBUG,
73449bb1f7aSBrian Russell 					     "failed to read pci cap at pos:"
73549bb1f7aSBrian Russell 					     " %x ret %d", pos + 2, ret);
73649bb1f7aSBrian Russell 				break;
73749bb1f7aSBrian Russell 			}
738cb482cb3SJianfeng Tan 
739cb482cb3SJianfeng Tan 			if (flags & PCI_MSIX_ENABLE)
740fe19d49cSZhiyong Yang 				hw->use_msix = VIRTIO_MSIX_ENABLED;
741fe19d49cSZhiyong Yang 			else
742fe19d49cSZhiyong Yang 				hw->use_msix = VIRTIO_MSIX_DISABLED;
743cb482cb3SJianfeng Tan 		}
744554b6d3eSJianfeng Tan 
7456ba1f63bSYuanhan Liu 		if (cap.cap_vndr != PCI_CAP_ID_VNDR) {
7466ba1f63bSYuanhan Liu 			PMD_INIT_LOG(DEBUG,
7476ba1f63bSYuanhan Liu 				"[%2x] skipping non VNDR cap id: %02x",
7486ba1f63bSYuanhan Liu 				pos, cap.cap_vndr);
7496ba1f63bSYuanhan Liu 			goto next;
7506ba1f63bSYuanhan Liu 		}
7516ba1f63bSYuanhan Liu 
752*266ece29SMaxime Coquelin 		ret = rte_pci_read_config(pci_dev, &cap, sizeof(cap), pos);
75349bb1f7aSBrian Russell 		if (ret != sizeof(cap)) {
75449bb1f7aSBrian Russell 			PMD_INIT_LOG(DEBUG,
75549bb1f7aSBrian Russell 				     "failed to read pci cap at pos: %x ret %d",
75649bb1f7aSBrian Russell 				     pos, ret);
75749bb1f7aSBrian Russell 			break;
75849bb1f7aSBrian Russell 		}
75949bb1f7aSBrian Russell 
7606ba1f63bSYuanhan Liu 		PMD_INIT_LOG(DEBUG,
7616ba1f63bSYuanhan Liu 			"[%2x] cfg type: %u, bar: %u, offset: %04x, len: %u",
7626ba1f63bSYuanhan Liu 			pos, cap.cfg_type, cap.bar, cap.offset, cap.length);
7636ba1f63bSYuanhan Liu 
7646ba1f63bSYuanhan Liu 		switch (cap.cfg_type) {
7656ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_COMMON_CFG:
766*266ece29SMaxime Coquelin 			dev->common_cfg = get_cfg_addr(pci_dev, &cap);
7676ba1f63bSYuanhan Liu 			break;
7686ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_NOTIFY_CFG:
769*266ece29SMaxime Coquelin 			ret = rte_pci_read_config(pci_dev,
770*266ece29SMaxime Coquelin 					&dev->notify_off_multiplier,
7716ba1f63bSYuanhan Liu 					4, pos + sizeof(cap));
772ecfae151STiwei Bie 			if (ret != 4)
773ecfae151STiwei Bie 				PMD_INIT_LOG(DEBUG,
774ecfae151STiwei Bie 					"failed to read notify_off_multiplier, ret %d",
775ecfae151STiwei Bie 					ret);
776ecfae151STiwei Bie 			else
777*266ece29SMaxime Coquelin 				dev->notify_base = get_cfg_addr(pci_dev, &cap);
7786ba1f63bSYuanhan Liu 			break;
7796ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_DEVICE_CFG:
780*266ece29SMaxime Coquelin 			dev->dev_cfg = get_cfg_addr(pci_dev, &cap);
7816ba1f63bSYuanhan Liu 			break;
7826ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_ISR_CFG:
783*266ece29SMaxime Coquelin 			dev->isr = get_cfg_addr(pci_dev, &cap);
7846ba1f63bSYuanhan Liu 			break;
7856ba1f63bSYuanhan Liu 		}
7866ba1f63bSYuanhan Liu 
7876ba1f63bSYuanhan Liu next:
7886ba1f63bSYuanhan Liu 		pos = cap.cap_next;
7896ba1f63bSYuanhan Liu 	}
7906ba1f63bSYuanhan Liu 
791*266ece29SMaxime Coquelin 	if (dev->common_cfg == NULL || dev->notify_base == NULL ||
792*266ece29SMaxime Coquelin 	    dev->dev_cfg == NULL    || dev->isr == NULL) {
7936ba1f63bSYuanhan Liu 		PMD_INIT_LOG(INFO, "no modern virtio pci device found.");
7946ba1f63bSYuanhan Liu 		return -1;
7956ba1f63bSYuanhan Liu 	}
7966ba1f63bSYuanhan Liu 
7976ba1f63bSYuanhan Liu 	PMD_INIT_LOG(INFO, "found modern virtio pci device.");
7986ba1f63bSYuanhan Liu 
799*266ece29SMaxime Coquelin 	PMD_INIT_LOG(DEBUG, "common cfg mapped at: %p", dev->common_cfg);
800*266ece29SMaxime Coquelin 	PMD_INIT_LOG(DEBUG, "device cfg mapped at: %p", dev->dev_cfg);
801*266ece29SMaxime Coquelin 	PMD_INIT_LOG(DEBUG, "isr cfg mapped at: %p", dev->isr);
8026ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "notify base: %p, notify off multiplier: %u",
803*266ece29SMaxime Coquelin 		dev->notify_base, dev->notify_off_multiplier);
8046ba1f63bSYuanhan Liu 
8056ba1f63bSYuanhan Liu 	return 0;
8066ba1f63bSYuanhan Liu }
8076ba1f63bSYuanhan Liu 
808ac5e1d83SHuawei Xie /*
809ac5e1d83SHuawei Xie  * Return -1:
810ac5e1d83SHuawei Xie  *   if there is error mapping with VFIO/UIO.
811ac5e1d83SHuawei Xie  *   if port map error when driver type is KDRV_NONE.
812a65a34a8SStephen Hemminger  *   if marked as allowed but driver type is KDRV_UNKNOWN.
813ac5e1d83SHuawei Xie  * Return 1 if kernel driver is managing the device.
814ac5e1d83SHuawei Xie  * Return 0 on success.
815ac5e1d83SHuawei Xie  */
816d5bbeefcSYuanhan Liu int
8171ac79346SMaxime Coquelin vtpci_init(struct rte_pci_device *pci_dev, struct virtio_pci_dev *dev)
818d5bbeefcSYuanhan Liu {
8191ac79346SMaxime Coquelin 	struct virtio_hw *hw = &dev->hw;
8201ac79346SMaxime Coquelin 
821f305ecbbSMaxime Coquelin 	RTE_BUILD_BUG_ON(offsetof(struct virtio_pci_dev, hw) != 0);
822f305ecbbSMaxime Coquelin 
823f12908c8SMaxime Coquelin 	dev->pci_dev = pci_dev;
824f12908c8SMaxime Coquelin 
8256ba1f63bSYuanhan Liu 	/*
8266ba1f63bSYuanhan Liu 	 * Try if we can succeed reading virtio pci caps, which exists
8276ba1f63bSYuanhan Liu 	 * only on modern pci device. If failed, we fallback to legacy
8286ba1f63bSYuanhan Liu 	 * virtio handling.
8296ba1f63bSYuanhan Liu 	 */
8301ac79346SMaxime Coquelin 	if (virtio_read_caps(pci_dev, hw) == 0) {
8316ba1f63bSYuanhan Liu 		PMD_INIT_LOG(INFO, "modern virtio pci detected.");
832553f4593SYuanhan Liu 		virtio_hw_internal[hw->port_id].vtpci_ops = &modern_ops;
8331ac79346SMaxime Coquelin 		dev->modern = true;
8347793d293SMaxime Coquelin 		goto msix_detect;
8356ba1f63bSYuanhan Liu 	}
8366ba1f63bSYuanhan Liu 
8376ba1f63bSYuanhan Liu 	PMD_INIT_LOG(INFO, "trying with legacy virtio pci.");
8381ac79346SMaxime Coquelin 	if (rte_pci_ioport_map(pci_dev, 0, VTPCI_IO(hw)) < 0) {
8391ac79346SMaxime Coquelin 		rte_pci_unmap_device(pci_dev);
8401ac79346SMaxime Coquelin 		if (pci_dev->kdrv == RTE_PCI_KDRV_UNKNOWN &&
8411ac79346SMaxime Coquelin 		    (!pci_dev->device.devargs ||
8421ac79346SMaxime Coquelin 		     pci_dev->device.devargs->bus !=
8432b0e39c1SGaetan Rivet 		     rte_bus_find_by_name("pci"))) {
844ac5e1d83SHuawei Xie 			PMD_INIT_LOG(INFO,
845ac5e1d83SHuawei Xie 				"skip kernel managed virtio device.");
846ac5e1d83SHuawei Xie 			return 1;
847ac5e1d83SHuawei Xie 		}
848c52afa68SYuanhan Liu 		return -1;
849ac5e1d83SHuawei Xie 	}
8506ba1f63bSYuanhan Liu 
851553f4593SYuanhan Liu 	virtio_hw_internal[hw->port_id].vtpci_ops = &legacy_ops;
8521ac79346SMaxime Coquelin 	dev->modern = false;
853c52afa68SYuanhan Liu 
8547793d293SMaxime Coquelin msix_detect:
8557793d293SMaxime Coquelin 	VTPCI_OPS(hw)->intr_detect(hw);
8567793d293SMaxime Coquelin 
857d5bbeefcSYuanhan Liu 	return 0;
8586c3169a3SBruce Richardson }
859fe19d49cSZhiyong Yang 
860