xref: /dpdk/drivers/net/virtio/virtio_pci_ethdev.c (revision 1c1b35b59b4cee8836f34498b7c55b49de39d7b3)
136a7a2e7SMaxime Coquelin /* SPDX-License-Identifier: BSD-3-Clause
236a7a2e7SMaxime Coquelin  * Copyright(c) 2010-2016 Intel Corporation
336a7a2e7SMaxime Coquelin  */
436a7a2e7SMaxime Coquelin 
536a7a2e7SMaxime Coquelin #include <stdint.h>
636a7a2e7SMaxime Coquelin #include <string.h>
736a7a2e7SMaxime Coquelin #include <stdio.h>
836a7a2e7SMaxime Coquelin #include <errno.h>
936a7a2e7SMaxime Coquelin #include <unistd.h>
1036a7a2e7SMaxime Coquelin 
1136a7a2e7SMaxime Coquelin #include <ethdev_driver.h>
1236a7a2e7SMaxime Coquelin #include <ethdev_pci.h>
1336a7a2e7SMaxime Coquelin #include <rte_pci.h>
141f37cb2bSDavid Marchand #include <bus_pci_driver.h>
1536a7a2e7SMaxime Coquelin #include <rte_errno.h>
1636a7a2e7SMaxime Coquelin 
1736a7a2e7SMaxime Coquelin #include <rte_memory.h>
1836a7a2e7SMaxime Coquelin #include <rte_eal.h>
191acb7f54SDavid Marchand #include <dev_driver.h>
2036a7a2e7SMaxime Coquelin #include <rte_kvargs.h>
2136a7a2e7SMaxime Coquelin 
22f8b60756SMaxime Coquelin #include "virtio.h"
2336a7a2e7SMaxime Coquelin #include "virtio_ethdev.h"
2436a7a2e7SMaxime Coquelin #include "virtio_pci.h"
2536a7a2e7SMaxime Coquelin #include "virtio_logs.h"
2636a7a2e7SMaxime Coquelin 
2736a7a2e7SMaxime Coquelin /*
2836a7a2e7SMaxime Coquelin  * The set of PCI devices this driver supports
2936a7a2e7SMaxime Coquelin  */
3036a7a2e7SMaxime Coquelin static const struct rte_pci_id pci_id_virtio_map[] = {
3136a7a2e7SMaxime Coquelin 	{ RTE_PCI_DEVICE(VIRTIO_PCI_VENDORID, VIRTIO_PCI_LEGACY_DEVICEID_NET) },
3236a7a2e7SMaxime Coquelin 	{ RTE_PCI_DEVICE(VIRTIO_PCI_VENDORID, VIRTIO_PCI_MODERN_DEVICEID_NET) },
3336a7a2e7SMaxime Coquelin 	{ .vendor_id = 0, /* sentinel */ },
3436a7a2e7SMaxime Coquelin };
3536a7a2e7SMaxime Coquelin 
36512e27eeSMaxime Coquelin 
37512e27eeSMaxime Coquelin /*
38512e27eeSMaxime Coquelin  * Remap the PCI device again (IO port map for legacy device and
39512e27eeSMaxime Coquelin  * memory map for modern device), so that the secondary process
40512e27eeSMaxime Coquelin  * could have the PCI initiated correctly.
41512e27eeSMaxime Coquelin  */
42512e27eeSMaxime Coquelin static int
virtio_remap_pci(struct rte_pci_device * pci_dev,struct virtio_pci_dev * dev)431ac79346SMaxime Coquelin virtio_remap_pci(struct rte_pci_device *pci_dev, struct virtio_pci_dev *dev)
44512e27eeSMaxime Coquelin {
451ac79346SMaxime Coquelin 	struct virtio_hw *hw = &dev->hw;
461ac79346SMaxime Coquelin 
471ac79346SMaxime Coquelin 	if (dev->modern) {
48512e27eeSMaxime Coquelin 		/*
49512e27eeSMaxime Coquelin 		 * We don't have to re-parse the PCI config space, since
50512e27eeSMaxime Coquelin 		 * rte_pci_map_device() makes sure the mapped address
51512e27eeSMaxime Coquelin 		 * in secondary process would equal to the one mapped in
52512e27eeSMaxime Coquelin 		 * the primary process: error will be returned if that
53512e27eeSMaxime Coquelin 		 * requirement is not met.
54512e27eeSMaxime Coquelin 		 *
55512e27eeSMaxime Coquelin 		 * That said, we could simply reuse all cap pointers
56512e27eeSMaxime Coquelin 		 * (such as dev_cfg, common_cfg, etc.) parsed from the
57512e27eeSMaxime Coquelin 		 * primary process, which is stored in shared memory.
58512e27eeSMaxime Coquelin 		 */
59512e27eeSMaxime Coquelin 		if (rte_pci_map_device(pci_dev)) {
60512e27eeSMaxime Coquelin 			PMD_INIT_LOG(DEBUG, "failed to map pci device!");
61512e27eeSMaxime Coquelin 			return -1;
62512e27eeSMaxime Coquelin 		}
631ac79346SMaxime Coquelin 	} else {
64c8d4b02fSMaxime Coquelin 		if (vtpci_legacy_ioport_map(hw) < 0)
65512e27eeSMaxime Coquelin 			return -1;
66512e27eeSMaxime Coquelin 	}
67512e27eeSMaxime Coquelin 
68512e27eeSMaxime Coquelin 	return 0;
69512e27eeSMaxime Coquelin }
70512e27eeSMaxime Coquelin 
7136a7a2e7SMaxime Coquelin static int
eth_virtio_pci_init(struct rte_eth_dev * eth_dev)7236a7a2e7SMaxime Coquelin eth_virtio_pci_init(struct rte_eth_dev *eth_dev)
7336a7a2e7SMaxime Coquelin {
74512e27eeSMaxime Coquelin 	struct virtio_pci_dev *dev = eth_dev->data->dev_private;
75512e27eeSMaxime Coquelin 	struct virtio_hw *hw = &dev->hw;
76512e27eeSMaxime Coquelin 	struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(eth_dev);
77512e27eeSMaxime Coquelin 	int ret;
78512e27eeSMaxime Coquelin 
79512e27eeSMaxime Coquelin 	if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
80b93c3530SMaxime Coquelin 		hw->port_id = eth_dev->data->port_id;
81aa0d4b8aSMaxime Coquelin 		VTPCI_DEV(hw) = pci_dev;
821ac79346SMaxime Coquelin 		ret = vtpci_init(RTE_ETH_DEV_TO_PCI(eth_dev), dev);
83512e27eeSMaxime Coquelin 		if (ret) {
84f3854ebaSThomas Monjalon 			PMD_INIT_LOG(ERR, "Failed to init PCI device");
85512e27eeSMaxime Coquelin 			return -1;
86512e27eeSMaxime Coquelin 		}
87512e27eeSMaxime Coquelin 	} else {
88aa0d4b8aSMaxime Coquelin 		VTPCI_DEV(hw) = pci_dev;
89c4fa9cc7SMaxime Coquelin 		if (dev->modern)
90f8b60756SMaxime Coquelin 			VIRTIO_OPS(hw) = &modern_ops;
91c4fa9cc7SMaxime Coquelin 		else
92f8b60756SMaxime Coquelin 			VIRTIO_OPS(hw) = &legacy_ops;
93c4fa9cc7SMaxime Coquelin 
941ac79346SMaxime Coquelin 		ret = virtio_remap_pci(RTE_ETH_DEV_TO_PCI(eth_dev), dev);
95512e27eeSMaxime Coquelin 		if (ret < 0) {
96f3854ebaSThomas Monjalon 			PMD_INIT_LOG(ERR, "Failed to remap PCI device");
97512e27eeSMaxime Coquelin 			return -1;
98512e27eeSMaxime Coquelin 		}
99512e27eeSMaxime Coquelin 	}
100512e27eeSMaxime Coquelin 
101512e27eeSMaxime Coquelin 	ret = eth_virtio_dev_init(eth_dev);
102512e27eeSMaxime Coquelin 	if (ret < 0) {
103f3854ebaSThomas Monjalon 		PMD_INIT_LOG(ERR, "Failed to init virtio device");
104512e27eeSMaxime Coquelin 		goto err_unmap;
105512e27eeSMaxime Coquelin 	}
106512e27eeSMaxime Coquelin 
107512e27eeSMaxime Coquelin 	PMD_INIT_LOG(DEBUG, "port %d vendorID=0x%x deviceID=0x%x",
108512e27eeSMaxime Coquelin 		eth_dev->data->port_id, pci_dev->id.vendor_id,
109512e27eeSMaxime Coquelin 		pci_dev->id.device_id);
110512e27eeSMaxime Coquelin 
111512e27eeSMaxime Coquelin 	return 0;
112512e27eeSMaxime Coquelin 
113512e27eeSMaxime Coquelin err_unmap:
114512e27eeSMaxime Coquelin 	rte_pci_unmap_device(RTE_ETH_DEV_TO_PCI(eth_dev));
115c4fa9cc7SMaxime Coquelin 	if (!dev->modern)
116c8d4b02fSMaxime Coquelin 		vtpci_legacy_ioport_unmap(hw);
117512e27eeSMaxime Coquelin 
118512e27eeSMaxime Coquelin 	return ret;
11936a7a2e7SMaxime Coquelin }
12036a7a2e7SMaxime Coquelin 
12136a7a2e7SMaxime Coquelin static int
eth_virtio_pci_uninit(struct rte_eth_dev * eth_dev)12236a7a2e7SMaxime Coquelin eth_virtio_pci_uninit(struct rte_eth_dev *eth_dev)
12336a7a2e7SMaxime Coquelin {
12436a7a2e7SMaxime Coquelin 	int ret;
1255729407fSYuan Wang 	struct virtio_pci_dev *dev;
1265729407fSYuan Wang 	struct virtio_hw *hw;
12736a7a2e7SMaxime Coquelin 	PMD_INIT_FUNC_TRACE();
12836a7a2e7SMaxime Coquelin 
1295729407fSYuan Wang 	if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
1305729407fSYuan Wang 		dev = eth_dev->data->dev_private;
1315729407fSYuan Wang 		hw = &dev->hw;
1325729407fSYuan Wang 
1335729407fSYuan Wang 		if (dev->modern)
1345729407fSYuan Wang 			rte_pci_unmap_device(RTE_ETH_DEV_TO_PCI(eth_dev));
1355729407fSYuan Wang 		else
1365729407fSYuan Wang 			vtpci_legacy_ioport_unmap(hw);
13736a7a2e7SMaxime Coquelin 		return 0;
1385729407fSYuan Wang 	}
13936a7a2e7SMaxime Coquelin 
14036a7a2e7SMaxime Coquelin 	ret = virtio_dev_stop(eth_dev);
14136a7a2e7SMaxime Coquelin 	virtio_dev_close(eth_dev);
14236a7a2e7SMaxime Coquelin 
14336a7a2e7SMaxime Coquelin 	PMD_INIT_LOG(DEBUG, "dev_uninit completed");
14436a7a2e7SMaxime Coquelin 
14536a7a2e7SMaxime Coquelin 	return ret;
14636a7a2e7SMaxime Coquelin }
14736a7a2e7SMaxime Coquelin 
vdpa_check_handler(__rte_unused const char * key,const char * value,void * ret_val)14836a7a2e7SMaxime Coquelin static int vdpa_check_handler(__rte_unused const char *key,
14936a7a2e7SMaxime Coquelin 		const char *value, void *ret_val)
15036a7a2e7SMaxime Coquelin {
151*1c1b35b5SChengwen Feng 	if (value == NULL || ret_val == NULL)
152*1c1b35b5SChengwen Feng 		return -EINVAL;
153*1c1b35b5SChengwen Feng 
15436a7a2e7SMaxime Coquelin 	if (strcmp(value, "1") == 0)
15536a7a2e7SMaxime Coquelin 		*(int *)ret_val = 1;
15636a7a2e7SMaxime Coquelin 	else
15736a7a2e7SMaxime Coquelin 		*(int *)ret_val = 0;
15836a7a2e7SMaxime Coquelin 
15936a7a2e7SMaxime Coquelin 	return 0;
16036a7a2e7SMaxime Coquelin }
16136a7a2e7SMaxime Coquelin 
16236a7a2e7SMaxime Coquelin #define VIRTIO_ARG_VDPA       "vdpa"
16336a7a2e7SMaxime Coquelin 
16436a7a2e7SMaxime Coquelin static int
virtio_pci_devargs_parse(struct rte_devargs * devargs,int * vdpa)16536a7a2e7SMaxime Coquelin virtio_pci_devargs_parse(struct rte_devargs *devargs, int *vdpa)
16636a7a2e7SMaxime Coquelin {
16736a7a2e7SMaxime Coquelin 	struct rte_kvargs *kvlist;
16836a7a2e7SMaxime Coquelin 	int ret = 0;
16936a7a2e7SMaxime Coquelin 
17036a7a2e7SMaxime Coquelin 	if (devargs == NULL)
17136a7a2e7SMaxime Coquelin 		return 0;
17236a7a2e7SMaxime Coquelin 
17336a7a2e7SMaxime Coquelin 	kvlist = rte_kvargs_parse(devargs->args, NULL);
17436a7a2e7SMaxime Coquelin 	if (kvlist == NULL) {
17536a7a2e7SMaxime Coquelin 		PMD_INIT_LOG(ERR, "error when parsing param");
17636a7a2e7SMaxime Coquelin 		return 0;
17736a7a2e7SMaxime Coquelin 	}
17836a7a2e7SMaxime Coquelin 
17936a7a2e7SMaxime Coquelin 	if (rte_kvargs_count(kvlist, VIRTIO_ARG_VDPA) == 1) {
18036a7a2e7SMaxime Coquelin 		/* vdpa mode selected when there's a key-value pair:
18136a7a2e7SMaxime Coquelin 		 * vdpa=1
18236a7a2e7SMaxime Coquelin 		 */
18336a7a2e7SMaxime Coquelin 		ret = rte_kvargs_process(kvlist, VIRTIO_ARG_VDPA,
18436a7a2e7SMaxime Coquelin 				vdpa_check_handler, vdpa);
18536a7a2e7SMaxime Coquelin 		if (ret < 0)
18636a7a2e7SMaxime Coquelin 			PMD_INIT_LOG(ERR, "Failed to parse %s", VIRTIO_ARG_VDPA);
18736a7a2e7SMaxime Coquelin 	}
18836a7a2e7SMaxime Coquelin 
18936a7a2e7SMaxime Coquelin 	rte_kvargs_free(kvlist);
19036a7a2e7SMaxime Coquelin 
19136a7a2e7SMaxime Coquelin 	return ret;
19236a7a2e7SMaxime Coquelin }
19336a7a2e7SMaxime Coquelin 
eth_virtio_pci_probe(struct rte_pci_driver * pci_drv __rte_unused,struct rte_pci_device * pci_dev)19436a7a2e7SMaxime Coquelin static int eth_virtio_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
19536a7a2e7SMaxime Coquelin 	struct rte_pci_device *pci_dev)
19636a7a2e7SMaxime Coquelin {
19736a7a2e7SMaxime Coquelin 	int vdpa = 0;
19836a7a2e7SMaxime Coquelin 	int ret = 0;
19936a7a2e7SMaxime Coquelin 
20036a7a2e7SMaxime Coquelin 	ret = virtio_pci_devargs_parse(pci_dev->device.devargs, &vdpa);
20136a7a2e7SMaxime Coquelin 	if (ret < 0) {
20236a7a2e7SMaxime Coquelin 		PMD_INIT_LOG(ERR, "devargs parsing is failed");
20336a7a2e7SMaxime Coquelin 		return ret;
20436a7a2e7SMaxime Coquelin 	}
20536a7a2e7SMaxime Coquelin 	/* virtio pmd skips probe if device needs to work in vdpa mode */
20636a7a2e7SMaxime Coquelin 	if (vdpa == 1)
20736a7a2e7SMaxime Coquelin 		return 1;
20836a7a2e7SMaxime Coquelin 
20936a7a2e7SMaxime Coquelin 	return rte_eth_dev_pci_generic_probe(pci_dev, sizeof(struct virtio_pci_dev),
21036a7a2e7SMaxime Coquelin 		eth_virtio_pci_init);
21136a7a2e7SMaxime Coquelin }
21236a7a2e7SMaxime Coquelin 
eth_virtio_pci_remove(struct rte_pci_device * pci_dev)21336a7a2e7SMaxime Coquelin static int eth_virtio_pci_remove(struct rte_pci_device *pci_dev)
21436a7a2e7SMaxime Coquelin {
21536a7a2e7SMaxime Coquelin 	int ret;
21636a7a2e7SMaxime Coquelin 
21736a7a2e7SMaxime Coquelin 	ret = rte_eth_dev_pci_generic_remove(pci_dev, eth_virtio_pci_uninit);
21836a7a2e7SMaxime Coquelin 	/* Port has already been released by close. */
21936a7a2e7SMaxime Coquelin 	if (ret == -ENODEV)
22036a7a2e7SMaxime Coquelin 		ret = 0;
22136a7a2e7SMaxime Coquelin 	return ret;
22236a7a2e7SMaxime Coquelin }
22336a7a2e7SMaxime Coquelin 
22436a7a2e7SMaxime Coquelin static struct rte_pci_driver rte_virtio_net_pci_pmd = {
22536a7a2e7SMaxime Coquelin 	.driver = {
22636a7a2e7SMaxime Coquelin 		.name = "net_virtio",
22736a7a2e7SMaxime Coquelin 	},
22836a7a2e7SMaxime Coquelin 	.id_table = pci_id_virtio_map,
22936a7a2e7SMaxime Coquelin 	.drv_flags = 0,
23036a7a2e7SMaxime Coquelin 	.probe = eth_virtio_pci_probe,
23136a7a2e7SMaxime Coquelin 	.remove = eth_virtio_pci_remove,
23236a7a2e7SMaxime Coquelin };
23336a7a2e7SMaxime Coquelin 
RTE_INIT(rte_virtio_net_pci_pmd_init)23436a7a2e7SMaxime Coquelin RTE_INIT(rte_virtio_net_pci_pmd_init)
23536a7a2e7SMaxime Coquelin {
23636a7a2e7SMaxime Coquelin 	rte_eal_iopl_init();
23736a7a2e7SMaxime Coquelin 	rte_pci_register(&rte_virtio_net_pci_pmd);
23836a7a2e7SMaxime Coquelin }
23936a7a2e7SMaxime Coquelin 
24036a7a2e7SMaxime Coquelin RTE_PMD_REGISTER_PCI_TABLE(net_virtio, pci_id_virtio_map);
24136a7a2e7SMaxime Coquelin RTE_PMD_REGISTER_KMOD_DEP(net_virtio, "* igb_uio | uio_pci_generic | vfio-pci");
24236a7a2e7SMaxime Coquelin RTE_PMD_EXPORT_NAME(net_virtio, __COUNTER__);
243