xref: /dpdk/drivers/net/virtio/virtio_pci.c (revision c52afa68d763c58facfbf5a80f452d5cac077125)
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 
35*c52afa68SYuanhan Liu #ifdef RTE_EXEC_ENV_LINUXAPP
36*c52afa68SYuanhan Liu  #include <dirent.h>
37*c52afa68SYuanhan Liu  #include <fcntl.h>
38*c52afa68SYuanhan Liu #endif
39*c52afa68SYuanhan Liu 
406c3169a3SBruce Richardson #include "virtio_pci.h"
416c3169a3SBruce Richardson #include "virtio_logs.h"
42d5bbeefcSYuanhan Liu #include "virtqueue.h"
436c3169a3SBruce Richardson 
44d5bbeefcSYuanhan Liu static void
45d5bbeefcSYuanhan Liu legacy_read_dev_config(struct virtio_hw *hw, size_t offset,
466c3169a3SBruce Richardson 		       void *dst, int length)
476c3169a3SBruce Richardson {
486c3169a3SBruce Richardson 	uint64_t off;
496c3169a3SBruce Richardson 	uint8_t *d;
506c3169a3SBruce Richardson 	int size;
516c3169a3SBruce Richardson 
526c3169a3SBruce Richardson 	off = VIRTIO_PCI_CONFIG(hw) + offset;
536c3169a3SBruce Richardson 	for (d = dst; length > 0; d += size, off += size, length -= size) {
546c3169a3SBruce Richardson 		if (length >= 4) {
556c3169a3SBruce Richardson 			size = 4;
566c3169a3SBruce Richardson 			*(uint32_t *)d = VIRTIO_READ_REG_4(hw, off);
576c3169a3SBruce Richardson 		} else if (length >= 2) {
586c3169a3SBruce Richardson 			size = 2;
596c3169a3SBruce Richardson 			*(uint16_t *)d = VIRTIO_READ_REG_2(hw, off);
606c3169a3SBruce Richardson 		} else {
616c3169a3SBruce Richardson 			size = 1;
626c3169a3SBruce Richardson 			*d = VIRTIO_READ_REG_1(hw, off);
636c3169a3SBruce Richardson 		}
646c3169a3SBruce Richardson 	}
656c3169a3SBruce Richardson }
666c3169a3SBruce Richardson 
67d5bbeefcSYuanhan Liu static void
68d5bbeefcSYuanhan Liu legacy_write_dev_config(struct virtio_hw *hw, size_t offset,
69d5bbeefcSYuanhan Liu 			const void *src, int length)
706c3169a3SBruce Richardson {
716c3169a3SBruce Richardson 	uint64_t off;
72d5bbeefcSYuanhan Liu 	const uint8_t *s;
736c3169a3SBruce Richardson 	int size;
746c3169a3SBruce Richardson 
756c3169a3SBruce Richardson 	off = VIRTIO_PCI_CONFIG(hw) + offset;
766c3169a3SBruce Richardson 	for (s = src; length > 0; s += size, off += size, length -= size) {
776c3169a3SBruce Richardson 		if (length >= 4) {
786c3169a3SBruce Richardson 			size = 4;
79d5bbeefcSYuanhan Liu 			VIRTIO_WRITE_REG_4(hw, off, *(const uint32_t *)s);
806c3169a3SBruce Richardson 		} else if (length >= 2) {
816c3169a3SBruce Richardson 			size = 2;
82d5bbeefcSYuanhan Liu 			VIRTIO_WRITE_REG_2(hw, off, *(const uint16_t *)s);
836c3169a3SBruce Richardson 		} else {
846c3169a3SBruce Richardson 			size = 1;
856c3169a3SBruce Richardson 			VIRTIO_WRITE_REG_1(hw, off, *s);
866c3169a3SBruce Richardson 		}
876c3169a3SBruce Richardson 	}
886c3169a3SBruce Richardson }
896c3169a3SBruce Richardson 
90d5bbeefcSYuanhan Liu static uint32_t
91d5bbeefcSYuanhan Liu legacy_get_features(struct virtio_hw *hw)
92d5bbeefcSYuanhan Liu {
93d5bbeefcSYuanhan Liu 	return VIRTIO_READ_REG_4(hw, VIRTIO_PCI_HOST_FEATURES);
94d5bbeefcSYuanhan Liu }
95d5bbeefcSYuanhan Liu 
96d5bbeefcSYuanhan Liu static void
97d5bbeefcSYuanhan Liu legacy_set_features(struct virtio_hw *hw, uint32_t features)
98d5bbeefcSYuanhan Liu {
99d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_4(hw, VIRTIO_PCI_GUEST_FEATURES, features);
100d5bbeefcSYuanhan Liu }
101d5bbeefcSYuanhan Liu 
102d5bbeefcSYuanhan Liu static uint8_t
103d5bbeefcSYuanhan Liu legacy_get_status(struct virtio_hw *hw)
104d5bbeefcSYuanhan Liu {
105d5bbeefcSYuanhan Liu 	return VIRTIO_READ_REG_1(hw, VIRTIO_PCI_STATUS);
106d5bbeefcSYuanhan Liu }
107d5bbeefcSYuanhan Liu 
108d5bbeefcSYuanhan Liu static void
109d5bbeefcSYuanhan Liu legacy_set_status(struct virtio_hw *hw, uint8_t status)
110d5bbeefcSYuanhan Liu {
111d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_1(hw, VIRTIO_PCI_STATUS, status);
112d5bbeefcSYuanhan Liu }
113d5bbeefcSYuanhan Liu 
114d5bbeefcSYuanhan Liu static void
115d5bbeefcSYuanhan Liu legacy_reset(struct virtio_hw *hw)
116d5bbeefcSYuanhan Liu {
117d5bbeefcSYuanhan Liu 	legacy_set_status(hw, VIRTIO_CONFIG_STATUS_RESET);
118d5bbeefcSYuanhan Liu }
119d5bbeefcSYuanhan Liu 
120d5bbeefcSYuanhan Liu static uint8_t
121d5bbeefcSYuanhan Liu legacy_get_isr(struct virtio_hw *hw)
122d5bbeefcSYuanhan Liu {
123d5bbeefcSYuanhan Liu 	return VIRTIO_READ_REG_1(hw, VIRTIO_PCI_ISR);
124d5bbeefcSYuanhan Liu }
125d5bbeefcSYuanhan Liu 
126d5bbeefcSYuanhan Liu /* Enable one vector (0) for Link State Intrerrupt */
127d5bbeefcSYuanhan Liu static uint16_t
128d5bbeefcSYuanhan Liu legacy_set_config_irq(struct virtio_hw *hw, uint16_t vec)
129d5bbeefcSYuanhan Liu {
130d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_2(hw, VIRTIO_MSI_CONFIG_VECTOR, vec);
131d5bbeefcSYuanhan Liu 	return VIRTIO_READ_REG_2(hw, VIRTIO_MSI_CONFIG_VECTOR);
132d5bbeefcSYuanhan Liu }
133d5bbeefcSYuanhan Liu 
134d5bbeefcSYuanhan Liu static uint16_t
135d5bbeefcSYuanhan Liu legacy_get_queue_num(struct virtio_hw *hw, uint16_t queue_id)
136d5bbeefcSYuanhan Liu {
137d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_SEL, queue_id);
138d5bbeefcSYuanhan Liu 	return VIRTIO_READ_REG_2(hw, VIRTIO_PCI_QUEUE_NUM);
139d5bbeefcSYuanhan Liu }
140d5bbeefcSYuanhan Liu 
141d5bbeefcSYuanhan Liu static void
142d5bbeefcSYuanhan Liu legacy_setup_queue(struct virtio_hw *hw, struct virtqueue *vq)
143d5bbeefcSYuanhan Liu {
144d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_SEL, vq->vq_queue_index);
145d5bbeefcSYuanhan Liu 
146d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_4(hw, VIRTIO_PCI_QUEUE_PFN,
147d5bbeefcSYuanhan Liu 		vq->mz->phys_addr >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
148d5bbeefcSYuanhan Liu }
149d5bbeefcSYuanhan Liu 
150d5bbeefcSYuanhan Liu static void
151d5bbeefcSYuanhan Liu legacy_del_queue(struct virtio_hw *hw, struct virtqueue *vq)
152d5bbeefcSYuanhan Liu {
153d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_SEL, vq->vq_queue_index);
154d5bbeefcSYuanhan Liu 
155d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_4(hw, VIRTIO_PCI_QUEUE_PFN, 0);
156d5bbeefcSYuanhan Liu }
157d5bbeefcSYuanhan Liu 
158d5bbeefcSYuanhan Liu static void
159d5bbeefcSYuanhan Liu legacy_notify_queue(struct virtio_hw *hw, struct virtqueue *vq)
160d5bbeefcSYuanhan Liu {
161d5bbeefcSYuanhan Liu 	VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_NOTIFY, vq->vq_queue_index);
162d5bbeefcSYuanhan Liu }
163d5bbeefcSYuanhan Liu 
164*c52afa68SYuanhan Liu #ifdef RTE_EXEC_ENV_LINUXAPP
165*c52afa68SYuanhan Liu static int
166*c52afa68SYuanhan Liu parse_sysfs_value(const char *filename, unsigned long *val)
167*c52afa68SYuanhan Liu {
168*c52afa68SYuanhan Liu 	FILE *f;
169*c52afa68SYuanhan Liu 	char buf[BUFSIZ];
170*c52afa68SYuanhan Liu 	char *end = NULL;
171*c52afa68SYuanhan Liu 
172*c52afa68SYuanhan Liu 	f = fopen(filename, "r");
173*c52afa68SYuanhan Liu 	if (f == NULL) {
174*c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "%s(): cannot open sysfs value %s",
175*c52afa68SYuanhan Liu 			     __func__, filename);
176*c52afa68SYuanhan Liu 		return -1;
177*c52afa68SYuanhan Liu 	}
178*c52afa68SYuanhan Liu 
179*c52afa68SYuanhan Liu 	if (fgets(buf, sizeof(buf), f) == NULL) {
180*c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "%s(): cannot read sysfs value %s",
181*c52afa68SYuanhan Liu 			     __func__, filename);
182*c52afa68SYuanhan Liu 		fclose(f);
183*c52afa68SYuanhan Liu 		return -1;
184*c52afa68SYuanhan Liu 	}
185*c52afa68SYuanhan Liu 	*val = strtoul(buf, &end, 0);
186*c52afa68SYuanhan Liu 	if ((buf[0] == '\0') || (end == NULL) || (*end != '\n')) {
187*c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "%s(): cannot parse sysfs value %s",
188*c52afa68SYuanhan Liu 			     __func__, filename);
189*c52afa68SYuanhan Liu 		fclose(f);
190*c52afa68SYuanhan Liu 		return -1;
191*c52afa68SYuanhan Liu 	}
192*c52afa68SYuanhan Liu 	fclose(f);
193*c52afa68SYuanhan Liu 	return 0;
194*c52afa68SYuanhan Liu }
195*c52afa68SYuanhan Liu 
196*c52afa68SYuanhan Liu static int
197*c52afa68SYuanhan Liu get_uio_dev(struct rte_pci_addr *loc, char *buf, unsigned int buflen,
198*c52afa68SYuanhan Liu 			unsigned int *uio_num)
199*c52afa68SYuanhan Liu {
200*c52afa68SYuanhan Liu 	struct dirent *e;
201*c52afa68SYuanhan Liu 	DIR *dir;
202*c52afa68SYuanhan Liu 	char dirname[PATH_MAX];
203*c52afa68SYuanhan Liu 
204*c52afa68SYuanhan Liu 	/*
205*c52afa68SYuanhan Liu 	 * depending on kernel version, uio can be located in uio/uioX
206*c52afa68SYuanhan Liu 	 * or uio:uioX
207*c52afa68SYuanhan Liu 	 */
208*c52afa68SYuanhan Liu 	snprintf(dirname, sizeof(dirname),
209*c52afa68SYuanhan Liu 		     SYSFS_PCI_DEVICES "/" PCI_PRI_FMT "/uio",
210*c52afa68SYuanhan Liu 		     loc->domain, loc->bus, loc->devid, loc->function);
211*c52afa68SYuanhan Liu 	dir = opendir(dirname);
212*c52afa68SYuanhan Liu 	if (dir == NULL) {
213*c52afa68SYuanhan Liu 		/* retry with the parent directory */
214*c52afa68SYuanhan Liu 		snprintf(dirname, sizeof(dirname),
215*c52afa68SYuanhan Liu 			     SYSFS_PCI_DEVICES "/" PCI_PRI_FMT,
216*c52afa68SYuanhan Liu 			     loc->domain, loc->bus, loc->devid, loc->function);
217*c52afa68SYuanhan Liu 		dir = opendir(dirname);
218*c52afa68SYuanhan Liu 
219*c52afa68SYuanhan Liu 		if (dir == NULL) {
220*c52afa68SYuanhan Liu 			PMD_INIT_LOG(ERR, "Cannot opendir %s", dirname);
221*c52afa68SYuanhan Liu 			return -1;
222*c52afa68SYuanhan Liu 		}
223*c52afa68SYuanhan Liu 	}
224*c52afa68SYuanhan Liu 
225*c52afa68SYuanhan Liu 	/* take the first file starting with "uio" */
226*c52afa68SYuanhan Liu 	while ((e = readdir(dir)) != NULL) {
227*c52afa68SYuanhan Liu 		/* format could be uio%d ...*/
228*c52afa68SYuanhan Liu 		int shortprefix_len = sizeof("uio") - 1;
229*c52afa68SYuanhan Liu 		/* ... or uio:uio%d */
230*c52afa68SYuanhan Liu 		int longprefix_len = sizeof("uio:uio") - 1;
231*c52afa68SYuanhan Liu 		char *endptr;
232*c52afa68SYuanhan Liu 
233*c52afa68SYuanhan Liu 		if (strncmp(e->d_name, "uio", 3) != 0)
234*c52afa68SYuanhan Liu 			continue;
235*c52afa68SYuanhan Liu 
236*c52afa68SYuanhan Liu 		/* first try uio%d */
237*c52afa68SYuanhan Liu 		errno = 0;
238*c52afa68SYuanhan Liu 		*uio_num = strtoull(e->d_name + shortprefix_len, &endptr, 10);
239*c52afa68SYuanhan Liu 		if (errno == 0 && endptr != (e->d_name + shortprefix_len)) {
240*c52afa68SYuanhan Liu 			snprintf(buf, buflen, "%s/uio%u", dirname, *uio_num);
241*c52afa68SYuanhan Liu 			break;
242*c52afa68SYuanhan Liu 		}
243*c52afa68SYuanhan Liu 
244*c52afa68SYuanhan Liu 		/* then try uio:uio%d */
245*c52afa68SYuanhan Liu 		errno = 0;
246*c52afa68SYuanhan Liu 		*uio_num = strtoull(e->d_name + longprefix_len, &endptr, 10);
247*c52afa68SYuanhan Liu 		if (errno == 0 && endptr != (e->d_name + longprefix_len)) {
248*c52afa68SYuanhan Liu 			snprintf(buf, buflen, "%s/uio:uio%u", dirname,
249*c52afa68SYuanhan Liu 				     *uio_num);
250*c52afa68SYuanhan Liu 			break;
251*c52afa68SYuanhan Liu 		}
252*c52afa68SYuanhan Liu 	}
253*c52afa68SYuanhan Liu 	closedir(dir);
254*c52afa68SYuanhan Liu 
255*c52afa68SYuanhan Liu 	/* No uio resource found */
256*c52afa68SYuanhan Liu 	if (e == NULL) {
257*c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "Could not find uio resource");
258*c52afa68SYuanhan Liu 		return -1;
259*c52afa68SYuanhan Liu 	}
260*c52afa68SYuanhan Liu 
261*c52afa68SYuanhan Liu 	return 0;
262*c52afa68SYuanhan Liu }
263*c52afa68SYuanhan Liu 
264*c52afa68SYuanhan Liu static int
265*c52afa68SYuanhan Liu legacy_virtio_has_msix(const struct rte_pci_addr *loc)
266*c52afa68SYuanhan Liu {
267*c52afa68SYuanhan Liu 	DIR *d;
268*c52afa68SYuanhan Liu 	char dirname[PATH_MAX];
269*c52afa68SYuanhan Liu 
270*c52afa68SYuanhan Liu 	snprintf(dirname, sizeof(dirname),
271*c52afa68SYuanhan Liu 		     SYSFS_PCI_DEVICES "/" PCI_PRI_FMT "/msi_irqs",
272*c52afa68SYuanhan Liu 		     loc->domain, loc->bus, loc->devid, loc->function);
273*c52afa68SYuanhan Liu 
274*c52afa68SYuanhan Liu 	d = opendir(dirname);
275*c52afa68SYuanhan Liu 	if (d)
276*c52afa68SYuanhan Liu 		closedir(d);
277*c52afa68SYuanhan Liu 
278*c52afa68SYuanhan Liu 	return (d != NULL);
279*c52afa68SYuanhan Liu }
280*c52afa68SYuanhan Liu 
281*c52afa68SYuanhan Liu /* Extract I/O port numbers from sysfs */
282*c52afa68SYuanhan Liu static int
283*c52afa68SYuanhan Liu virtio_resource_init_by_uio(struct rte_pci_device *pci_dev)
284*c52afa68SYuanhan Liu {
285*c52afa68SYuanhan Liu 	char dirname[PATH_MAX];
286*c52afa68SYuanhan Liu 	char filename[PATH_MAX];
287*c52afa68SYuanhan Liu 	unsigned long start, size;
288*c52afa68SYuanhan Liu 	unsigned int uio_num;
289*c52afa68SYuanhan Liu 
290*c52afa68SYuanhan Liu 	if (get_uio_dev(&pci_dev->addr, dirname, sizeof(dirname), &uio_num) < 0)
291*c52afa68SYuanhan Liu 		return -1;
292*c52afa68SYuanhan Liu 
293*c52afa68SYuanhan Liu 	/* get portio size */
294*c52afa68SYuanhan Liu 	snprintf(filename, sizeof(filename),
295*c52afa68SYuanhan Liu 		     "%s/portio/port0/size", dirname);
296*c52afa68SYuanhan Liu 	if (parse_sysfs_value(filename, &size) < 0) {
297*c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "%s(): cannot parse size",
298*c52afa68SYuanhan Liu 			     __func__);
299*c52afa68SYuanhan Liu 		return -1;
300*c52afa68SYuanhan Liu 	}
301*c52afa68SYuanhan Liu 
302*c52afa68SYuanhan Liu 	/* get portio start */
303*c52afa68SYuanhan Liu 	snprintf(filename, sizeof(filename),
304*c52afa68SYuanhan Liu 		 "%s/portio/port0/start", dirname);
305*c52afa68SYuanhan Liu 	if (parse_sysfs_value(filename, &start) < 0) {
306*c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "%s(): cannot parse portio start",
307*c52afa68SYuanhan Liu 			     __func__);
308*c52afa68SYuanhan Liu 		return -1;
309*c52afa68SYuanhan Liu 	}
310*c52afa68SYuanhan Liu 	pci_dev->mem_resource[0].addr = (void *)(uintptr_t)start;
311*c52afa68SYuanhan Liu 	pci_dev->mem_resource[0].len =  (uint64_t)size;
312*c52afa68SYuanhan Liu 	PMD_INIT_LOG(DEBUG,
313*c52afa68SYuanhan Liu 		     "PCI Port IO found start=0x%lx with size=0x%lx",
314*c52afa68SYuanhan Liu 		     start, size);
315*c52afa68SYuanhan Liu 
316*c52afa68SYuanhan Liu 	/* save fd */
317*c52afa68SYuanhan Liu 	memset(dirname, 0, sizeof(dirname));
318*c52afa68SYuanhan Liu 	snprintf(dirname, sizeof(dirname), "/dev/uio%u", uio_num);
319*c52afa68SYuanhan Liu 	pci_dev->intr_handle.fd = open(dirname, O_RDWR);
320*c52afa68SYuanhan Liu 	if (pci_dev->intr_handle.fd < 0) {
321*c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "Cannot open %s: %s\n",
322*c52afa68SYuanhan Liu 			dirname, strerror(errno));
323*c52afa68SYuanhan Liu 		return -1;
324*c52afa68SYuanhan Liu 	}
325*c52afa68SYuanhan Liu 
326*c52afa68SYuanhan Liu 	pci_dev->intr_handle.type = RTE_INTR_HANDLE_UIO;
327*c52afa68SYuanhan Liu 	pci_dev->driver->drv_flags |= RTE_PCI_DRV_INTR_LSC;
328*c52afa68SYuanhan Liu 
329*c52afa68SYuanhan Liu 	return 0;
330*c52afa68SYuanhan Liu }
331*c52afa68SYuanhan Liu 
332*c52afa68SYuanhan Liu /* Extract port I/O numbers from proc/ioports */
333*c52afa68SYuanhan Liu static int
334*c52afa68SYuanhan Liu virtio_resource_init_by_ioports(struct rte_pci_device *pci_dev)
335*c52afa68SYuanhan Liu {
336*c52afa68SYuanhan Liu 	uint16_t start, end;
337*c52afa68SYuanhan Liu 	int size;
338*c52afa68SYuanhan Liu 	FILE *fp;
339*c52afa68SYuanhan Liu 	char *line = NULL;
340*c52afa68SYuanhan Liu 	char pci_id[16];
341*c52afa68SYuanhan Liu 	int found = 0;
342*c52afa68SYuanhan Liu 	size_t linesz;
343*c52afa68SYuanhan Liu 
344*c52afa68SYuanhan Liu 	snprintf(pci_id, sizeof(pci_id), PCI_PRI_FMT,
345*c52afa68SYuanhan Liu 		 pci_dev->addr.domain,
346*c52afa68SYuanhan Liu 		 pci_dev->addr.bus,
347*c52afa68SYuanhan Liu 		 pci_dev->addr.devid,
348*c52afa68SYuanhan Liu 		 pci_dev->addr.function);
349*c52afa68SYuanhan Liu 
350*c52afa68SYuanhan Liu 	fp = fopen("/proc/ioports", "r");
351*c52afa68SYuanhan Liu 	if (fp == NULL) {
352*c52afa68SYuanhan Liu 		PMD_INIT_LOG(ERR, "%s(): can't open ioports", __func__);
353*c52afa68SYuanhan Liu 		return -1;
354*c52afa68SYuanhan Liu 	}
355*c52afa68SYuanhan Liu 
356*c52afa68SYuanhan Liu 	while (getdelim(&line, &linesz, '\n', fp) > 0) {
357*c52afa68SYuanhan Liu 		char *ptr = line;
358*c52afa68SYuanhan Liu 		char *left;
359*c52afa68SYuanhan Liu 		int n;
360*c52afa68SYuanhan Liu 
361*c52afa68SYuanhan Liu 		n = strcspn(ptr, ":");
362*c52afa68SYuanhan Liu 		ptr[n] = 0;
363*c52afa68SYuanhan Liu 		left = &ptr[n + 1];
364*c52afa68SYuanhan Liu 
365*c52afa68SYuanhan Liu 		while (*left && isspace(*left))
366*c52afa68SYuanhan Liu 			left++;
367*c52afa68SYuanhan Liu 
368*c52afa68SYuanhan Liu 		if (!strncmp(left, pci_id, strlen(pci_id))) {
369*c52afa68SYuanhan Liu 			found = 1;
370*c52afa68SYuanhan Liu 
371*c52afa68SYuanhan Liu 			while (*ptr && isspace(*ptr))
372*c52afa68SYuanhan Liu 				ptr++;
373*c52afa68SYuanhan Liu 
374*c52afa68SYuanhan Liu 			sscanf(ptr, "%04hx-%04hx", &start, &end);
375*c52afa68SYuanhan Liu 			size = end - start + 1;
376*c52afa68SYuanhan Liu 
377*c52afa68SYuanhan Liu 			break;
378*c52afa68SYuanhan Liu 		}
379*c52afa68SYuanhan Liu 	}
380*c52afa68SYuanhan Liu 
381*c52afa68SYuanhan Liu 	free(line);
382*c52afa68SYuanhan Liu 	fclose(fp);
383*c52afa68SYuanhan Liu 
384*c52afa68SYuanhan Liu 	if (!found)
385*c52afa68SYuanhan Liu 		return -1;
386*c52afa68SYuanhan Liu 
387*c52afa68SYuanhan Liu 	pci_dev->mem_resource[0].addr = (void *)(uintptr_t)(uint32_t)start;
388*c52afa68SYuanhan Liu 	pci_dev->mem_resource[0].len =  (uint64_t)size;
389*c52afa68SYuanhan Liu 	PMD_INIT_LOG(DEBUG,
390*c52afa68SYuanhan Liu 		"PCI Port IO found start=0x%x with size=0x%x",
391*c52afa68SYuanhan Liu 		start, size);
392*c52afa68SYuanhan Liu 
393*c52afa68SYuanhan Liu 	/* can't support lsc interrupt without uio */
394*c52afa68SYuanhan Liu 	pci_dev->driver->drv_flags &= ~RTE_PCI_DRV_INTR_LSC;
395*c52afa68SYuanhan Liu 
396*c52afa68SYuanhan Liu 	return 0;
397*c52afa68SYuanhan Liu }
398*c52afa68SYuanhan Liu 
399*c52afa68SYuanhan Liu /* Extract I/O port numbers from sysfs */
400*c52afa68SYuanhan Liu static int
401*c52afa68SYuanhan Liu legacy_virtio_resource_init(struct rte_pci_device *pci_dev)
402*c52afa68SYuanhan Liu {
403*c52afa68SYuanhan Liu 	if (virtio_resource_init_by_uio(pci_dev) == 0)
404*c52afa68SYuanhan Liu 		return 0;
405*c52afa68SYuanhan Liu 	else
406*c52afa68SYuanhan Liu 		return virtio_resource_init_by_ioports(pci_dev);
407*c52afa68SYuanhan Liu }
408*c52afa68SYuanhan Liu 
409*c52afa68SYuanhan Liu #else
410*c52afa68SYuanhan Liu static int
411*c52afa68SYuanhan Liu legayc_virtio_has_msix(const struct rte_pci_addr *loc __rte_unused)
412*c52afa68SYuanhan Liu {
413*c52afa68SYuanhan Liu 	/* nic_uio does not enable interrupts, return 0 (false). */
414*c52afa68SYuanhan Liu 	return 0;
415*c52afa68SYuanhan Liu }
416*c52afa68SYuanhan Liu 
417*c52afa68SYuanhan Liu static int
418*c52afa68SYuanhan Liu legacy_virtio_resource_init(struct rte_pci_device *pci_dev __rte_unused)
419*c52afa68SYuanhan Liu {
420*c52afa68SYuanhan Liu 	/* no setup required */
421*c52afa68SYuanhan Liu 	return 0;
422*c52afa68SYuanhan Liu }
423*c52afa68SYuanhan Liu #endif
424d5bbeefcSYuanhan Liu 
425d5bbeefcSYuanhan Liu static const struct virtio_pci_ops legacy_ops = {
426d5bbeefcSYuanhan Liu 	.read_dev_cfg	= legacy_read_dev_config,
427d5bbeefcSYuanhan Liu 	.write_dev_cfg	= legacy_write_dev_config,
428d5bbeefcSYuanhan Liu 	.reset		= legacy_reset,
429d5bbeefcSYuanhan Liu 	.get_status	= legacy_get_status,
430d5bbeefcSYuanhan Liu 	.set_status	= legacy_set_status,
431d5bbeefcSYuanhan Liu 	.get_features	= legacy_get_features,
432d5bbeefcSYuanhan Liu 	.set_features	= legacy_set_features,
433d5bbeefcSYuanhan Liu 	.get_isr	= legacy_get_isr,
434d5bbeefcSYuanhan Liu 	.set_config_irq	= legacy_set_config_irq,
435d5bbeefcSYuanhan Liu 	.get_queue_num	= legacy_get_queue_num,
436d5bbeefcSYuanhan Liu 	.setup_queue	= legacy_setup_queue,
437d5bbeefcSYuanhan Liu 	.del_queue	= legacy_del_queue,
438d5bbeefcSYuanhan Liu 	.notify_queue	= legacy_notify_queue,
439d5bbeefcSYuanhan Liu };
440d5bbeefcSYuanhan Liu 
441d5bbeefcSYuanhan Liu 
442d5bbeefcSYuanhan Liu void
443d5bbeefcSYuanhan Liu vtpci_read_dev_config(struct virtio_hw *hw, size_t offset,
444d5bbeefcSYuanhan Liu 		      void *dst, int length)
445d5bbeefcSYuanhan Liu {
446d5bbeefcSYuanhan Liu 	hw->vtpci_ops->read_dev_cfg(hw, offset, dst, length);
447d5bbeefcSYuanhan Liu }
448d5bbeefcSYuanhan Liu 
449d5bbeefcSYuanhan Liu void
450d5bbeefcSYuanhan Liu vtpci_write_dev_config(struct virtio_hw *hw, size_t offset,
451d5bbeefcSYuanhan Liu 		       const void *src, int length)
452d5bbeefcSYuanhan Liu {
453d5bbeefcSYuanhan Liu 	hw->vtpci_ops->write_dev_cfg(hw, offset, src, length);
454d5bbeefcSYuanhan Liu }
455d5bbeefcSYuanhan Liu 
4566c3169a3SBruce Richardson uint32_t
4576c3169a3SBruce Richardson vtpci_negotiate_features(struct virtio_hw *hw, uint32_t host_features)
4586c3169a3SBruce Richardson {
4596c3169a3SBruce Richardson 	uint32_t features;
460d5bbeefcSYuanhan Liu 
4616c3169a3SBruce Richardson 	/*
4626c3169a3SBruce Richardson 	 * Limit negotiated features to what the driver, virtqueue, and
4636c3169a3SBruce Richardson 	 * host all support.
4646c3169a3SBruce Richardson 	 */
4656c3169a3SBruce Richardson 	features = host_features & hw->guest_features;
466d5bbeefcSYuanhan Liu 	hw->vtpci_ops->set_features(hw, features);
4676c3169a3SBruce Richardson 
4686c3169a3SBruce Richardson 	return features;
4696c3169a3SBruce Richardson }
4706c3169a3SBruce Richardson 
4716c3169a3SBruce Richardson void
4726c3169a3SBruce Richardson vtpci_reset(struct virtio_hw *hw)
4736c3169a3SBruce Richardson {
474d5bbeefcSYuanhan Liu 	hw->vtpci_ops->set_status(hw, VIRTIO_CONFIG_STATUS_RESET);
475d5bbeefcSYuanhan Liu 	/* flush status write */
476d5bbeefcSYuanhan Liu 	hw->vtpci_ops->get_status(hw);
4776c3169a3SBruce Richardson }
4786c3169a3SBruce Richardson 
4796c3169a3SBruce Richardson void
4806c3169a3SBruce Richardson vtpci_reinit_complete(struct virtio_hw *hw)
4816c3169a3SBruce Richardson {
4826c3169a3SBruce Richardson 	vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK);
4836c3169a3SBruce Richardson }
4846c3169a3SBruce Richardson 
4856c3169a3SBruce Richardson void
4866c3169a3SBruce Richardson vtpci_set_status(struct virtio_hw *hw, uint8_t status)
4876c3169a3SBruce Richardson {
4886c3169a3SBruce Richardson 	if (status != VIRTIO_CONFIG_STATUS_RESET)
489d5bbeefcSYuanhan Liu 		status |= hw->vtpci_ops->get_status(hw);
4906c3169a3SBruce Richardson 
491d5bbeefcSYuanhan Liu 	hw->vtpci_ops->set_status(hw, status);
4926c3169a3SBruce Richardson }
4936c3169a3SBruce Richardson 
4946c3169a3SBruce Richardson uint8_t
4956c3169a3SBruce Richardson vtpci_isr(struct virtio_hw *hw)
4966c3169a3SBruce Richardson {
497d5bbeefcSYuanhan Liu 	return hw->vtpci_ops->get_isr(hw);
4986c3169a3SBruce Richardson }
4996c3169a3SBruce Richardson 
5006c3169a3SBruce Richardson 
5016c3169a3SBruce Richardson /* Enable one vector (0) for Link State Intrerrupt */
5026c3169a3SBruce Richardson uint16_t
5036c3169a3SBruce Richardson vtpci_irq_config(struct virtio_hw *hw, uint16_t vec)
5046c3169a3SBruce Richardson {
505d5bbeefcSYuanhan Liu 	return hw->vtpci_ops->set_config_irq(hw, vec);
506d5bbeefcSYuanhan Liu }
507d5bbeefcSYuanhan Liu 
508d5bbeefcSYuanhan Liu int
509*c52afa68SYuanhan Liu vtpci_init(struct rte_pci_device *dev, struct virtio_hw *hw)
510d5bbeefcSYuanhan Liu {
511d5bbeefcSYuanhan Liu 	hw->vtpci_ops = &legacy_ops;
512d5bbeefcSYuanhan Liu 
513*c52afa68SYuanhan Liu 	if (legacy_virtio_resource_init(dev) < 0)
514*c52afa68SYuanhan Liu 		return -1;
515*c52afa68SYuanhan Liu 	hw->use_msix = legacy_virtio_has_msix(&dev->addr);
516*c52afa68SYuanhan Liu 	hw->io_base  = (uint32_t)(uintptr_t)dev->mem_resource[0].addr;
517*c52afa68SYuanhan Liu 
518d5bbeefcSYuanhan Liu 	return 0;
5196c3169a3SBruce Richardson }
520