xref: /dpdk/drivers/net/virtio/virtio_pci.c (revision 62a785a68ebf710eba22b70b46a970a4e4e1cec7)
16c3169a3SBruce Richardson /*-
26c3169a3SBruce Richardson  *   BSD LICENSE
36c3169a3SBruce Richardson  *
46c3169a3SBruce Richardson  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
56c3169a3SBruce Richardson  *   All rights reserved.
66c3169a3SBruce Richardson  *
76c3169a3SBruce Richardson  *   Redistribution and use in source and binary forms, with or without
86c3169a3SBruce Richardson  *   modification, are permitted provided that the following conditions
96c3169a3SBruce Richardson  *   are met:
106c3169a3SBruce Richardson  *
116c3169a3SBruce Richardson  *     * Redistributions of source code must retain the above copyright
126c3169a3SBruce Richardson  *       notice, this list of conditions and the following disclaimer.
136c3169a3SBruce Richardson  *     * Redistributions in binary form must reproduce the above copyright
146c3169a3SBruce Richardson  *       notice, this list of conditions and the following disclaimer in
156c3169a3SBruce Richardson  *       the documentation and/or other materials provided with the
166c3169a3SBruce Richardson  *       distribution.
176c3169a3SBruce Richardson  *     * Neither the name of Intel Corporation nor the names of its
186c3169a3SBruce Richardson  *       contributors may be used to endorse or promote products derived
196c3169a3SBruce Richardson  *       from this software without specific prior written permission.
206c3169a3SBruce Richardson  *
216c3169a3SBruce Richardson  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
226c3169a3SBruce Richardson  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
236c3169a3SBruce Richardson  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
246c3169a3SBruce Richardson  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
256c3169a3SBruce Richardson  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
266c3169a3SBruce Richardson  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
276c3169a3SBruce Richardson  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
286c3169a3SBruce Richardson  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
296c3169a3SBruce Richardson  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
306c3169a3SBruce Richardson  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
316c3169a3SBruce Richardson  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
326c3169a3SBruce Richardson  */
336c3169a3SBruce Richardson #include <stdint.h>
346c3169a3SBruce Richardson 
35c52afa68SYuanhan Liu #ifdef RTE_EXEC_ENV_LINUXAPP
36c52afa68SYuanhan Liu  #include <dirent.h>
37c52afa68SYuanhan Liu  #include <fcntl.h>
38c52afa68SYuanhan Liu #endif
39c52afa68SYuanhan Liu 
406c3169a3SBruce Richardson #include "virtio_pci.h"
416c3169a3SBruce Richardson #include "virtio_logs.h"
42d5bbeefcSYuanhan Liu #include "virtqueue.h"
436c3169a3SBruce Richardson 
446ba1f63bSYuanhan Liu /*
456ba1f63bSYuanhan Liu  * Following macros are derived from linux/pci_regs.h, however,
466ba1f63bSYuanhan Liu  * we can't simply include that header here, as there is no such
476ba1f63bSYuanhan Liu  * file for non-Linux platform.
486ba1f63bSYuanhan Liu  */
496ba1f63bSYuanhan Liu #define PCI_CAPABILITY_LIST	0x34
506ba1f63bSYuanhan Liu #define PCI_CAP_ID_VNDR		0x09
516ba1f63bSYuanhan Liu 
52b8f04520SDavid Marchand /*
53b8f04520SDavid Marchand  * The remaining space is defined by each driver as the per-driver
54b8f04520SDavid Marchand  * configuration space.
55b8f04520SDavid Marchand  */
56b8f04520SDavid Marchand #define VIRTIO_PCI_CONFIG(hw) (((hw)->use_msix) ? 24 : 20)
57b86af7b1SYuanhan Liu 
58d5bbeefcSYuanhan Liu static void
59d5bbeefcSYuanhan Liu legacy_read_dev_config(struct virtio_hw *hw, size_t offset,
606c3169a3SBruce Richardson 		       void *dst, int length)
616c3169a3SBruce Richardson {
62b8f04520SDavid Marchand 	rte_eal_pci_ioport_read(&hw->io, dst, length,
63b8f04520SDavid Marchand 				VIRTIO_PCI_CONFIG(hw) + offset);
646c3169a3SBruce Richardson }
656c3169a3SBruce Richardson 
66d5bbeefcSYuanhan Liu static void
67d5bbeefcSYuanhan Liu legacy_write_dev_config(struct virtio_hw *hw, size_t offset,
68d5bbeefcSYuanhan Liu 			const void *src, int length)
696c3169a3SBruce Richardson {
70b8f04520SDavid Marchand 	rte_eal_pci_ioport_write(&hw->io, src, length,
71b8f04520SDavid Marchand 				 VIRTIO_PCI_CONFIG(hw) + offset);
726c3169a3SBruce Richardson }
736c3169a3SBruce Richardson 
743891f233SYuanhan Liu static uint64_t
75d5bbeefcSYuanhan Liu legacy_get_features(struct virtio_hw *hw)
76d5bbeefcSYuanhan Liu {
7736ea36efSYuanhan Liu 	uint32_t dst;
78b8f04520SDavid Marchand 
79b8f04520SDavid Marchand 	rte_eal_pci_ioport_read(&hw->io, &dst, 4, VIRTIO_PCI_HOST_FEATURES);
80b8f04520SDavid Marchand 	return dst;
81d5bbeefcSYuanhan Liu }
82d5bbeefcSYuanhan Liu 
83d5bbeefcSYuanhan Liu static void
843891f233SYuanhan Liu legacy_set_features(struct virtio_hw *hw, uint64_t features)
85d5bbeefcSYuanhan Liu {
863891f233SYuanhan Liu 	if ((features >> 32) != 0) {
873891f233SYuanhan Liu 		PMD_DRV_LOG(ERR,
883891f233SYuanhan Liu 			"only 32 bit features are allowed for legacy virtio!");
893891f233SYuanhan Liu 		return;
903891f233SYuanhan Liu 	}
91b8f04520SDavid Marchand 	rte_eal_pci_ioport_write(&hw->io, &features, 4,
92b8f04520SDavid Marchand 				 VIRTIO_PCI_GUEST_FEATURES);
93d5bbeefcSYuanhan Liu }
94d5bbeefcSYuanhan Liu 
95d5bbeefcSYuanhan Liu static uint8_t
96d5bbeefcSYuanhan Liu legacy_get_status(struct virtio_hw *hw)
97d5bbeefcSYuanhan Liu {
98b8f04520SDavid Marchand 	uint8_t dst;
99b8f04520SDavid Marchand 
100b8f04520SDavid Marchand 	rte_eal_pci_ioport_read(&hw->io, &dst, 1, VIRTIO_PCI_STATUS);
101b8f04520SDavid Marchand 	return dst;
102d5bbeefcSYuanhan Liu }
103d5bbeefcSYuanhan Liu 
104d5bbeefcSYuanhan Liu static void
105d5bbeefcSYuanhan Liu legacy_set_status(struct virtio_hw *hw, uint8_t status)
106d5bbeefcSYuanhan Liu {
107b8f04520SDavid Marchand 	rte_eal_pci_ioport_write(&hw->io, &status, 1, VIRTIO_PCI_STATUS);
108d5bbeefcSYuanhan Liu }
109d5bbeefcSYuanhan Liu 
110d5bbeefcSYuanhan Liu static void
111d5bbeefcSYuanhan Liu legacy_reset(struct virtio_hw *hw)
112d5bbeefcSYuanhan Liu {
113d5bbeefcSYuanhan Liu 	legacy_set_status(hw, VIRTIO_CONFIG_STATUS_RESET);
114d5bbeefcSYuanhan Liu }
115d5bbeefcSYuanhan Liu 
116d5bbeefcSYuanhan Liu static uint8_t
117d5bbeefcSYuanhan Liu legacy_get_isr(struct virtio_hw *hw)
118d5bbeefcSYuanhan Liu {
119b8f04520SDavid Marchand 	uint8_t dst;
120b8f04520SDavid Marchand 
121b8f04520SDavid Marchand 	rte_eal_pci_ioport_read(&hw->io, &dst, 1, VIRTIO_PCI_ISR);
122b8f04520SDavid Marchand 	return dst;
123d5bbeefcSYuanhan Liu }
124d5bbeefcSYuanhan Liu 
125d5bbeefcSYuanhan Liu /* Enable one vector (0) for Link State Intrerrupt */
126d5bbeefcSYuanhan Liu static uint16_t
127d5bbeefcSYuanhan Liu legacy_set_config_irq(struct virtio_hw *hw, uint16_t vec)
128d5bbeefcSYuanhan Liu {
129b8f04520SDavid Marchand 	uint16_t dst;
130b8f04520SDavid Marchand 
131b8f04520SDavid Marchand 	rte_eal_pci_ioport_write(&hw->io, &vec, 2, VIRTIO_MSI_CONFIG_VECTOR);
132b8f04520SDavid Marchand 	rte_eal_pci_ioport_read(&hw->io, &dst, 2, VIRTIO_MSI_CONFIG_VECTOR);
133b8f04520SDavid Marchand 	return dst;
134d5bbeefcSYuanhan Liu }
135d5bbeefcSYuanhan Liu 
136d5bbeefcSYuanhan Liu static uint16_t
137d5bbeefcSYuanhan Liu legacy_get_queue_num(struct virtio_hw *hw, uint16_t queue_id)
138d5bbeefcSYuanhan Liu {
139b8f04520SDavid Marchand 	uint16_t dst;
140b8f04520SDavid Marchand 
141b8f04520SDavid Marchand 	rte_eal_pci_ioport_write(&hw->io, &queue_id, 2, VIRTIO_PCI_QUEUE_SEL);
142b8f04520SDavid Marchand 	rte_eal_pci_ioport_read(&hw->io, &dst, 2, VIRTIO_PCI_QUEUE_NUM);
143b8f04520SDavid Marchand 	return dst;
144d5bbeefcSYuanhan Liu }
145d5bbeefcSYuanhan Liu 
146d5bbeefcSYuanhan Liu static void
147d5bbeefcSYuanhan Liu legacy_setup_queue(struct virtio_hw *hw, struct virtqueue *vq)
148d5bbeefcSYuanhan Liu {
149b8f04520SDavid Marchand 	uint32_t src;
150d5bbeefcSYuanhan Liu 
151b8f04520SDavid Marchand 	rte_eal_pci_ioport_write(&hw->io, &vq->vq_queue_index, 2,
152b8f04520SDavid Marchand 			 VIRTIO_PCI_QUEUE_SEL);
153b8f04520SDavid Marchand 	src = vq->mz->phys_addr >> VIRTIO_PCI_QUEUE_ADDR_SHIFT;
154b8f04520SDavid Marchand 	rte_eal_pci_ioport_write(&hw->io, &src, 4, VIRTIO_PCI_QUEUE_PFN);
155d5bbeefcSYuanhan Liu }
156d5bbeefcSYuanhan Liu 
157d5bbeefcSYuanhan Liu static void
158d5bbeefcSYuanhan Liu legacy_del_queue(struct virtio_hw *hw, struct virtqueue *vq)
159d5bbeefcSYuanhan Liu {
160b8f04520SDavid Marchand 	uint32_t src = 0;
161d5bbeefcSYuanhan Liu 
162b8f04520SDavid Marchand 	rte_eal_pci_ioport_write(&hw->io, &vq->vq_queue_index, 2,
163b8f04520SDavid Marchand 			 VIRTIO_PCI_QUEUE_SEL);
164b8f04520SDavid Marchand 	rte_eal_pci_ioport_write(&hw->io, &src, 4, VIRTIO_PCI_QUEUE_PFN);
165d5bbeefcSYuanhan Liu }
166d5bbeefcSYuanhan Liu 
167d5bbeefcSYuanhan Liu static void
168d5bbeefcSYuanhan Liu legacy_notify_queue(struct virtio_hw *hw, struct virtqueue *vq)
169d5bbeefcSYuanhan Liu {
170b8f04520SDavid Marchand 	rte_eal_pci_ioport_write(&hw->io, &vq->vq_queue_index, 2,
171b8f04520SDavid Marchand 			 VIRTIO_PCI_QUEUE_NOTIFY);
172d5bbeefcSYuanhan Liu }
173d5bbeefcSYuanhan Liu 
174c52afa68SYuanhan Liu #ifdef RTE_EXEC_ENV_LINUXAPP
175c52afa68SYuanhan Liu static int
176c52afa68SYuanhan Liu legacy_virtio_has_msix(const struct rte_pci_addr *loc)
177c52afa68SYuanhan Liu {
178c52afa68SYuanhan Liu 	DIR *d;
179c52afa68SYuanhan Liu 	char dirname[PATH_MAX];
180c52afa68SYuanhan Liu 
181c52afa68SYuanhan Liu 	snprintf(dirname, sizeof(dirname),
182c52afa68SYuanhan Liu 		     SYSFS_PCI_DEVICES "/" PCI_PRI_FMT "/msi_irqs",
183c52afa68SYuanhan Liu 		     loc->domain, loc->bus, loc->devid, loc->function);
184c52afa68SYuanhan Liu 
185c52afa68SYuanhan Liu 	d = opendir(dirname);
186c52afa68SYuanhan Liu 	if (d)
187c52afa68SYuanhan Liu 		closedir(d);
188c52afa68SYuanhan Liu 
189693f715dSHuawei Xie 	return d != NULL;
190c52afa68SYuanhan Liu }
191c52afa68SYuanhan Liu #else
192c52afa68SYuanhan Liu static int
19325294cd3SDavid Marchand legacy_virtio_has_msix(const struct rte_pci_addr *loc __rte_unused)
194c52afa68SYuanhan Liu {
195c52afa68SYuanhan Liu 	/* nic_uio does not enable interrupts, return 0 (false). */
196c52afa68SYuanhan Liu 	return 0;
197c52afa68SYuanhan Liu }
198b8f04520SDavid Marchand #endif
199c52afa68SYuanhan Liu 
200c52afa68SYuanhan Liu static int
201b8f04520SDavid Marchand legacy_virtio_resource_init(struct rte_pci_device *pci_dev,
202*62a785a6SJianfeng Tan 			    struct virtio_hw *hw, uint32_t *dev_flags)
203c52afa68SYuanhan Liu {
204b8f04520SDavid Marchand 	if (rte_eal_pci_ioport_map(pci_dev, 0, &hw->io) < 0)
205b8f04520SDavid Marchand 		return -1;
206b8f04520SDavid Marchand 
207b8f04520SDavid Marchand 	if (pci_dev->intr_handle.type != RTE_INTR_HANDLE_UNKNOWN)
208*62a785a6SJianfeng Tan 		*dev_flags |= RTE_ETH_DEV_INTR_LSC;
209b8f04520SDavid Marchand 	else
210*62a785a6SJianfeng Tan 		*dev_flags &= ~RTE_ETH_DEV_INTR_LSC;
211b8f04520SDavid Marchand 
212c52afa68SYuanhan Liu 	return 0;
213c52afa68SYuanhan Liu }
214d5bbeefcSYuanhan Liu 
215d5bbeefcSYuanhan Liu static const struct virtio_pci_ops legacy_ops = {
216d5bbeefcSYuanhan Liu 	.read_dev_cfg	= legacy_read_dev_config,
217d5bbeefcSYuanhan Liu 	.write_dev_cfg	= legacy_write_dev_config,
218d5bbeefcSYuanhan Liu 	.reset		= legacy_reset,
219d5bbeefcSYuanhan Liu 	.get_status	= legacy_get_status,
220d5bbeefcSYuanhan Liu 	.set_status	= legacy_set_status,
221d5bbeefcSYuanhan Liu 	.get_features	= legacy_get_features,
222d5bbeefcSYuanhan Liu 	.set_features	= legacy_set_features,
223d5bbeefcSYuanhan Liu 	.get_isr	= legacy_get_isr,
224d5bbeefcSYuanhan Liu 	.set_config_irq	= legacy_set_config_irq,
225d5bbeefcSYuanhan Liu 	.get_queue_num	= legacy_get_queue_num,
226d5bbeefcSYuanhan Liu 	.setup_queue	= legacy_setup_queue,
227d5bbeefcSYuanhan Liu 	.del_queue	= legacy_del_queue,
228d5bbeefcSYuanhan Liu 	.notify_queue	= legacy_notify_queue,
229d5bbeefcSYuanhan Liu };
230d5bbeefcSYuanhan Liu 
231d5bbeefcSYuanhan Liu 
2326ba1f63bSYuanhan Liu static inline uint8_t
2336ba1f63bSYuanhan Liu io_read8(uint8_t *addr)
2346ba1f63bSYuanhan Liu {
2356ba1f63bSYuanhan Liu 	return *(volatile uint8_t *)addr;
2366ba1f63bSYuanhan Liu }
2376ba1f63bSYuanhan Liu 
2386ba1f63bSYuanhan Liu static inline void
2396ba1f63bSYuanhan Liu io_write8(uint8_t val, uint8_t *addr)
2406ba1f63bSYuanhan Liu {
2416ba1f63bSYuanhan Liu 	*(volatile uint8_t *)addr = val;
2426ba1f63bSYuanhan Liu }
2436ba1f63bSYuanhan Liu 
2446ba1f63bSYuanhan Liu static inline uint16_t
2456ba1f63bSYuanhan Liu io_read16(uint16_t *addr)
2466ba1f63bSYuanhan Liu {
2476ba1f63bSYuanhan Liu 	return *(volatile uint16_t *)addr;
2486ba1f63bSYuanhan Liu }
2496ba1f63bSYuanhan Liu 
2506ba1f63bSYuanhan Liu static inline void
2516ba1f63bSYuanhan Liu io_write16(uint16_t val, uint16_t *addr)
2526ba1f63bSYuanhan Liu {
2536ba1f63bSYuanhan Liu 	*(volatile uint16_t *)addr = val;
2546ba1f63bSYuanhan Liu }
2556ba1f63bSYuanhan Liu 
2566ba1f63bSYuanhan Liu static inline uint32_t
2576ba1f63bSYuanhan Liu io_read32(uint32_t *addr)
2586ba1f63bSYuanhan Liu {
2596ba1f63bSYuanhan Liu 	return *(volatile uint32_t *)addr;
2606ba1f63bSYuanhan Liu }
2616ba1f63bSYuanhan Liu 
2626ba1f63bSYuanhan Liu static inline void
2636ba1f63bSYuanhan Liu io_write32(uint32_t val, uint32_t *addr)
2646ba1f63bSYuanhan Liu {
2656ba1f63bSYuanhan Liu 	*(volatile uint32_t *)addr = val;
2666ba1f63bSYuanhan Liu }
2676ba1f63bSYuanhan Liu 
2686ba1f63bSYuanhan Liu static inline void
2696ba1f63bSYuanhan Liu io_write64_twopart(uint64_t val, uint32_t *lo, uint32_t *hi)
2706ba1f63bSYuanhan Liu {
2716ba1f63bSYuanhan Liu 	io_write32(val & ((1ULL << 32) - 1), lo);
2726ba1f63bSYuanhan Liu 	io_write32(val >> 32,		     hi);
2736ba1f63bSYuanhan Liu }
2746ba1f63bSYuanhan Liu 
2756ba1f63bSYuanhan Liu static void
2766ba1f63bSYuanhan Liu modern_read_dev_config(struct virtio_hw *hw, size_t offset,
2776ba1f63bSYuanhan Liu 		       void *dst, int length)
2786ba1f63bSYuanhan Liu {
2796ba1f63bSYuanhan Liu 	int i;
2806ba1f63bSYuanhan Liu 	uint8_t *p;
2816ba1f63bSYuanhan Liu 	uint8_t old_gen, new_gen;
2826ba1f63bSYuanhan Liu 
2836ba1f63bSYuanhan Liu 	do {
2846ba1f63bSYuanhan Liu 		old_gen = io_read8(&hw->common_cfg->config_generation);
2856ba1f63bSYuanhan Liu 
2866ba1f63bSYuanhan Liu 		p = dst;
2876ba1f63bSYuanhan Liu 		for (i = 0;  i < length; i++)
2886ba1f63bSYuanhan Liu 			*p++ = io_read8((uint8_t *)hw->dev_cfg + offset + i);
2896ba1f63bSYuanhan Liu 
2906ba1f63bSYuanhan Liu 		new_gen = io_read8(&hw->common_cfg->config_generation);
2916ba1f63bSYuanhan Liu 	} while (old_gen != new_gen);
2926ba1f63bSYuanhan Liu }
2936ba1f63bSYuanhan Liu 
2946ba1f63bSYuanhan Liu static void
2956ba1f63bSYuanhan Liu modern_write_dev_config(struct virtio_hw *hw, size_t offset,
2966ba1f63bSYuanhan Liu 			const void *src, int length)
2976ba1f63bSYuanhan Liu {
2986ba1f63bSYuanhan Liu 	int i;
2996ba1f63bSYuanhan Liu 	const uint8_t *p = src;
3006ba1f63bSYuanhan Liu 
3016ba1f63bSYuanhan Liu 	for (i = 0;  i < length; i++)
3026ba1f63bSYuanhan Liu 		io_write8(*p++, (uint8_t *)hw->dev_cfg + offset + i);
3036ba1f63bSYuanhan Liu }
3046ba1f63bSYuanhan Liu 
3056ba1f63bSYuanhan Liu static uint64_t
3066ba1f63bSYuanhan Liu modern_get_features(struct virtio_hw *hw)
3076ba1f63bSYuanhan Liu {
3086ba1f63bSYuanhan Liu 	uint32_t features_lo, features_hi;
3096ba1f63bSYuanhan Liu 
3106ba1f63bSYuanhan Liu 	io_write32(0, &hw->common_cfg->device_feature_select);
3116ba1f63bSYuanhan Liu 	features_lo = io_read32(&hw->common_cfg->device_feature);
3126ba1f63bSYuanhan Liu 
3136ba1f63bSYuanhan Liu 	io_write32(1, &hw->common_cfg->device_feature_select);
3146ba1f63bSYuanhan Liu 	features_hi = io_read32(&hw->common_cfg->device_feature);
3156ba1f63bSYuanhan Liu 
3166ba1f63bSYuanhan Liu 	return ((uint64_t)features_hi << 32) | features_lo;
3176ba1f63bSYuanhan Liu }
3186ba1f63bSYuanhan Liu 
3196ba1f63bSYuanhan Liu static void
3206ba1f63bSYuanhan Liu modern_set_features(struct virtio_hw *hw, uint64_t features)
3216ba1f63bSYuanhan Liu {
3226ba1f63bSYuanhan Liu 	io_write32(0, &hw->common_cfg->guest_feature_select);
3236ba1f63bSYuanhan Liu 	io_write32(features & ((1ULL << 32) - 1),
3246ba1f63bSYuanhan Liu 		&hw->common_cfg->guest_feature);
3256ba1f63bSYuanhan Liu 
3266ba1f63bSYuanhan Liu 	io_write32(1, &hw->common_cfg->guest_feature_select);
3276ba1f63bSYuanhan Liu 	io_write32(features >> 32,
3286ba1f63bSYuanhan Liu 		&hw->common_cfg->guest_feature);
3296ba1f63bSYuanhan Liu }
3306ba1f63bSYuanhan Liu 
3316ba1f63bSYuanhan Liu static uint8_t
3326ba1f63bSYuanhan Liu modern_get_status(struct virtio_hw *hw)
3336ba1f63bSYuanhan Liu {
3346ba1f63bSYuanhan Liu 	return io_read8(&hw->common_cfg->device_status);
3356ba1f63bSYuanhan Liu }
3366ba1f63bSYuanhan Liu 
3376ba1f63bSYuanhan Liu static void
3386ba1f63bSYuanhan Liu modern_set_status(struct virtio_hw *hw, uint8_t status)
3396ba1f63bSYuanhan Liu {
3406ba1f63bSYuanhan Liu 	io_write8(status, &hw->common_cfg->device_status);
3416ba1f63bSYuanhan Liu }
3426ba1f63bSYuanhan Liu 
3436ba1f63bSYuanhan Liu static void
3446ba1f63bSYuanhan Liu modern_reset(struct virtio_hw *hw)
3456ba1f63bSYuanhan Liu {
3466ba1f63bSYuanhan Liu 	modern_set_status(hw, VIRTIO_CONFIG_STATUS_RESET);
3476ba1f63bSYuanhan Liu 	modern_get_status(hw);
3486ba1f63bSYuanhan Liu }
3496ba1f63bSYuanhan Liu 
3506ba1f63bSYuanhan Liu static uint8_t
3516ba1f63bSYuanhan Liu modern_get_isr(struct virtio_hw *hw)
3526ba1f63bSYuanhan Liu {
3536ba1f63bSYuanhan Liu 	return io_read8(hw->isr);
3546ba1f63bSYuanhan Liu }
3556ba1f63bSYuanhan Liu 
3566ba1f63bSYuanhan Liu static uint16_t
3576ba1f63bSYuanhan Liu modern_set_config_irq(struct virtio_hw *hw, uint16_t vec)
3586ba1f63bSYuanhan Liu {
3596ba1f63bSYuanhan Liu 	io_write16(vec, &hw->common_cfg->msix_config);
3606ba1f63bSYuanhan Liu 	return io_read16(&hw->common_cfg->msix_config);
3616ba1f63bSYuanhan Liu }
3626ba1f63bSYuanhan Liu 
3636ba1f63bSYuanhan Liu static uint16_t
3646ba1f63bSYuanhan Liu modern_get_queue_num(struct virtio_hw *hw, uint16_t queue_id)
3656ba1f63bSYuanhan Liu {
3666ba1f63bSYuanhan Liu 	io_write16(queue_id, &hw->common_cfg->queue_select);
3676ba1f63bSYuanhan Liu 	return io_read16(&hw->common_cfg->queue_size);
3686ba1f63bSYuanhan Liu }
3696ba1f63bSYuanhan Liu 
3706ba1f63bSYuanhan Liu static void
3716ba1f63bSYuanhan Liu modern_setup_queue(struct virtio_hw *hw, struct virtqueue *vq)
3726ba1f63bSYuanhan Liu {
3736ba1f63bSYuanhan Liu 	uint64_t desc_addr, avail_addr, used_addr;
3746ba1f63bSYuanhan Liu 	uint16_t notify_off;
3756ba1f63bSYuanhan Liu 
3766ba1f63bSYuanhan Liu 	desc_addr = vq->mz->phys_addr;
3776ba1f63bSYuanhan Liu 	avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc);
3786ba1f63bSYuanhan Liu 	used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail,
3796ba1f63bSYuanhan Liu 							 ring[vq->vq_nentries]),
3806ba1f63bSYuanhan Liu 				   VIRTIO_PCI_VRING_ALIGN);
3816ba1f63bSYuanhan Liu 
3826ba1f63bSYuanhan Liu 	io_write16(vq->vq_queue_index, &hw->common_cfg->queue_select);
3836ba1f63bSYuanhan Liu 
3846ba1f63bSYuanhan Liu 	io_write64_twopart(desc_addr, &hw->common_cfg->queue_desc_lo,
3856ba1f63bSYuanhan Liu 				      &hw->common_cfg->queue_desc_hi);
3866ba1f63bSYuanhan Liu 	io_write64_twopart(avail_addr, &hw->common_cfg->queue_avail_lo,
3876ba1f63bSYuanhan Liu 				       &hw->common_cfg->queue_avail_hi);
3886ba1f63bSYuanhan Liu 	io_write64_twopart(used_addr, &hw->common_cfg->queue_used_lo,
3896ba1f63bSYuanhan Liu 				      &hw->common_cfg->queue_used_hi);
3906ba1f63bSYuanhan Liu 
3916ba1f63bSYuanhan Liu 	notify_off = io_read16(&hw->common_cfg->queue_notify_off);
3926ba1f63bSYuanhan Liu 	vq->notify_addr = (void *)((uint8_t *)hw->notify_base +
3936ba1f63bSYuanhan Liu 				notify_off * hw->notify_off_multiplier);
3946ba1f63bSYuanhan Liu 
3956ba1f63bSYuanhan Liu 	io_write16(1, &hw->common_cfg->queue_enable);
3966ba1f63bSYuanhan Liu 
3976ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "queue %u addresses:", vq->vq_queue_index);
3986ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t desc_addr: %" PRIx64, desc_addr);
3996ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t aval_addr: %" PRIx64, avail_addr);
4006ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t used_addr: %" PRIx64, used_addr);
4016ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "\t notify addr: %p (notify offset: %u)",
4026ba1f63bSYuanhan Liu 		vq->notify_addr, notify_off);
4036ba1f63bSYuanhan Liu }
4046ba1f63bSYuanhan Liu 
4056ba1f63bSYuanhan Liu static void
4066ba1f63bSYuanhan Liu modern_del_queue(struct virtio_hw *hw, struct virtqueue *vq)
4076ba1f63bSYuanhan Liu {
4086ba1f63bSYuanhan Liu 	io_write16(vq->vq_queue_index, &hw->common_cfg->queue_select);
4096ba1f63bSYuanhan Liu 
4106ba1f63bSYuanhan Liu 	io_write64_twopart(0, &hw->common_cfg->queue_desc_lo,
4116ba1f63bSYuanhan Liu 				  &hw->common_cfg->queue_desc_hi);
4126ba1f63bSYuanhan Liu 	io_write64_twopart(0, &hw->common_cfg->queue_avail_lo,
4136ba1f63bSYuanhan Liu 				  &hw->common_cfg->queue_avail_hi);
4146ba1f63bSYuanhan Liu 	io_write64_twopart(0, &hw->common_cfg->queue_used_lo,
4156ba1f63bSYuanhan Liu 				  &hw->common_cfg->queue_used_hi);
4166ba1f63bSYuanhan Liu 
4176ba1f63bSYuanhan Liu 	io_write16(0, &hw->common_cfg->queue_enable);
4186ba1f63bSYuanhan Liu }
4196ba1f63bSYuanhan Liu 
4206ba1f63bSYuanhan Liu static void
4216ba1f63bSYuanhan Liu modern_notify_queue(struct virtio_hw *hw __rte_unused, struct virtqueue *vq)
4226ba1f63bSYuanhan Liu {
4236ba1f63bSYuanhan Liu 	io_write16(1, vq->notify_addr);
4246ba1f63bSYuanhan Liu }
4256ba1f63bSYuanhan Liu 
4266ba1f63bSYuanhan Liu static const struct virtio_pci_ops modern_ops = {
4276ba1f63bSYuanhan Liu 	.read_dev_cfg	= modern_read_dev_config,
4286ba1f63bSYuanhan Liu 	.write_dev_cfg	= modern_write_dev_config,
4296ba1f63bSYuanhan Liu 	.reset		= modern_reset,
4306ba1f63bSYuanhan Liu 	.get_status	= modern_get_status,
4316ba1f63bSYuanhan Liu 	.set_status	= modern_set_status,
4326ba1f63bSYuanhan Liu 	.get_features	= modern_get_features,
4336ba1f63bSYuanhan Liu 	.set_features	= modern_set_features,
4346ba1f63bSYuanhan Liu 	.get_isr	= modern_get_isr,
4356ba1f63bSYuanhan Liu 	.set_config_irq	= modern_set_config_irq,
4366ba1f63bSYuanhan Liu 	.get_queue_num	= modern_get_queue_num,
4376ba1f63bSYuanhan Liu 	.setup_queue	= modern_setup_queue,
4386ba1f63bSYuanhan Liu 	.del_queue	= modern_del_queue,
4396ba1f63bSYuanhan Liu 	.notify_queue	= modern_notify_queue,
4406ba1f63bSYuanhan Liu };
4416ba1f63bSYuanhan Liu 
4426ba1f63bSYuanhan Liu 
443d5bbeefcSYuanhan Liu void
444d5bbeefcSYuanhan Liu vtpci_read_dev_config(struct virtio_hw *hw, size_t offset,
445d5bbeefcSYuanhan Liu 		      void *dst, int length)
446d5bbeefcSYuanhan Liu {
447d5bbeefcSYuanhan Liu 	hw->vtpci_ops->read_dev_cfg(hw, offset, dst, length);
448d5bbeefcSYuanhan Liu }
449d5bbeefcSYuanhan Liu 
450d5bbeefcSYuanhan Liu void
451d5bbeefcSYuanhan Liu vtpci_write_dev_config(struct virtio_hw *hw, size_t offset,
452d5bbeefcSYuanhan Liu 		       const void *src, int length)
453d5bbeefcSYuanhan Liu {
454d5bbeefcSYuanhan Liu 	hw->vtpci_ops->write_dev_cfg(hw, offset, src, length);
455d5bbeefcSYuanhan Liu }
456d5bbeefcSYuanhan Liu 
4573891f233SYuanhan Liu uint64_t
4583891f233SYuanhan Liu vtpci_negotiate_features(struct virtio_hw *hw, uint64_t host_features)
4596c3169a3SBruce Richardson {
4603891f233SYuanhan Liu 	uint64_t features;
461d5bbeefcSYuanhan Liu 
4626c3169a3SBruce Richardson 	/*
4636c3169a3SBruce Richardson 	 * Limit negotiated features to what the driver, virtqueue, and
4646c3169a3SBruce Richardson 	 * host all support.
4656c3169a3SBruce Richardson 	 */
4666c3169a3SBruce Richardson 	features = host_features & hw->guest_features;
467d5bbeefcSYuanhan Liu 	hw->vtpci_ops->set_features(hw, features);
4686c3169a3SBruce Richardson 
4696c3169a3SBruce Richardson 	return features;
4706c3169a3SBruce Richardson }
4716c3169a3SBruce Richardson 
4726c3169a3SBruce Richardson void
4736c3169a3SBruce Richardson vtpci_reset(struct virtio_hw *hw)
4746c3169a3SBruce Richardson {
475d5bbeefcSYuanhan Liu 	hw->vtpci_ops->set_status(hw, VIRTIO_CONFIG_STATUS_RESET);
476d5bbeefcSYuanhan Liu 	/* flush status write */
477d5bbeefcSYuanhan Liu 	hw->vtpci_ops->get_status(hw);
4786c3169a3SBruce Richardson }
4796c3169a3SBruce Richardson 
4806c3169a3SBruce Richardson void
4816c3169a3SBruce Richardson vtpci_reinit_complete(struct virtio_hw *hw)
4826c3169a3SBruce Richardson {
4836c3169a3SBruce Richardson 	vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK);
4846c3169a3SBruce Richardson }
4856c3169a3SBruce Richardson 
4866c3169a3SBruce Richardson void
4876c3169a3SBruce Richardson vtpci_set_status(struct virtio_hw *hw, uint8_t status)
4886c3169a3SBruce Richardson {
4896c3169a3SBruce Richardson 	if (status != VIRTIO_CONFIG_STATUS_RESET)
490d5bbeefcSYuanhan Liu 		status |= hw->vtpci_ops->get_status(hw);
4916c3169a3SBruce Richardson 
492d5bbeefcSYuanhan Liu 	hw->vtpci_ops->set_status(hw, status);
4936c3169a3SBruce Richardson }
4946c3169a3SBruce Richardson 
4956c3169a3SBruce Richardson uint8_t
4966ba1f63bSYuanhan Liu vtpci_get_status(struct virtio_hw *hw)
4976ba1f63bSYuanhan Liu {
4986ba1f63bSYuanhan Liu 	return hw->vtpci_ops->get_status(hw);
4996ba1f63bSYuanhan Liu }
5006ba1f63bSYuanhan Liu 
5016ba1f63bSYuanhan Liu uint8_t
5026c3169a3SBruce Richardson vtpci_isr(struct virtio_hw *hw)
5036c3169a3SBruce Richardson {
504d5bbeefcSYuanhan Liu 	return hw->vtpci_ops->get_isr(hw);
5056c3169a3SBruce Richardson }
5066c3169a3SBruce Richardson 
5076c3169a3SBruce Richardson 
5086c3169a3SBruce Richardson /* Enable one vector (0) for Link State Intrerrupt */
5096c3169a3SBruce Richardson uint16_t
5106c3169a3SBruce Richardson vtpci_irq_config(struct virtio_hw *hw, uint16_t vec)
5116c3169a3SBruce Richardson {
512d5bbeefcSYuanhan Liu 	return hw->vtpci_ops->set_config_irq(hw, vec);
513d5bbeefcSYuanhan Liu }
514d5bbeefcSYuanhan Liu 
5156ba1f63bSYuanhan Liu static void *
5166ba1f63bSYuanhan Liu get_cfg_addr(struct rte_pci_device *dev, struct virtio_pci_cap *cap)
5176ba1f63bSYuanhan Liu {
5186ba1f63bSYuanhan Liu 	uint8_t  bar    = cap->bar;
5196ba1f63bSYuanhan Liu 	uint32_t length = cap->length;
5206ba1f63bSYuanhan Liu 	uint32_t offset = cap->offset;
5216ba1f63bSYuanhan Liu 	uint8_t *base;
5226ba1f63bSYuanhan Liu 
5236ba1f63bSYuanhan Liu 	if (bar > 5) {
5246ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR, "invalid bar: %u", bar);
5256ba1f63bSYuanhan Liu 		return NULL;
5266ba1f63bSYuanhan Liu 	}
5276ba1f63bSYuanhan Liu 
5286ba1f63bSYuanhan Liu 	if (offset + length < offset) {
5296ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR, "offset(%u) + length(%u) overflows",
5306ba1f63bSYuanhan Liu 			offset, length);
5316ba1f63bSYuanhan Liu 		return NULL;
5326ba1f63bSYuanhan Liu 	}
5336ba1f63bSYuanhan Liu 
5346ba1f63bSYuanhan Liu 	if (offset + length > dev->mem_resource[bar].len) {
5356ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR,
5366ba1f63bSYuanhan Liu 			"invalid cap: overflows bar space: %u > %" PRIu64,
5376ba1f63bSYuanhan Liu 			offset + length, dev->mem_resource[bar].len);
5386ba1f63bSYuanhan Liu 		return NULL;
5396ba1f63bSYuanhan Liu 	}
5406ba1f63bSYuanhan Liu 
5416ba1f63bSYuanhan Liu 	base = dev->mem_resource[bar].addr;
5426ba1f63bSYuanhan Liu 	if (base == NULL) {
5436ba1f63bSYuanhan Liu 		PMD_INIT_LOG(ERR, "bar %u base addr is NULL", bar);
5446ba1f63bSYuanhan Liu 		return NULL;
5456ba1f63bSYuanhan Liu 	}
5466ba1f63bSYuanhan Liu 
5476ba1f63bSYuanhan Liu 	return base + offset;
5486ba1f63bSYuanhan Liu }
5496ba1f63bSYuanhan Liu 
5506ba1f63bSYuanhan Liu static int
5516ba1f63bSYuanhan Liu virtio_read_caps(struct rte_pci_device *dev, struct virtio_hw *hw)
5526ba1f63bSYuanhan Liu {
5536ba1f63bSYuanhan Liu 	uint8_t pos;
5546ba1f63bSYuanhan Liu 	struct virtio_pci_cap cap;
5556ba1f63bSYuanhan Liu 	int ret;
5566ba1f63bSYuanhan Liu 
5577a66c72dSDavid Marchand 	if (rte_eal_pci_map_device(dev)) {
5586ba1f63bSYuanhan Liu 		PMD_INIT_LOG(DEBUG, "failed to map pci device!");
5596ba1f63bSYuanhan Liu 		return -1;
5606ba1f63bSYuanhan Liu 	}
5616ba1f63bSYuanhan Liu 
5626ba1f63bSYuanhan Liu 	ret = rte_eal_pci_read_config(dev, &pos, 1, PCI_CAPABILITY_LIST);
5636ba1f63bSYuanhan Liu 	if (ret < 0) {
5646ba1f63bSYuanhan Liu 		PMD_INIT_LOG(DEBUG, "failed to read pci capability list");
5656ba1f63bSYuanhan Liu 		return -1;
5666ba1f63bSYuanhan Liu 	}
5676ba1f63bSYuanhan Liu 
5686ba1f63bSYuanhan Liu 	while (pos) {
5696ba1f63bSYuanhan Liu 		ret = rte_eal_pci_read_config(dev, &cap, sizeof(cap), pos);
5706ba1f63bSYuanhan Liu 		if (ret < 0) {
5716ba1f63bSYuanhan Liu 			PMD_INIT_LOG(ERR,
5726ba1f63bSYuanhan Liu 				"failed to read pci cap at pos: %x", pos);
5736ba1f63bSYuanhan Liu 			break;
5746ba1f63bSYuanhan Liu 		}
5756ba1f63bSYuanhan Liu 
5766ba1f63bSYuanhan Liu 		if (cap.cap_vndr != PCI_CAP_ID_VNDR) {
5776ba1f63bSYuanhan Liu 			PMD_INIT_LOG(DEBUG,
5786ba1f63bSYuanhan Liu 				"[%2x] skipping non VNDR cap id: %02x",
5796ba1f63bSYuanhan Liu 				pos, cap.cap_vndr);
5806ba1f63bSYuanhan Liu 			goto next;
5816ba1f63bSYuanhan Liu 		}
5826ba1f63bSYuanhan Liu 
5836ba1f63bSYuanhan Liu 		PMD_INIT_LOG(DEBUG,
5846ba1f63bSYuanhan Liu 			"[%2x] cfg type: %u, bar: %u, offset: %04x, len: %u",
5856ba1f63bSYuanhan Liu 			pos, cap.cfg_type, cap.bar, cap.offset, cap.length);
5866ba1f63bSYuanhan Liu 
5876ba1f63bSYuanhan Liu 		switch (cap.cfg_type) {
5886ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_COMMON_CFG:
5896ba1f63bSYuanhan Liu 			hw->common_cfg = get_cfg_addr(dev, &cap);
5906ba1f63bSYuanhan Liu 			break;
5916ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_NOTIFY_CFG:
5926ba1f63bSYuanhan Liu 			rte_eal_pci_read_config(dev, &hw->notify_off_multiplier,
5936ba1f63bSYuanhan Liu 						4, pos + sizeof(cap));
5946ba1f63bSYuanhan Liu 			hw->notify_base = get_cfg_addr(dev, &cap);
5956ba1f63bSYuanhan Liu 			break;
5966ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_DEVICE_CFG:
5976ba1f63bSYuanhan Liu 			hw->dev_cfg = get_cfg_addr(dev, &cap);
5986ba1f63bSYuanhan Liu 			break;
5996ba1f63bSYuanhan Liu 		case VIRTIO_PCI_CAP_ISR_CFG:
6006ba1f63bSYuanhan Liu 			hw->isr = get_cfg_addr(dev, &cap);
6016ba1f63bSYuanhan Liu 			break;
6026ba1f63bSYuanhan Liu 		}
6036ba1f63bSYuanhan Liu 
6046ba1f63bSYuanhan Liu next:
6056ba1f63bSYuanhan Liu 		pos = cap.cap_next;
6066ba1f63bSYuanhan Liu 	}
6076ba1f63bSYuanhan Liu 
6086ba1f63bSYuanhan Liu 	if (hw->common_cfg == NULL || hw->notify_base == NULL ||
6096ba1f63bSYuanhan Liu 	    hw->dev_cfg == NULL    || hw->isr == NULL) {
6106ba1f63bSYuanhan Liu 		PMD_INIT_LOG(INFO, "no modern virtio pci device found.");
6116ba1f63bSYuanhan Liu 		return -1;
6126ba1f63bSYuanhan Liu 	}
6136ba1f63bSYuanhan Liu 
6146ba1f63bSYuanhan Liu 	PMD_INIT_LOG(INFO, "found modern virtio pci device.");
6156ba1f63bSYuanhan Liu 
6166ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "common cfg mapped at: %p", hw->common_cfg);
6176ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "device cfg mapped at: %p", hw->dev_cfg);
6186ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "isr cfg mapped at: %p", hw->isr);
6196ba1f63bSYuanhan Liu 	PMD_INIT_LOG(DEBUG, "notify base: %p, notify off multiplier: %u",
6206ba1f63bSYuanhan Liu 		hw->notify_base, hw->notify_off_multiplier);
6216ba1f63bSYuanhan Liu 
6226ba1f63bSYuanhan Liu 	return 0;
6236ba1f63bSYuanhan Liu }
6246ba1f63bSYuanhan Liu 
625ac5e1d83SHuawei Xie /*
626ac5e1d83SHuawei Xie  * Return -1:
627ac5e1d83SHuawei Xie  *   if there is error mapping with VFIO/UIO.
628ac5e1d83SHuawei Xie  *   if port map error when driver type is KDRV_NONE.
629ac5e1d83SHuawei Xie  * Return 1 if kernel driver is managing the device.
630ac5e1d83SHuawei Xie  * Return 0 on success.
631ac5e1d83SHuawei Xie  */
632d5bbeefcSYuanhan Liu int
633*62a785a6SJianfeng Tan vtpci_init(struct rte_pci_device *dev, struct virtio_hw *hw,
634*62a785a6SJianfeng Tan 	   uint32_t *dev_flags)
635d5bbeefcSYuanhan Liu {
6366ba1f63bSYuanhan Liu 	hw->dev = dev;
637d5bbeefcSYuanhan Liu 
6386ba1f63bSYuanhan Liu 	/*
6396ba1f63bSYuanhan Liu 	 * Try if we can succeed reading virtio pci caps, which exists
6406ba1f63bSYuanhan Liu 	 * only on modern pci device. If failed, we fallback to legacy
6416ba1f63bSYuanhan Liu 	 * virtio handling.
6426ba1f63bSYuanhan Liu 	 */
6436ba1f63bSYuanhan Liu 	if (virtio_read_caps(dev, hw) == 0) {
6446ba1f63bSYuanhan Liu 		PMD_INIT_LOG(INFO, "modern virtio pci detected.");
6456ba1f63bSYuanhan Liu 		hw->vtpci_ops = &modern_ops;
6466ba1f63bSYuanhan Liu 		hw->modern    = 1;
647*62a785a6SJianfeng Tan 		*dev_flags |= RTE_ETH_DEV_INTR_LSC;
6486ba1f63bSYuanhan Liu 		return 0;
6496ba1f63bSYuanhan Liu 	}
6506ba1f63bSYuanhan Liu 
6516ba1f63bSYuanhan Liu 	PMD_INIT_LOG(INFO, "trying with legacy virtio pci.");
652*62a785a6SJianfeng Tan 	if (legacy_virtio_resource_init(dev, hw, dev_flags) < 0) {
653ac5e1d83SHuawei Xie 		if (dev->kdrv == RTE_KDRV_UNKNOWN &&
654ac5e1d83SHuawei Xie 		    dev->devargs->type != RTE_DEVTYPE_WHITELISTED_PCI) {
655ac5e1d83SHuawei Xie 			PMD_INIT_LOG(INFO,
656ac5e1d83SHuawei Xie 				"skip kernel managed virtio device.");
657ac5e1d83SHuawei Xie 			return 1;
658ac5e1d83SHuawei Xie 		}
659c52afa68SYuanhan Liu 		return -1;
660ac5e1d83SHuawei Xie 	}
6616ba1f63bSYuanhan Liu 
6626ba1f63bSYuanhan Liu 	hw->vtpci_ops = &legacy_ops;
663c52afa68SYuanhan Liu 	hw->use_msix = legacy_virtio_has_msix(&dev->addr);
6646ba1f63bSYuanhan Liu 	hw->modern   = 0;
665c52afa68SYuanhan Liu 
666d5bbeefcSYuanhan Liu 	return 0;
6676c3169a3SBruce Richardson }
668