xref: /dpdk/drivers/net/virtio/virtio_pci.c (revision 7a66c72d6cd6617888d1e20082d923a80abff836)
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 
52b86af7b1SYuanhan Liu #define VIRTIO_PCI_REG_ADDR(hw, reg) \
53b86af7b1SYuanhan Liu 	(unsigned short)((hw)->io_base + (reg))
54b86af7b1SYuanhan Liu 
55b86af7b1SYuanhan Liu #define VIRTIO_READ_REG_1(hw, reg) \
56b86af7b1SYuanhan Liu 	inb((VIRTIO_PCI_REG_ADDR((hw), (reg))))
57b86af7b1SYuanhan Liu #define VIRTIO_WRITE_REG_1(hw, reg, value) \
58b86af7b1SYuanhan Liu 	outb_p((unsigned char)(value), (VIRTIO_PCI_REG_ADDR((hw), (reg))))
59b86af7b1SYuanhan Liu 
60b86af7b1SYuanhan Liu #define VIRTIO_READ_REG_2(hw, reg) \
61b86af7b1SYuanhan Liu 	inw((VIRTIO_PCI_REG_ADDR((hw), (reg))))
62b86af7b1SYuanhan Liu #define VIRTIO_WRITE_REG_2(hw, reg, value) \
63b86af7b1SYuanhan Liu 	outw_p((unsigned short)(value), (VIRTIO_PCI_REG_ADDR((hw), (reg))))
64b86af7b1SYuanhan Liu 
65b86af7b1SYuanhan Liu #define VIRTIO_READ_REG_4(hw, reg) \
66b86af7b1SYuanhan Liu 	inl((VIRTIO_PCI_REG_ADDR((hw), (reg))))
67b86af7b1SYuanhan Liu #define VIRTIO_WRITE_REG_4(hw, reg, value) \
68b86af7b1SYuanhan Liu 	outl_p((unsigned int)(value), (VIRTIO_PCI_REG_ADDR((hw), (reg))))
69b86af7b1SYuanhan Liu 
70d5bbeefcSYuanhan Liu static void
71d5bbeefcSYuanhan Liu legacy_read_dev_config(struct virtio_hw *hw, size_t offset,
726c3169a3SBruce Richardson 		       void *dst, int length)
736c3169a3SBruce Richardson {
746c3169a3SBruce Richardson 	uint64_t off;
756c3169a3SBruce Richardson 	uint8_t *d;
766c3169a3SBruce Richardson 	int size;
776c3169a3SBruce Richardson 
786c3169a3SBruce Richardson 	off = VIRTIO_PCI_CONFIG(hw) + offset;
796c3169a3SBruce Richardson 	for (d = dst; length > 0; d += size, off += size, length -= size) {
806c3169a3SBruce Richardson 		if (length >= 4) {
816c3169a3SBruce Richardson 			size = 4;
826c3169a3SBruce Richardson 			*(uint32_t *)d = VIRTIO_READ_REG_4(hw, off);
836c3169a3SBruce Richardson 		} else if (length >= 2) {
846c3169a3SBruce Richardson 			size = 2;
856c3169a3SBruce Richardson 			*(uint16_t *)d = VIRTIO_READ_REG_2(hw, off);
866c3169a3SBruce Richardson 		} else {
876c3169a3SBruce Richardson 			size = 1;
886c3169a3SBruce Richardson 			*d = VIRTIO_READ_REG_1(hw, off);
896c3169a3SBruce Richardson 		}
906c3169a3SBruce Richardson 	}
916c3169a3SBruce Richardson }
926c3169a3SBruce Richardson 
93d5bbeefcSYuanhan Liu static void
94d5bbeefcSYuanhan Liu legacy_write_dev_config(struct virtio_hw *hw, size_t offset,
95d5bbeefcSYuanhan Liu 			const void *src, int length)
966c3169a3SBruce Richardson {
976c3169a3SBruce Richardson 	uint64_t off;
98d5bbeefcSYuanhan Liu 	const uint8_t *s;
996c3169a3SBruce Richardson 	int size;
1006c3169a3SBruce Richardson 
1016c3169a3SBruce Richardson 	off = VIRTIO_PCI_CONFIG(hw) + offset;
1026c3169a3SBruce Richardson 	for (s = src; length > 0; s += size, off += size, length -= size) {
1036c3169a3SBruce Richardson 		if (length >= 4) {
1046c3169a3SBruce Richardson 			size = 4;
105d5bbeefcSYuanhan Liu 			VIRTIO_WRITE_REG_4(hw, off, *(const uint32_t *)s);
1066c3169a3SBruce Richardson 		} else if (length >= 2) {
1076c3169a3SBruce Richardson 			size = 2;
108d5bbeefcSYuanhan Liu 			VIRTIO_WRITE_REG_2(hw, off, *(const uint16_t *)s);
1096c3169a3SBruce Richardson 		} else {
1106c3169a3SBruce Richardson 			size = 1;
1116c3169a3SBruce Richardson 			VIRTIO_WRITE_REG_1(hw, off, *s);
1126c3169a3SBruce Richardson 		}
1136c3169a3SBruce Richardson 	}
1146c3169a3SBruce Richardson }
1156c3169a3SBruce Richardson 
1163891f233SYuanhan Liu static uint64_t
117d5bbeefcSYuanhan Liu legacy_get_features(struct virtio_hw *hw)
118d5bbeefcSYuanhan Liu {
119d5bbeefcSYuanhan Liu 	return VIRTIO_READ_REG_4(hw, VIRTIO_PCI_HOST_FEATURES);
120d5bbeefcSYuanhan Liu }
121d5bbeefcSYuanhan Liu 
122d5bbeefcSYuanhan Liu static void
1233891f233SYuanhan Liu legacy_set_features(struct virtio_hw *hw, uint64_t features)
124d5bbeefcSYuanhan Liu {
1253891f233SYuanhan Liu 	if ((features >> 32) != 0) {
1263891f233SYuanhan Liu 		PMD_DRV_LOG(ERR,
1273891f233SYuanhan Liu 			"only 32 bit features are allowed for legacy virtio!");
1283891f233SYuanhan Liu 		return;
1293891f233SYuanhan Liu 	}
130d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_4(hw, VIRTIO_PCI_GUEST_FEATURES, features);
131d5bbeefcSYuanhan Liu }
132d5bbeefcSYuanhan Liu 
133d5bbeefcSYuanhan Liu static uint8_t
134d5bbeefcSYuanhan Liu legacy_get_status(struct virtio_hw *hw)
135d5bbeefcSYuanhan Liu {
136d5bbeefcSYuanhan Liu 	return VIRTIO_READ_REG_1(hw, VIRTIO_PCI_STATUS);
137d5bbeefcSYuanhan Liu }
138d5bbeefcSYuanhan Liu 
139d5bbeefcSYuanhan Liu static void
140d5bbeefcSYuanhan Liu legacy_set_status(struct virtio_hw *hw, uint8_t status)
141d5bbeefcSYuanhan Liu {
142d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_1(hw, VIRTIO_PCI_STATUS, status);
143d5bbeefcSYuanhan Liu }
144d5bbeefcSYuanhan Liu 
145d5bbeefcSYuanhan Liu static void
146d5bbeefcSYuanhan Liu legacy_reset(struct virtio_hw *hw)
147d5bbeefcSYuanhan Liu {
148d5bbeefcSYuanhan Liu 	legacy_set_status(hw, VIRTIO_CONFIG_STATUS_RESET);
149d5bbeefcSYuanhan Liu }
150d5bbeefcSYuanhan Liu 
151d5bbeefcSYuanhan Liu static uint8_t
152d5bbeefcSYuanhan Liu legacy_get_isr(struct virtio_hw *hw)
153d5bbeefcSYuanhan Liu {
154d5bbeefcSYuanhan Liu 	return VIRTIO_READ_REG_1(hw, VIRTIO_PCI_ISR);
155d5bbeefcSYuanhan Liu }
156d5bbeefcSYuanhan Liu 
157d5bbeefcSYuanhan Liu /* Enable one vector (0) for Link State Intrerrupt */
158d5bbeefcSYuanhan Liu static uint16_t
159d5bbeefcSYuanhan Liu legacy_set_config_irq(struct virtio_hw *hw, uint16_t vec)
160d5bbeefcSYuanhan Liu {
161d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_2(hw, VIRTIO_MSI_CONFIG_VECTOR, vec);
162d5bbeefcSYuanhan Liu 	return VIRTIO_READ_REG_2(hw, VIRTIO_MSI_CONFIG_VECTOR);
163d5bbeefcSYuanhan Liu }
164d5bbeefcSYuanhan Liu 
165d5bbeefcSYuanhan Liu static uint16_t
166d5bbeefcSYuanhan Liu legacy_get_queue_num(struct virtio_hw *hw, uint16_t queue_id)
167d5bbeefcSYuanhan Liu {
168d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_SEL, queue_id);
169d5bbeefcSYuanhan Liu 	return VIRTIO_READ_REG_2(hw, VIRTIO_PCI_QUEUE_NUM);
170d5bbeefcSYuanhan Liu }
171d5bbeefcSYuanhan Liu 
172d5bbeefcSYuanhan Liu static void
173d5bbeefcSYuanhan Liu legacy_setup_queue(struct virtio_hw *hw, struct virtqueue *vq)
174d5bbeefcSYuanhan Liu {
175d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_SEL, vq->vq_queue_index);
176d5bbeefcSYuanhan Liu 
177d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_4(hw, VIRTIO_PCI_QUEUE_PFN,
178d5bbeefcSYuanhan Liu 		vq->mz->phys_addr >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
179d5bbeefcSYuanhan Liu }
180d5bbeefcSYuanhan Liu 
181d5bbeefcSYuanhan Liu static void
182d5bbeefcSYuanhan Liu legacy_del_queue(struct virtio_hw *hw, struct virtqueue *vq)
183d5bbeefcSYuanhan Liu {
184d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_SEL, vq->vq_queue_index);
185d5bbeefcSYuanhan Liu 
186d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_4(hw, VIRTIO_PCI_QUEUE_PFN, 0);
187d5bbeefcSYuanhan Liu }
188d5bbeefcSYuanhan Liu 
189d5bbeefcSYuanhan Liu static void
190d5bbeefcSYuanhan Liu legacy_notify_queue(struct virtio_hw *hw, struct virtqueue *vq)
191d5bbeefcSYuanhan Liu {
192d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_NOTIFY, vq->vq_queue_index);
193d5bbeefcSYuanhan Liu }
194d5bbeefcSYuanhan Liu 
195c52afa68SYuanhan Liu #ifdef RTE_EXEC_ENV_LINUXAPP
196c52afa68SYuanhan Liu static int
197c52afa68SYuanhan Liu parse_sysfs_value(const char *filename, unsigned long *val)
198c52afa68SYuanhan Liu {
199c52afa68SYuanhan Liu 	FILE *f;
200c52afa68SYuanhan Liu 	char buf[BUFSIZ];
201c52afa68SYuanhan Liu 	char *end = NULL;
202c52afa68SYuanhan Liu 
203c52afa68SYuanhan Liu 	f = fopen(filename, "r");
204c52afa68SYuanhan Liu 	if (f == NULL) {
205c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "%s(): cannot open sysfs value %s",
206c52afa68SYuanhan Liu 			     __func__, filename);
207c52afa68SYuanhan Liu 		return -1;
208c52afa68SYuanhan Liu 	}
209c52afa68SYuanhan Liu 
210c52afa68SYuanhan Liu 	if (fgets(buf, sizeof(buf), f) == NULL) {
211c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "%s(): cannot read sysfs value %s",
212c52afa68SYuanhan Liu 			     __func__, filename);
213c52afa68SYuanhan Liu 		fclose(f);
214c52afa68SYuanhan Liu 		return -1;
215c52afa68SYuanhan Liu 	}
216c52afa68SYuanhan Liu 	*val = strtoul(buf, &end, 0);
217c52afa68SYuanhan Liu 	if ((buf[0] == '\0') || (end == NULL) || (*end != '\n')) {
218c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "%s(): cannot parse sysfs value %s",
219c52afa68SYuanhan Liu 			     __func__, filename);
220c52afa68SYuanhan Liu 		fclose(f);
221c52afa68SYuanhan Liu 		return -1;
222c52afa68SYuanhan Liu 	}
223c52afa68SYuanhan Liu 	fclose(f);
224c52afa68SYuanhan Liu 	return 0;
225c52afa68SYuanhan Liu }
226c52afa68SYuanhan Liu 
227c52afa68SYuanhan Liu static int
228c52afa68SYuanhan Liu get_uio_dev(struct rte_pci_addr *loc, char *buf, unsigned int buflen,
229c52afa68SYuanhan Liu 			unsigned int *uio_num)
230c52afa68SYuanhan Liu {
231c52afa68SYuanhan Liu 	struct dirent *e;
232c52afa68SYuanhan Liu 	DIR *dir;
233c52afa68SYuanhan Liu 	char dirname[PATH_MAX];
234c52afa68SYuanhan Liu 
235c52afa68SYuanhan Liu 	/*
236c52afa68SYuanhan Liu 	 * depending on kernel version, uio can be located in uio/uioX
237c52afa68SYuanhan Liu 	 * or uio:uioX
238c52afa68SYuanhan Liu 	 */
239c52afa68SYuanhan Liu 	snprintf(dirname, sizeof(dirname),
240c52afa68SYuanhan Liu 		     SYSFS_PCI_DEVICES "/" PCI_PRI_FMT "/uio",
241c52afa68SYuanhan Liu 		     loc->domain, loc->bus, loc->devid, loc->function);
242c52afa68SYuanhan Liu 	dir = opendir(dirname);
243c52afa68SYuanhan Liu 	if (dir == NULL) {
244c52afa68SYuanhan Liu 		/* retry with the parent directory */
245c52afa68SYuanhan Liu 		snprintf(dirname, sizeof(dirname),
246c52afa68SYuanhan Liu 			     SYSFS_PCI_DEVICES "/" PCI_PRI_FMT,
247c52afa68SYuanhan Liu 			     loc->domain, loc->bus, loc->devid, loc->function);
248c52afa68SYuanhan Liu 		dir = opendir(dirname);
249c52afa68SYuanhan Liu 
250c52afa68SYuanhan Liu 		if (dir == NULL) {
251c52afa68SYuanhan Liu 			PMD_INIT_LOG(ERR, "Cannot opendir %s", dirname);
252c52afa68SYuanhan Liu 			return -1;
253c52afa68SYuanhan Liu 		}
254c52afa68SYuanhan Liu 	}
255c52afa68SYuanhan Liu 
256c52afa68SYuanhan Liu 	/* take the first file starting with "uio" */
257c52afa68SYuanhan Liu 	while ((e = readdir(dir)) != NULL) {
258c52afa68SYuanhan Liu 		/* format could be uio%d ...*/
259c52afa68SYuanhan Liu 		int shortprefix_len = sizeof("uio") - 1;
260c52afa68SYuanhan Liu 		/* ... or uio:uio%d */
261c52afa68SYuanhan Liu 		int longprefix_len = sizeof("uio:uio") - 1;
262c52afa68SYuanhan Liu 		char *endptr;
263c52afa68SYuanhan Liu 
264c52afa68SYuanhan Liu 		if (strncmp(e->d_name, "uio", 3) != 0)
265c52afa68SYuanhan Liu 			continue;
266c52afa68SYuanhan Liu 
267c52afa68SYuanhan Liu 		/* first try uio%d */
268c52afa68SYuanhan Liu 		errno = 0;
269c52afa68SYuanhan Liu 		*uio_num = strtoull(e->d_name + shortprefix_len, &endptr, 10);
270c52afa68SYuanhan Liu 		if (errno == 0 && endptr != (e->d_name + shortprefix_len)) {
271c52afa68SYuanhan Liu 			snprintf(buf, buflen, "%s/uio%u", dirname, *uio_num);
272c52afa68SYuanhan Liu 			break;
273c52afa68SYuanhan Liu 		}
274c52afa68SYuanhan Liu 
275c52afa68SYuanhan Liu 		/* then try uio:uio%d */
276c52afa68SYuanhan Liu 		errno = 0;
277c52afa68SYuanhan Liu 		*uio_num = strtoull(e->d_name + longprefix_len, &endptr, 10);
278c52afa68SYuanhan Liu 		if (errno == 0 && endptr != (e->d_name + longprefix_len)) {
279c52afa68SYuanhan Liu 			snprintf(buf, buflen, "%s/uio:uio%u", dirname,
280c52afa68SYuanhan Liu 				     *uio_num);
281c52afa68SYuanhan Liu 			break;
282c52afa68SYuanhan Liu 		}
283c52afa68SYuanhan Liu 	}
284c52afa68SYuanhan Liu 	closedir(dir);
285c52afa68SYuanhan Liu 
286c52afa68SYuanhan Liu 	/* No uio resource found */
287c52afa68SYuanhan Liu 	if (e == NULL) {
288c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "Could not find uio resource");
289c52afa68SYuanhan Liu 		return -1;
290c52afa68SYuanhan Liu 	}
291c52afa68SYuanhan Liu 
292c52afa68SYuanhan Liu 	return 0;
293c52afa68SYuanhan Liu }
294c52afa68SYuanhan Liu 
295c52afa68SYuanhan Liu static int
296c52afa68SYuanhan Liu legacy_virtio_has_msix(const struct rte_pci_addr *loc)
297c52afa68SYuanhan Liu {
298c52afa68SYuanhan Liu 	DIR *d;
299c52afa68SYuanhan Liu 	char dirname[PATH_MAX];
300c52afa68SYuanhan Liu 
301c52afa68SYuanhan Liu 	snprintf(dirname, sizeof(dirname),
302c52afa68SYuanhan Liu 		     SYSFS_PCI_DEVICES "/" PCI_PRI_FMT "/msi_irqs",
303c52afa68SYuanhan Liu 		     loc->domain, loc->bus, loc->devid, loc->function);
304c52afa68SYuanhan Liu 
305c52afa68SYuanhan Liu 	d = opendir(dirname);
306c52afa68SYuanhan Liu 	if (d)
307c52afa68SYuanhan Liu 		closedir(d);
308c52afa68SYuanhan Liu 
309693f715dSHuawei Xie 	return d != NULL;
310c52afa68SYuanhan Liu }
311c52afa68SYuanhan Liu 
312c52afa68SYuanhan Liu /* Extract I/O port numbers from sysfs */
313c52afa68SYuanhan Liu static int
314c52afa68SYuanhan Liu virtio_resource_init_by_uio(struct rte_pci_device *pci_dev)
315c52afa68SYuanhan Liu {
316c52afa68SYuanhan Liu 	char dirname[PATH_MAX];
317c52afa68SYuanhan Liu 	char filename[PATH_MAX];
318c52afa68SYuanhan Liu 	unsigned long start, size;
319c52afa68SYuanhan Liu 	unsigned int uio_num;
320c52afa68SYuanhan Liu 
321c52afa68SYuanhan Liu 	if (get_uio_dev(&pci_dev->addr, dirname, sizeof(dirname), &uio_num) < 0)
322c52afa68SYuanhan Liu 		return -1;
323c52afa68SYuanhan Liu 
324c52afa68SYuanhan Liu 	/* get portio size */
325c52afa68SYuanhan Liu 	snprintf(filename, sizeof(filename),
326c52afa68SYuanhan Liu 		     "%s/portio/port0/size", dirname);
327c52afa68SYuanhan Liu 	if (parse_sysfs_value(filename, &size) < 0) {
328c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "%s(): cannot parse size",
329c52afa68SYuanhan Liu 			     __func__);
330c52afa68SYuanhan Liu 		return -1;
331c52afa68SYuanhan Liu 	}
332c52afa68SYuanhan Liu 
333c52afa68SYuanhan Liu 	/* get portio start */
334c52afa68SYuanhan Liu 	snprintf(filename, sizeof(filename),
335c52afa68SYuanhan Liu 		 "%s/portio/port0/start", dirname);
336c52afa68SYuanhan Liu 	if (parse_sysfs_value(filename, &start) < 0) {
337c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "%s(): cannot parse portio start",
338c52afa68SYuanhan Liu 			     __func__);
339c52afa68SYuanhan Liu 		return -1;
340c52afa68SYuanhan Liu 	}
341c52afa68SYuanhan Liu 	pci_dev->mem_resource[0].addr = (void *)(uintptr_t)start;
342c52afa68SYuanhan Liu 	pci_dev->mem_resource[0].len =  (uint64_t)size;
343c52afa68SYuanhan Liu 	PMD_INIT_LOG(DEBUG,
344c52afa68SYuanhan Liu 		     "PCI Port IO found start=0x%lx with size=0x%lx",
345c52afa68SYuanhan Liu 		     start, size);
346c52afa68SYuanhan Liu 
347c52afa68SYuanhan Liu 	/* save fd */
348c52afa68SYuanhan Liu 	memset(dirname, 0, sizeof(dirname));
349c52afa68SYuanhan Liu 	snprintf(dirname, sizeof(dirname), "/dev/uio%u", uio_num);
350c52afa68SYuanhan Liu 	pci_dev->intr_handle.fd = open(dirname, O_RDWR);
351c52afa68SYuanhan Liu 	if (pci_dev->intr_handle.fd < 0) {
352c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "Cannot open %s: %s\n",
353c52afa68SYuanhan Liu 			dirname, strerror(errno));
354c52afa68SYuanhan Liu 		return -1;
355c52afa68SYuanhan Liu 	}
356c52afa68SYuanhan Liu 
357c52afa68SYuanhan Liu 	pci_dev->intr_handle.type = RTE_INTR_HANDLE_UIO;
358c52afa68SYuanhan Liu 	pci_dev->driver->drv_flags |= RTE_PCI_DRV_INTR_LSC;
359c52afa68SYuanhan Liu 
360c52afa68SYuanhan Liu 	return 0;
361c52afa68SYuanhan Liu }
362c52afa68SYuanhan Liu 
363c52afa68SYuanhan Liu /* Extract port I/O numbers from proc/ioports */
364c52afa68SYuanhan Liu static int
365c52afa68SYuanhan Liu virtio_resource_init_by_ioports(struct rte_pci_device *pci_dev)
366c52afa68SYuanhan Liu {
367c52afa68SYuanhan Liu 	uint16_t start, end;
368c52afa68SYuanhan Liu 	int size;
369c52afa68SYuanhan Liu 	FILE *fp;
370c52afa68SYuanhan Liu 	char *line = NULL;
371c52afa68SYuanhan Liu 	char pci_id[16];
372c52afa68SYuanhan Liu 	int found = 0;
373c52afa68SYuanhan Liu 	size_t linesz;
374c52afa68SYuanhan Liu 
375c52afa68SYuanhan Liu 	snprintf(pci_id, sizeof(pci_id), PCI_PRI_FMT,
376c52afa68SYuanhan Liu 		 pci_dev->addr.domain,
377c52afa68SYuanhan Liu 		 pci_dev->addr.bus,
378c52afa68SYuanhan Liu 		 pci_dev->addr.devid,
379c52afa68SYuanhan Liu 		 pci_dev->addr.function);
380c52afa68SYuanhan Liu 
381c52afa68SYuanhan Liu 	fp = fopen("/proc/ioports", "r");
382c52afa68SYuanhan Liu 	if (fp == NULL) {
383c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "%s(): can't open ioports", __func__);
384c52afa68SYuanhan Liu 		return -1;
385c52afa68SYuanhan Liu 	}
386c52afa68SYuanhan Liu 
387c52afa68SYuanhan Liu 	while (getdelim(&line, &linesz, '\n', fp) > 0) {
388c52afa68SYuanhan Liu 		char *ptr = line;
389c52afa68SYuanhan Liu 		char *left;
390c52afa68SYuanhan Liu 		int n;
391c52afa68SYuanhan Liu 
392c52afa68SYuanhan Liu 		n = strcspn(ptr, ":");
393c52afa68SYuanhan Liu 		ptr[n] = 0;
394c52afa68SYuanhan Liu 		left = &ptr[n + 1];
395c52afa68SYuanhan Liu 
396c52afa68SYuanhan Liu 		while (*left && isspace(*left))
397c52afa68SYuanhan Liu 			left++;
398c52afa68SYuanhan Liu 
399c52afa68SYuanhan Liu 		if (!strncmp(left, pci_id, strlen(pci_id))) {
400c52afa68SYuanhan Liu 			found = 1;
401c52afa68SYuanhan Liu 
402c52afa68SYuanhan Liu 			while (*ptr && isspace(*ptr))
403c52afa68SYuanhan Liu 				ptr++;
404c52afa68SYuanhan Liu 
405c52afa68SYuanhan Liu 			sscanf(ptr, "%04hx-%04hx", &start, &end);
406c52afa68SYuanhan Liu 			size = end - start + 1;
407c52afa68SYuanhan Liu 
408c52afa68SYuanhan Liu 			break;
409c52afa68SYuanhan Liu 		}
410c52afa68SYuanhan Liu 	}
411c52afa68SYuanhan Liu 
412c52afa68SYuanhan Liu 	free(line);
413c52afa68SYuanhan Liu 	fclose(fp);
414c52afa68SYuanhan Liu 
415c52afa68SYuanhan Liu 	if (!found)
416c52afa68SYuanhan Liu 		return -1;
417c52afa68SYuanhan Liu 
418c52afa68SYuanhan Liu 	pci_dev->mem_resource[0].addr = (void *)(uintptr_t)(uint32_t)start;
419c52afa68SYuanhan Liu 	pci_dev->mem_resource[0].len =  (uint64_t)size;
420c52afa68SYuanhan Liu 	PMD_INIT_LOG(DEBUG,
421c52afa68SYuanhan Liu 		"PCI Port IO found start=0x%x with size=0x%x",
422c52afa68SYuanhan Liu 		start, size);
423c52afa68SYuanhan Liu 
424c52afa68SYuanhan Liu 	/* can't support lsc interrupt without uio */
425c52afa68SYuanhan Liu 	pci_dev->driver->drv_flags &= ~RTE_PCI_DRV_INTR_LSC;
426c52afa68SYuanhan Liu 
427c52afa68SYuanhan Liu 	return 0;
428c52afa68SYuanhan Liu }
429c52afa68SYuanhan Liu 
430c52afa68SYuanhan Liu /* Extract I/O port numbers from sysfs */
431c52afa68SYuanhan Liu static int
432c52afa68SYuanhan Liu legacy_virtio_resource_init(struct rte_pci_device *pci_dev)
433c52afa68SYuanhan Liu {
434c52afa68SYuanhan Liu 	if (virtio_resource_init_by_uio(pci_dev) == 0)
435c52afa68SYuanhan Liu 		return 0;
436c52afa68SYuanhan Liu 	else
437c52afa68SYuanhan Liu 		return virtio_resource_init_by_ioports(pci_dev);
438c52afa68SYuanhan Liu }
439c52afa68SYuanhan Liu 
440c52afa68SYuanhan Liu #else
441c52afa68SYuanhan Liu static int
44225294cd3SDavid Marchand legacy_virtio_has_msix(const struct rte_pci_addr *loc __rte_unused)
443c52afa68SYuanhan Liu {
444c52afa68SYuanhan Liu 	/* nic_uio does not enable interrupts, return 0 (false). */
445c52afa68SYuanhan Liu 	return 0;
446c52afa68SYuanhan Liu }
447c52afa68SYuanhan Liu 
448c52afa68SYuanhan Liu static int
449c52afa68SYuanhan Liu legacy_virtio_resource_init(struct rte_pci_device *pci_dev __rte_unused)
450c52afa68SYuanhan Liu {
451c52afa68SYuanhan Liu 	/* no setup required */
452c52afa68SYuanhan Liu 	return 0;
453c52afa68SYuanhan Liu }
454c52afa68SYuanhan Liu #endif
455d5bbeefcSYuanhan Liu 
456d5bbeefcSYuanhan Liu static const struct virtio_pci_ops legacy_ops = {
457d5bbeefcSYuanhan Liu 	.read_dev_cfg	= legacy_read_dev_config,
458d5bbeefcSYuanhan Liu 	.write_dev_cfg	= legacy_write_dev_config,
459d5bbeefcSYuanhan Liu 	.reset		= legacy_reset,
460d5bbeefcSYuanhan Liu 	.get_status	= legacy_get_status,
461d5bbeefcSYuanhan Liu 	.set_status	= legacy_set_status,
462d5bbeefcSYuanhan Liu 	.get_features	= legacy_get_features,
463d5bbeefcSYuanhan Liu 	.set_features	= legacy_set_features,
464d5bbeefcSYuanhan Liu 	.get_isr	= legacy_get_isr,
465d5bbeefcSYuanhan Liu 	.set_config_irq	= legacy_set_config_irq,
466d5bbeefcSYuanhan Liu 	.get_queue_num	= legacy_get_queue_num,
467d5bbeefcSYuanhan Liu 	.setup_queue	= legacy_setup_queue,
468d5bbeefcSYuanhan Liu 	.del_queue	= legacy_del_queue,
469d5bbeefcSYuanhan Liu 	.notify_queue	= legacy_notify_queue,
470d5bbeefcSYuanhan Liu };
471d5bbeefcSYuanhan Liu 
472d5bbeefcSYuanhan Liu 
4736ba1f63bSYuanhan Liu static inline uint8_t
4746ba1f63bSYuanhan Liu io_read8(uint8_t *addr)
4756ba1f63bSYuanhan Liu {
4766ba1f63bSYuanhan Liu 	return *(volatile uint8_t *)addr;
4776ba1f63bSYuanhan Liu }
4786ba1f63bSYuanhan Liu 
4796ba1f63bSYuanhan Liu static inline void
4806ba1f63bSYuanhan Liu io_write8(uint8_t val, uint8_t *addr)
4816ba1f63bSYuanhan Liu {
4826ba1f63bSYuanhan Liu 	*(volatile uint8_t *)addr = val;
4836ba1f63bSYuanhan Liu }
4846ba1f63bSYuanhan Liu 
4856ba1f63bSYuanhan Liu static inline uint16_t
4866ba1f63bSYuanhan Liu io_read16(uint16_t *addr)
4876ba1f63bSYuanhan Liu {
4886ba1f63bSYuanhan Liu 	return *(volatile uint16_t *)addr;
4896ba1f63bSYuanhan Liu }
4906ba1f63bSYuanhan Liu 
4916ba1f63bSYuanhan Liu static inline void
4926ba1f63bSYuanhan Liu io_write16(uint16_t val, uint16_t *addr)
4936ba1f63bSYuanhan Liu {
4946ba1f63bSYuanhan Liu 	*(volatile uint16_t *)addr = val;
4956ba1f63bSYuanhan Liu }
4966ba1f63bSYuanhan Liu 
4976ba1f63bSYuanhan Liu static inline uint32_t
4986ba1f63bSYuanhan Liu io_read32(uint32_t *addr)
4996ba1f63bSYuanhan Liu {
5006ba1f63bSYuanhan Liu 	return *(volatile uint32_t *)addr;
5016ba1f63bSYuanhan Liu }
5026ba1f63bSYuanhan Liu 
5036ba1f63bSYuanhan Liu static inline void
5046ba1f63bSYuanhan Liu io_write32(uint32_t val, uint32_t *addr)
5056ba1f63bSYuanhan Liu {
5066ba1f63bSYuanhan Liu 	*(volatile uint32_t *)addr = val;
5076ba1f63bSYuanhan Liu }
5086ba1f63bSYuanhan Liu 
5096ba1f63bSYuanhan Liu static inline void
5106ba1f63bSYuanhan Liu io_write64_twopart(uint64_t val, uint32_t *lo, uint32_t *hi)
5116ba1f63bSYuanhan Liu {
5126ba1f63bSYuanhan Liu 	io_write32(val & ((1ULL << 32) - 1), lo);
5136ba1f63bSYuanhan Liu 	io_write32(val >> 32,		     hi);
5146ba1f63bSYuanhan Liu }
5156ba1f63bSYuanhan Liu 
5166ba1f63bSYuanhan Liu static void
5176ba1f63bSYuanhan Liu modern_read_dev_config(struct virtio_hw *hw, size_t offset,
5186ba1f63bSYuanhan Liu 		       void *dst, int length)
5196ba1f63bSYuanhan Liu {
5206ba1f63bSYuanhan Liu 	int i;
5216ba1f63bSYuanhan Liu 	uint8_t *p;
5226ba1f63bSYuanhan Liu 	uint8_t old_gen, new_gen;
5236ba1f63bSYuanhan Liu 
5246ba1f63bSYuanhan Liu 	do {
5256ba1f63bSYuanhan Liu 		old_gen = io_read8(&hw->common_cfg->config_generation);
5266ba1f63bSYuanhan Liu 
5276ba1f63bSYuanhan Liu 		p = dst;
5286ba1f63bSYuanhan Liu 		for (i = 0;  i < length; i++)
5296ba1f63bSYuanhan Liu 			*p++ = io_read8((uint8_t *)hw->dev_cfg + offset + i);
5306ba1f63bSYuanhan Liu 
5316ba1f63bSYuanhan Liu 		new_gen = io_read8(&hw->common_cfg->config_generation);
5326ba1f63bSYuanhan Liu 	} while (old_gen != new_gen);
5336ba1f63bSYuanhan Liu }
5346ba1f63bSYuanhan Liu 
5356ba1f63bSYuanhan Liu static void
5366ba1f63bSYuanhan Liu modern_write_dev_config(struct virtio_hw *hw, size_t offset,
5376ba1f63bSYuanhan Liu 			const void *src, int length)
5386ba1f63bSYuanhan Liu {
5396ba1f63bSYuanhan Liu 	int i;
5406ba1f63bSYuanhan Liu 	const uint8_t *p = src;
5416ba1f63bSYuanhan Liu 
5426ba1f63bSYuanhan Liu 	for (i = 0;  i < length; i++)
5436ba1f63bSYuanhan Liu 		io_write8(*p++, (uint8_t *)hw->dev_cfg + offset + i);
5446ba1f63bSYuanhan Liu }
5456ba1f63bSYuanhan Liu 
5466ba1f63bSYuanhan Liu static uint64_t
5476ba1f63bSYuanhan Liu modern_get_features(struct virtio_hw *hw)
5486ba1f63bSYuanhan Liu {
5496ba1f63bSYuanhan Liu 	uint32_t features_lo, features_hi;
5506ba1f63bSYuanhan Liu 
5516ba1f63bSYuanhan Liu 	io_write32(0, &hw->common_cfg->device_feature_select);
5526ba1f63bSYuanhan Liu 	features_lo = io_read32(&hw->common_cfg->device_feature);
5536ba1f63bSYuanhan Liu 
5546ba1f63bSYuanhan Liu 	io_write32(1, &hw->common_cfg->device_feature_select);
5556ba1f63bSYuanhan Liu 	features_hi = io_read32(&hw->common_cfg->device_feature);
5566ba1f63bSYuanhan Liu 
5576ba1f63bSYuanhan Liu 	return ((uint64_t)features_hi << 32) | features_lo;
5586ba1f63bSYuanhan Liu }
5596ba1f63bSYuanhan Liu 
5606ba1f63bSYuanhan Liu static void
5616ba1f63bSYuanhan Liu modern_set_features(struct virtio_hw *hw, uint64_t features)
5626ba1f63bSYuanhan Liu {
5636ba1f63bSYuanhan Liu 	io_write32(0, &hw->common_cfg->guest_feature_select);
5646ba1f63bSYuanhan Liu 	io_write32(features & ((1ULL << 32) - 1),
5656ba1f63bSYuanhan Liu 		&hw->common_cfg->guest_feature);
5666ba1f63bSYuanhan Liu 
5676ba1f63bSYuanhan Liu 	io_write32(1, &hw->common_cfg->guest_feature_select);
5686ba1f63bSYuanhan Liu 	io_write32(features >> 32,
5696ba1f63bSYuanhan Liu 		&hw->common_cfg->guest_feature);
5706ba1f63bSYuanhan Liu }
5716ba1f63bSYuanhan Liu 
5726ba1f63bSYuanhan Liu static uint8_t
5736ba1f63bSYuanhan Liu modern_get_status(struct virtio_hw *hw)
5746ba1f63bSYuanhan Liu {
5756ba1f63bSYuanhan Liu 	return io_read8(&hw->common_cfg->device_status);
5766ba1f63bSYuanhan Liu }
5776ba1f63bSYuanhan Liu 
5786ba1f63bSYuanhan Liu static void
5796ba1f63bSYuanhan Liu modern_set_status(struct virtio_hw *hw, uint8_t status)
5806ba1f63bSYuanhan Liu {
5816ba1f63bSYuanhan Liu 	io_write8(status, &hw->common_cfg->device_status);
5826ba1f63bSYuanhan Liu }
5836ba1f63bSYuanhan Liu 
5846ba1f63bSYuanhan Liu static void
5856ba1f63bSYuanhan Liu modern_reset(struct virtio_hw *hw)
5866ba1f63bSYuanhan Liu {
5876ba1f63bSYuanhan Liu 	modern_set_status(hw, VIRTIO_CONFIG_STATUS_RESET);
5886ba1f63bSYuanhan Liu 	modern_get_status(hw);
5896ba1f63bSYuanhan Liu }
5906ba1f63bSYuanhan Liu 
5916ba1f63bSYuanhan Liu static uint8_t
5926ba1f63bSYuanhan Liu modern_get_isr(struct virtio_hw *hw)
5936ba1f63bSYuanhan Liu {
5946ba1f63bSYuanhan Liu 	return io_read8(hw->isr);
5956ba1f63bSYuanhan Liu }
5966ba1f63bSYuanhan Liu 
5976ba1f63bSYuanhan Liu static uint16_t
5986ba1f63bSYuanhan Liu modern_set_config_irq(struct virtio_hw *hw, uint16_t vec)
5996ba1f63bSYuanhan Liu {
6006ba1f63bSYuanhan Liu 	io_write16(vec, &hw->common_cfg->msix_config);
6016ba1f63bSYuanhan Liu 	return io_read16(&hw->common_cfg->msix_config);
6026ba1f63bSYuanhan Liu }
6036ba1f63bSYuanhan Liu 
6046ba1f63bSYuanhan Liu static uint16_t
6056ba1f63bSYuanhan Liu modern_get_queue_num(struct virtio_hw *hw, uint16_t queue_id)
6066ba1f63bSYuanhan Liu {
6076ba1f63bSYuanhan Liu 	io_write16(queue_id, &hw->common_cfg->queue_select);
6086ba1f63bSYuanhan Liu 	return io_read16(&hw->common_cfg->queue_size);
6096ba1f63bSYuanhan Liu }
6106ba1f63bSYuanhan Liu 
6116ba1f63bSYuanhan Liu static void
6126ba1f63bSYuanhan Liu modern_setup_queue(struct virtio_hw *hw, struct virtqueue *vq)
6136ba1f63bSYuanhan Liu {
6146ba1f63bSYuanhan Liu 	uint64_t desc_addr, avail_addr, used_addr;
6156ba1f63bSYuanhan Liu 	uint16_t notify_off;
6166ba1f63bSYuanhan Liu 
6176ba1f63bSYuanhan Liu 	desc_addr = vq->mz->phys_addr;
6186ba1f63bSYuanhan Liu 	avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc);
6196ba1f63bSYuanhan Liu 	used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail,
6206ba1f63bSYuanhan Liu 							 ring[vq->vq_nentries]),
6216ba1f63bSYuanhan Liu 				   VIRTIO_PCI_VRING_ALIGN);
6226ba1f63bSYuanhan Liu 
6236ba1f63bSYuanhan Liu 	io_write16(vq->vq_queue_index, &hw->common_cfg->queue_select);
6246ba1f63bSYuanhan Liu 
6256ba1f63bSYuanhan Liu 	io_write64_twopart(desc_addr, &hw->common_cfg->queue_desc_lo,
6266ba1f63bSYuanhan Liu 				      &hw->common_cfg->queue_desc_hi);
6276ba1f63bSYuanhan Liu 	io_write64_twopart(avail_addr, &hw->common_cfg->queue_avail_lo,
6286ba1f63bSYuanhan Liu 				       &hw->common_cfg->queue_avail_hi);
6296ba1f63bSYuanhan Liu 	io_write64_twopart(used_addr, &hw->common_cfg->queue_used_lo,
6306ba1f63bSYuanhan Liu 				      &hw->common_cfg->queue_used_hi);
6316ba1f63bSYuanhan Liu 
6326ba1f63bSYuanhan Liu 	notify_off = io_read16(&hw->common_cfg->queue_notify_off);
6336ba1f63bSYuanhan Liu 	vq->notify_addr = (void *)((uint8_t *)hw->notify_base +
6346ba1f63bSYuanhan Liu 				notify_off * hw->notify_off_multiplier);
6356ba1f63bSYuanhan Liu 
6366ba1f63bSYuanhan Liu 	io_write16(1, &hw->common_cfg->queue_enable);
6376ba1f63bSYuanhan Liu 
6386ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "queue %u addresses:", vq->vq_queue_index);
6396ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t desc_addr: %" PRIx64, desc_addr);
6406ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t aval_addr: %" PRIx64, avail_addr);
6416ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t used_addr: %" PRIx64, used_addr);
6426ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t notify addr: %p (notify offset: %u)",
6436ba1f63bSYuanhan Liu 		vq->notify_addr, notify_off);
6446ba1f63bSYuanhan Liu }
6456ba1f63bSYuanhan Liu 
6466ba1f63bSYuanhan Liu static void
6476ba1f63bSYuanhan Liu modern_del_queue(struct virtio_hw *hw, struct virtqueue *vq)
6486ba1f63bSYuanhan Liu {
6496ba1f63bSYuanhan Liu 	io_write16(vq->vq_queue_index, &hw->common_cfg->queue_select);
6506ba1f63bSYuanhan Liu 
6516ba1f63bSYuanhan Liu 	io_write64_twopart(0, &hw->common_cfg->queue_desc_lo,
6526ba1f63bSYuanhan Liu 				  &hw->common_cfg->queue_desc_hi);
6536ba1f63bSYuanhan Liu 	io_write64_twopart(0, &hw->common_cfg->queue_avail_lo,
6546ba1f63bSYuanhan Liu 				  &hw->common_cfg->queue_avail_hi);
6556ba1f63bSYuanhan Liu 	io_write64_twopart(0, &hw->common_cfg->queue_used_lo,
6566ba1f63bSYuanhan Liu 				  &hw->common_cfg->queue_used_hi);
6576ba1f63bSYuanhan Liu 
6586ba1f63bSYuanhan Liu 	io_write16(0, &hw->common_cfg->queue_enable);
6596ba1f63bSYuanhan Liu }
6606ba1f63bSYuanhan Liu 
6616ba1f63bSYuanhan Liu static void
6626ba1f63bSYuanhan Liu modern_notify_queue(struct virtio_hw *hw __rte_unused, struct virtqueue *vq)
6636ba1f63bSYuanhan Liu {
6646ba1f63bSYuanhan Liu 	io_write16(1, vq->notify_addr);
6656ba1f63bSYuanhan Liu }
6666ba1f63bSYuanhan Liu 
6676ba1f63bSYuanhan Liu static const struct virtio_pci_ops modern_ops = {
6686ba1f63bSYuanhan Liu 	.read_dev_cfg	= modern_read_dev_config,
6696ba1f63bSYuanhan Liu 	.write_dev_cfg	= modern_write_dev_config,
6706ba1f63bSYuanhan Liu 	.reset		= modern_reset,
6716ba1f63bSYuanhan Liu 	.get_status	= modern_get_status,
6726ba1f63bSYuanhan Liu 	.set_status	= modern_set_status,
6736ba1f63bSYuanhan Liu 	.get_features	= modern_get_features,
6746ba1f63bSYuanhan Liu 	.set_features	= modern_set_features,
6756ba1f63bSYuanhan Liu 	.get_isr	= modern_get_isr,
6766ba1f63bSYuanhan Liu 	.set_config_irq	= modern_set_config_irq,
6776ba1f63bSYuanhan Liu 	.get_queue_num	= modern_get_queue_num,
6786ba1f63bSYuanhan Liu 	.setup_queue	= modern_setup_queue,
6796ba1f63bSYuanhan Liu 	.del_queue	= modern_del_queue,
6806ba1f63bSYuanhan Liu 	.notify_queue	= modern_notify_queue,
6816ba1f63bSYuanhan Liu };
6826ba1f63bSYuanhan Liu 
6836ba1f63bSYuanhan Liu 
684d5bbeefcSYuanhan Liu void
685d5bbeefcSYuanhan Liu vtpci_read_dev_config(struct virtio_hw *hw, size_t offset,
686d5bbeefcSYuanhan Liu 		      void *dst, int length)
687d5bbeefcSYuanhan Liu {
688d5bbeefcSYuanhan Liu 	hw->vtpci_ops->read_dev_cfg(hw, offset, dst, length);
689d5bbeefcSYuanhan Liu }
690d5bbeefcSYuanhan Liu 
691d5bbeefcSYuanhan Liu void
692d5bbeefcSYuanhan Liu vtpci_write_dev_config(struct virtio_hw *hw, size_t offset,
693d5bbeefcSYuanhan Liu 		       const void *src, int length)
694d5bbeefcSYuanhan Liu {
695d5bbeefcSYuanhan Liu 	hw->vtpci_ops->write_dev_cfg(hw, offset, src, length);
696d5bbeefcSYuanhan Liu }
697d5bbeefcSYuanhan Liu 
6983891f233SYuanhan Liu uint64_t
6993891f233SYuanhan Liu vtpci_negotiate_features(struct virtio_hw *hw, uint64_t host_features)
7006c3169a3SBruce Richardson {
7013891f233SYuanhan Liu 	uint64_t features;
702d5bbeefcSYuanhan Liu 
7036c3169a3SBruce Richardson 	/*
7046c3169a3SBruce Richardson 	 * Limit negotiated features to what the driver, virtqueue, and
7056c3169a3SBruce Richardson 	 * host all support.
7066c3169a3SBruce Richardson 	 */
7076c3169a3SBruce Richardson 	features = host_features & hw->guest_features;
708d5bbeefcSYuanhan Liu 	hw->vtpci_ops->set_features(hw, features);
7096c3169a3SBruce Richardson 
7106c3169a3SBruce Richardson 	return features;
7116c3169a3SBruce Richardson }
7126c3169a3SBruce Richardson 
7136c3169a3SBruce Richardson void
7146c3169a3SBruce Richardson vtpci_reset(struct virtio_hw *hw)
7156c3169a3SBruce Richardson {
716d5bbeefcSYuanhan Liu 	hw->vtpci_ops->set_status(hw, VIRTIO_CONFIG_STATUS_RESET);
717d5bbeefcSYuanhan Liu 	/* flush status write */
718d5bbeefcSYuanhan Liu 	hw->vtpci_ops->get_status(hw);
7196c3169a3SBruce Richardson }
7206c3169a3SBruce Richardson 
7216c3169a3SBruce Richardson void
7226c3169a3SBruce Richardson vtpci_reinit_complete(struct virtio_hw *hw)
7236c3169a3SBruce Richardson {
7246c3169a3SBruce Richardson 	vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK);
7256c3169a3SBruce Richardson }
7266c3169a3SBruce Richardson 
7276c3169a3SBruce Richardson void
7286c3169a3SBruce Richardson vtpci_set_status(struct virtio_hw *hw, uint8_t status)
7296c3169a3SBruce Richardson {
7306c3169a3SBruce Richardson 	if (status != VIRTIO_CONFIG_STATUS_RESET)
731d5bbeefcSYuanhan Liu 		status |= hw->vtpci_ops->get_status(hw);
7326c3169a3SBruce Richardson 
733d5bbeefcSYuanhan Liu 	hw->vtpci_ops->set_status(hw, status);
7346c3169a3SBruce Richardson }
7356c3169a3SBruce Richardson 
7366c3169a3SBruce Richardson uint8_t
7376ba1f63bSYuanhan Liu vtpci_get_status(struct virtio_hw *hw)
7386ba1f63bSYuanhan Liu {
7396ba1f63bSYuanhan Liu 	return hw->vtpci_ops->get_status(hw);
7406ba1f63bSYuanhan Liu }
7416ba1f63bSYuanhan Liu 
7426ba1f63bSYuanhan Liu uint8_t
7436c3169a3SBruce Richardson vtpci_isr(struct virtio_hw *hw)
7446c3169a3SBruce Richardson {
745d5bbeefcSYuanhan Liu 	return hw->vtpci_ops->get_isr(hw);
7466c3169a3SBruce Richardson }
7476c3169a3SBruce Richardson 
7486c3169a3SBruce Richardson 
7496c3169a3SBruce Richardson /* Enable one vector (0) for Link State Intrerrupt */
7506c3169a3SBruce Richardson uint16_t
7516c3169a3SBruce Richardson vtpci_irq_config(struct virtio_hw *hw, uint16_t vec)
7526c3169a3SBruce Richardson {
753d5bbeefcSYuanhan Liu 	return hw->vtpci_ops->set_config_irq(hw, vec);
754d5bbeefcSYuanhan Liu }
755d5bbeefcSYuanhan Liu 
7566ba1f63bSYuanhan Liu static void *
7576ba1f63bSYuanhan Liu get_cfg_addr(struct rte_pci_device *dev, struct virtio_pci_cap *cap)
7586ba1f63bSYuanhan Liu {
7596ba1f63bSYuanhan Liu 	uint8_t  bar    = cap->bar;
7606ba1f63bSYuanhan Liu 	uint32_t length = cap->length;
7616ba1f63bSYuanhan Liu 	uint32_t offset = cap->offset;
7626ba1f63bSYuanhan Liu 	uint8_t *base;
7636ba1f63bSYuanhan Liu 
7646ba1f63bSYuanhan Liu 	if (bar > 5) {
7656ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR, "invalid bar: %u", bar);
7666ba1f63bSYuanhan Liu 		return NULL;
7676ba1f63bSYuanhan Liu 	}
7686ba1f63bSYuanhan Liu 
7696ba1f63bSYuanhan Liu 	if (offset + length < offset) {
7706ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR, "offset(%u) + length(%u) overflows",
7716ba1f63bSYuanhan Liu 			offset, length);
7726ba1f63bSYuanhan Liu 		return NULL;
7736ba1f63bSYuanhan Liu 	}
7746ba1f63bSYuanhan Liu 
7756ba1f63bSYuanhan Liu 	if (offset + length > dev->mem_resource[bar].len) {
7766ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR,
7776ba1f63bSYuanhan Liu 			"invalid cap: overflows bar space: %u > %" PRIu64,
7786ba1f63bSYuanhan Liu 			offset + length, dev->mem_resource[bar].len);
7796ba1f63bSYuanhan Liu 		return NULL;
7806ba1f63bSYuanhan Liu 	}
7816ba1f63bSYuanhan Liu 
7826ba1f63bSYuanhan Liu 	base = dev->mem_resource[bar].addr;
7836ba1f63bSYuanhan Liu 	if (base == NULL) {
7846ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR, "bar %u base addr is NULL", bar);
7856ba1f63bSYuanhan Liu 		return NULL;
7866ba1f63bSYuanhan Liu 	}
7876ba1f63bSYuanhan Liu 
7886ba1f63bSYuanhan Liu 	return base + offset;
7896ba1f63bSYuanhan Liu }
7906ba1f63bSYuanhan Liu 
7916ba1f63bSYuanhan Liu static int
7926ba1f63bSYuanhan Liu virtio_read_caps(struct rte_pci_device *dev, struct virtio_hw *hw)
7936ba1f63bSYuanhan Liu {
7946ba1f63bSYuanhan Liu 	uint8_t pos;
7956ba1f63bSYuanhan Liu 	struct virtio_pci_cap cap;
7966ba1f63bSYuanhan Liu 	int ret;
7976ba1f63bSYuanhan Liu 
798*7a66c72dSDavid Marchand 	if (rte_eal_pci_map_device(dev)) {
7996ba1f63bSYuanhan Liu 		PMD_INIT_LOG(DEBUG, "failed to map pci device!");
8006ba1f63bSYuanhan Liu 		return -1;
8016ba1f63bSYuanhan Liu 	}
8026ba1f63bSYuanhan Liu 
8036ba1f63bSYuanhan Liu 	ret = rte_eal_pci_read_config(dev, &pos, 1, PCI_CAPABILITY_LIST);
8046ba1f63bSYuanhan Liu 	if (ret < 0) {
8056ba1f63bSYuanhan Liu 		PMD_INIT_LOG(DEBUG, "failed to read pci capability list");
8066ba1f63bSYuanhan Liu 		return -1;
8076ba1f63bSYuanhan Liu 	}
8086ba1f63bSYuanhan Liu 
8096ba1f63bSYuanhan Liu 	while (pos) {
8106ba1f63bSYuanhan Liu 		ret = rte_eal_pci_read_config(dev, &cap, sizeof(cap), pos);
8116ba1f63bSYuanhan Liu 		if (ret < 0) {
8126ba1f63bSYuanhan Liu 			PMD_INIT_LOG(ERR,
8136ba1f63bSYuanhan Liu 				"failed to read pci cap at pos: %x", pos);
8146ba1f63bSYuanhan Liu 			break;
8156ba1f63bSYuanhan Liu 		}
8166ba1f63bSYuanhan Liu 
8176ba1f63bSYuanhan Liu 		if (cap.cap_vndr != PCI_CAP_ID_VNDR) {
8186ba1f63bSYuanhan Liu 			PMD_INIT_LOG(DEBUG,
8196ba1f63bSYuanhan Liu 				"[%2x] skipping non VNDR cap id: %02x",
8206ba1f63bSYuanhan Liu 				pos, cap.cap_vndr);
8216ba1f63bSYuanhan Liu 			goto next;
8226ba1f63bSYuanhan Liu 		}
8236ba1f63bSYuanhan Liu 
8246ba1f63bSYuanhan Liu 		PMD_INIT_LOG(DEBUG,
8256ba1f63bSYuanhan Liu 			"[%2x] cfg type: %u, bar: %u, offset: %04x, len: %u",
8266ba1f63bSYuanhan Liu 			pos, cap.cfg_type, cap.bar, cap.offset, cap.length);
8276ba1f63bSYuanhan Liu 
8286ba1f63bSYuanhan Liu 		switch (cap.cfg_type) {
8296ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_COMMON_CFG:
8306ba1f63bSYuanhan Liu 			hw->common_cfg = get_cfg_addr(dev, &cap);
8316ba1f63bSYuanhan Liu 			break;
8326ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_NOTIFY_CFG:
8336ba1f63bSYuanhan Liu 			rte_eal_pci_read_config(dev, &hw->notify_off_multiplier,
8346ba1f63bSYuanhan Liu 						4, pos + sizeof(cap));
8356ba1f63bSYuanhan Liu 			hw->notify_base = get_cfg_addr(dev, &cap);
8366ba1f63bSYuanhan Liu 			break;
8376ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_DEVICE_CFG:
8386ba1f63bSYuanhan Liu 			hw->dev_cfg = get_cfg_addr(dev, &cap);
8396ba1f63bSYuanhan Liu 			break;
8406ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_ISR_CFG:
8416ba1f63bSYuanhan Liu 			hw->isr = get_cfg_addr(dev, &cap);
8426ba1f63bSYuanhan Liu 			break;
8436ba1f63bSYuanhan Liu 		}
8446ba1f63bSYuanhan Liu 
8456ba1f63bSYuanhan Liu next:
8466ba1f63bSYuanhan Liu 		pos = cap.cap_next;
8476ba1f63bSYuanhan Liu 	}
8486ba1f63bSYuanhan Liu 
8496ba1f63bSYuanhan Liu 	if (hw->common_cfg == NULL || hw->notify_base == NULL ||
8506ba1f63bSYuanhan Liu 	    hw->dev_cfg == NULL    || hw->isr == NULL) {
8516ba1f63bSYuanhan Liu 		PMD_INIT_LOG(INFO, "no modern virtio pci device found.");
8526ba1f63bSYuanhan Liu 		return -1;
8536ba1f63bSYuanhan Liu 	}
8546ba1f63bSYuanhan Liu 
8556ba1f63bSYuanhan Liu 	PMD_INIT_LOG(INFO, "found modern virtio pci device.");
8566ba1f63bSYuanhan Liu 
8576ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "common cfg mapped at: %p", hw->common_cfg);
8586ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "device cfg mapped at: %p", hw->dev_cfg);
8596ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "isr cfg mapped at: %p", hw->isr);
8606ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "notify base: %p, notify off multiplier: %u",
8616ba1f63bSYuanhan Liu 		hw->notify_base, hw->notify_off_multiplier);
8626ba1f63bSYuanhan Liu 
8636ba1f63bSYuanhan Liu 	return 0;
8646ba1f63bSYuanhan Liu }
8656ba1f63bSYuanhan Liu 
866d5bbeefcSYuanhan Liu int
867c52afa68SYuanhan Liu vtpci_init(struct rte_pci_device *dev, struct virtio_hw *hw)
868d5bbeefcSYuanhan Liu {
8696ba1f63bSYuanhan Liu 	hw->dev = dev;
870d5bbeefcSYuanhan Liu 
8716ba1f63bSYuanhan Liu 	/*
8726ba1f63bSYuanhan Liu 	 * Try if we can succeed reading virtio pci caps, which exists
8736ba1f63bSYuanhan Liu 	 * only on modern pci device. If failed, we fallback to legacy
8746ba1f63bSYuanhan Liu 	 * virtio handling.
8756ba1f63bSYuanhan Liu 	 */
8766ba1f63bSYuanhan Liu 	if (virtio_read_caps(dev, hw) == 0) {
8776ba1f63bSYuanhan Liu 		PMD_INIT_LOG(INFO, "modern virtio pci detected.");
8786ba1f63bSYuanhan Liu 		hw->vtpci_ops = &modern_ops;
8796ba1f63bSYuanhan Liu 		hw->modern    = 1;
8806ba1f63bSYuanhan Liu 		dev->driver->drv_flags |= RTE_PCI_DRV_INTR_LSC;
8816ba1f63bSYuanhan Liu 		return 0;
8826ba1f63bSYuanhan Liu 	}
8836ba1f63bSYuanhan Liu 
8846ba1f63bSYuanhan Liu 	PMD_INIT_LOG(INFO, "trying with legacy virtio pci.");
885c52afa68SYuanhan Liu 	if (legacy_virtio_resource_init(dev) < 0)
886c52afa68SYuanhan Liu 		return -1;
8876ba1f63bSYuanhan Liu 
8886ba1f63bSYuanhan Liu 	hw->vtpci_ops = &legacy_ops;
889c52afa68SYuanhan Liu 	hw->use_msix = legacy_virtio_has_msix(&dev->addr);
890c52afa68SYuanhan Liu 	hw->io_base  = (uint32_t)(uintptr_t)dev->mem_resource[0].addr;
8916ba1f63bSYuanhan Liu 	hw->modern   = 0;
892c52afa68SYuanhan Liu 
893d5bbeefcSYuanhan Liu 	return 0;
8946c3169a3SBruce Richardson }
895