xref: /dpdk/drivers/bus/cdx/cdx.c (revision 250b2b3899b2e58b1e059f99d6cb9f85683991aa)
145ef232aSNipun Gupta /* SPDX-License-Identifier: BSD-3-Clause
245ef232aSNipun Gupta  * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
345ef232aSNipun Gupta  */
445ef232aSNipun Gupta 
545ef232aSNipun Gupta /*
645ef232aSNipun Gupta  * Architecture Overview
745ef232aSNipun Gupta  * =====================
845ef232aSNipun Gupta  * CDX is a Hardware Architecture designed for AMD FPGA devices. It
945ef232aSNipun Gupta  * consists of sophisticated mechanism for interaction between FPGA,
1045ef232aSNipun Gupta  * Firmware and the APUs (Application CPUs).
1145ef232aSNipun Gupta  *
1245ef232aSNipun Gupta  * Firmware resides on RPU (Realtime CPUs) which interacts with
1345ef232aSNipun Gupta  * the FPGA program manager and the APUs. The RPU provides memory-mapped
1445ef232aSNipun Gupta  * interface (RPU if) which is used to communicate with APUs.
1545ef232aSNipun Gupta  *
1645ef232aSNipun Gupta  * The diagram below shows an overview of the AMD CDX architecture:
1745ef232aSNipun Gupta  *
1845ef232aSNipun Gupta  *          +--------------------------------------+
1945ef232aSNipun Gupta  *          |   DPDK                               |
2045ef232aSNipun Gupta  *          |                    DPDK CDX drivers  |
2145ef232aSNipun Gupta  *          |                             |        |
2245ef232aSNipun Gupta  *          |                    DPDK AMD CDX bus  |
2345ef232aSNipun Gupta  *          |                             |        |
2445ef232aSNipun Gupta  *          +-----------------------------|--------+
2545ef232aSNipun Gupta  *                                        |
2645ef232aSNipun Gupta  *          +-----------------------------|--------+
2745ef232aSNipun Gupta  *          |    Application CPUs (APU)   |        |
2845ef232aSNipun Gupta  *          |                             |        |
2945ef232aSNipun Gupta  *          |                     VFIO CDX driver  |
3045ef232aSNipun Gupta  *          |     Linux OS                |        |
3145ef232aSNipun Gupta  *          |                    Linux AMD CDX bus |
3245ef232aSNipun Gupta  *          |                             |        |
3345ef232aSNipun Gupta  *          +-----------------------------|--------+
3445ef232aSNipun Gupta  *                                        |
3545ef232aSNipun Gupta  *                                        |
3645ef232aSNipun Gupta  *          +------------------------| RPU if |----+
3745ef232aSNipun Gupta  *          |                             |        |
3845ef232aSNipun Gupta  *          |                             V        |
3945ef232aSNipun Gupta  *          |          Realtime CPUs (RPU)         |
4045ef232aSNipun Gupta  *          |                                      |
4145ef232aSNipun Gupta  *          +--------------------------------------+
4245ef232aSNipun Gupta  *                                |
4345ef232aSNipun Gupta  *          +---------------------|----------------+
4445ef232aSNipun Gupta  *          |  FPGA               |                |
4545ef232aSNipun Gupta  *          |      +-----------------------+       |
4645ef232aSNipun Gupta  *          |      |           |           |       |
4745ef232aSNipun Gupta  *          | +-------+    +-------+   +-------+   |
4845ef232aSNipun Gupta  *          | | dev 1 |    | dev 2 |   | dev 3 |   |
4945ef232aSNipun Gupta  *          | +-------+    +-------+   +-------+   |
5045ef232aSNipun Gupta  *          +--------------------------------------+
5145ef232aSNipun Gupta  *
5245ef232aSNipun Gupta  * The RPU firmware extracts the device information from the loaded FPGA
5345ef232aSNipun Gupta  * image and implements a mechanism that allows the APU drivers to
5445ef232aSNipun Gupta  * enumerate such devices (device personality and resource details) via
5545ef232aSNipun Gupta  * a dedicated communication channel.
5645ef232aSNipun Gupta  *
5745ef232aSNipun Gupta  * VFIO CDX driver provides the CDX device resources like MMIO and interrupts
5845ef232aSNipun Gupta  * to map to user-space. DPDK CDX bus uses sysfs interface and the vfio-cdx
5945ef232aSNipun Gupta  * driver to discover and initialize the CDX devices for user-space
6045ef232aSNipun Gupta  * applications.
6145ef232aSNipun Gupta  */
6245ef232aSNipun Gupta 
6345ef232aSNipun Gupta /**
6445ef232aSNipun Gupta  * @file
6545ef232aSNipun Gupta  * CDX probing using Linux sysfs.
6645ef232aSNipun Gupta  */
6745ef232aSNipun Gupta 
6845ef232aSNipun Gupta #include <string.h>
6945ef232aSNipun Gupta #include <dirent.h>
7045ef232aSNipun Gupta 
7145ef232aSNipun Gupta #include <rte_eal_paging.h>
7245ef232aSNipun Gupta #include <rte_errno.h>
7345ef232aSNipun Gupta #include <rte_devargs.h>
74eae7a04aSNipun Gupta #include <rte_kvargs.h>
7545ef232aSNipun Gupta #include <rte_malloc.h>
7645ef232aSNipun Gupta #include <rte_vfio.h>
7745ef232aSNipun Gupta 
7845ef232aSNipun Gupta #include <eal_filesystem.h>
7945ef232aSNipun Gupta 
8045ef232aSNipun Gupta #include "bus_cdx_driver.h"
8145ef232aSNipun Gupta #include "cdx_logs.h"
8245ef232aSNipun Gupta #include "private.h"
8345ef232aSNipun Gupta 
8445ef232aSNipun Gupta #define CDX_BUS_NAME	cdx
8545ef232aSNipun Gupta #define CDX_DEV_PREFIX	"cdx-"
8645ef232aSNipun Gupta 
8745ef232aSNipun Gupta /* CDX Bus iterators */
8845ef232aSNipun Gupta #define FOREACH_DEVICE_ON_CDXBUS(p)	\
8945ef232aSNipun Gupta 		RTE_TAILQ_FOREACH(p, &rte_cdx_bus.device_list, next)
9045ef232aSNipun Gupta 
9145ef232aSNipun Gupta #define FOREACH_DRIVER_ON_CDXBUS(p)	\
9245ef232aSNipun Gupta 		RTE_TAILQ_FOREACH(p, &rte_cdx_bus.driver_list, next)
9345ef232aSNipun Gupta 
9445ef232aSNipun Gupta struct rte_cdx_bus rte_cdx_bus;
9545ef232aSNipun Gupta 
96eae7a04aSNipun Gupta enum cdx_params {
97eae7a04aSNipun Gupta 	RTE_CDX_PARAM_NAME,
98eae7a04aSNipun Gupta };
99eae7a04aSNipun Gupta 
100eae7a04aSNipun Gupta static const char * const cdx_params_keys[] = {
101eae7a04aSNipun Gupta 	[RTE_CDX_PARAM_NAME] = "name",
102eae7a04aSNipun Gupta 	NULL,
103eae7a04aSNipun Gupta };
104eae7a04aSNipun Gupta 
10545ef232aSNipun Gupta /* Add a device to CDX bus */
10645ef232aSNipun Gupta static void
cdx_add_device(struct rte_cdx_device * cdx_dev)10745ef232aSNipun Gupta cdx_add_device(struct rte_cdx_device *cdx_dev)
10845ef232aSNipun Gupta {
10945ef232aSNipun Gupta 	TAILQ_INSERT_TAIL(&rte_cdx_bus.device_list, cdx_dev, next);
11045ef232aSNipun Gupta }
11145ef232aSNipun Gupta 
11245ef232aSNipun Gupta static int
cdx_get_kernel_driver_by_path(const char * filename,char * driver_name,size_t len)11345ef232aSNipun Gupta cdx_get_kernel_driver_by_path(const char *filename, char *driver_name,
11445ef232aSNipun Gupta 		size_t len)
11545ef232aSNipun Gupta {
11645ef232aSNipun Gupta 	int count;
11745ef232aSNipun Gupta 	char path[PATH_MAX];
11845ef232aSNipun Gupta 	char *name;
11945ef232aSNipun Gupta 
12045ef232aSNipun Gupta 	if (!filename || !driver_name)
12145ef232aSNipun Gupta 		return -1;
12245ef232aSNipun Gupta 
12345ef232aSNipun Gupta 	count = readlink(filename, path, PATH_MAX);
12445ef232aSNipun Gupta 	if (count >= PATH_MAX)
12545ef232aSNipun Gupta 		return -1;
12645ef232aSNipun Gupta 
12745ef232aSNipun Gupta 	/* For device does not have a driver */
12845ef232aSNipun Gupta 	if (count < 0)
12945ef232aSNipun Gupta 		return 1;
13045ef232aSNipun Gupta 
13145ef232aSNipun Gupta 	path[count] = '\0';
13245ef232aSNipun Gupta 
13345ef232aSNipun Gupta 	name = strrchr(path, '/');
13445ef232aSNipun Gupta 	if (name) {
13545ef232aSNipun Gupta 		strlcpy(driver_name, name + 1, len);
13645ef232aSNipun Gupta 		return 0;
13745ef232aSNipun Gupta 	}
13845ef232aSNipun Gupta 
13945ef232aSNipun Gupta 	return -1;
14045ef232aSNipun Gupta }
14145ef232aSNipun Gupta 
rte_cdx_map_device(struct rte_cdx_device * dev)14245ef232aSNipun Gupta int rte_cdx_map_device(struct rte_cdx_device *dev)
14345ef232aSNipun Gupta {
14445ef232aSNipun Gupta 	return cdx_vfio_map_resource(dev);
14545ef232aSNipun Gupta }
14645ef232aSNipun Gupta 
rte_cdx_unmap_device(struct rte_cdx_device * dev)14745ef232aSNipun Gupta void rte_cdx_unmap_device(struct rte_cdx_device *dev)
14845ef232aSNipun Gupta {
14945ef232aSNipun Gupta 	cdx_vfio_unmap_resource(dev);
15045ef232aSNipun Gupta }
15145ef232aSNipun Gupta 
15245ef232aSNipun Gupta static struct rte_devargs *
cdx_devargs_lookup(const char * dev_name)15345ef232aSNipun Gupta cdx_devargs_lookup(const char *dev_name)
15445ef232aSNipun Gupta {
15545ef232aSNipun Gupta 	struct rte_devargs *devargs;
15645ef232aSNipun Gupta 
15745ef232aSNipun Gupta 	RTE_EAL_DEVARGS_FOREACH("cdx", devargs) {
15845ef232aSNipun Gupta 		if (strcmp(devargs->name, dev_name) == 0)
15945ef232aSNipun Gupta 			return devargs;
16045ef232aSNipun Gupta 	}
16145ef232aSNipun Gupta 	return NULL;
16245ef232aSNipun Gupta }
16345ef232aSNipun Gupta 
16445ef232aSNipun Gupta static bool
cdx_ignore_device(const char * dev_name)16545ef232aSNipun Gupta cdx_ignore_device(const char *dev_name)
16645ef232aSNipun Gupta {
16745ef232aSNipun Gupta 	struct rte_devargs *devargs = cdx_devargs_lookup(dev_name);
16845ef232aSNipun Gupta 
16945ef232aSNipun Gupta 	switch (rte_cdx_bus.bus.conf.scan_mode) {
17045ef232aSNipun Gupta 	case RTE_BUS_SCAN_ALLOWLIST:
17145ef232aSNipun Gupta 		if (devargs && devargs->policy == RTE_DEV_ALLOWED)
17245ef232aSNipun Gupta 			return false;
17345ef232aSNipun Gupta 		break;
17445ef232aSNipun Gupta 	case RTE_BUS_SCAN_UNDEFINED:
17545ef232aSNipun Gupta 	case RTE_BUS_SCAN_BLOCKLIST:
17645ef232aSNipun Gupta 		if (devargs == NULL || devargs->policy != RTE_DEV_BLOCKED)
17745ef232aSNipun Gupta 			return false;
17845ef232aSNipun Gupta 		break;
17945ef232aSNipun Gupta 	}
18045ef232aSNipun Gupta 	return true;
18145ef232aSNipun Gupta }
18245ef232aSNipun Gupta 
18345ef232aSNipun Gupta /*
18445ef232aSNipun Gupta  * Scan one cdx sysfs entry, and fill the devices list from it.
18545ef232aSNipun Gupta  * It checks if the CDX device is bound to vfio-cdx driver. In case
18645ef232aSNipun Gupta  * the device is vfio bound, it reads the vendor and device id and
18745ef232aSNipun Gupta  * stores it for device-driver matching.
18845ef232aSNipun Gupta  */
18945ef232aSNipun Gupta static int
cdx_scan_one(const char * dirname,const char * dev_name)19045ef232aSNipun Gupta cdx_scan_one(const char *dirname, const char *dev_name)
19145ef232aSNipun Gupta {
19245ef232aSNipun Gupta 	char filename[PATH_MAX];
19345ef232aSNipun Gupta 	struct rte_cdx_device *dev = NULL;
19445ef232aSNipun Gupta 	char driver[PATH_MAX];
19545ef232aSNipun Gupta 	unsigned long tmp;
19645ef232aSNipun Gupta 	int ret;
19745ef232aSNipun Gupta 
19845ef232aSNipun Gupta 	dev = calloc(1, sizeof(*dev));
19945ef232aSNipun Gupta 	if (!dev)
20045ef232aSNipun Gupta 		return -ENOMEM;
20145ef232aSNipun Gupta 
20245ef232aSNipun Gupta 	dev->device.bus = &rte_cdx_bus.bus;
20345ef232aSNipun Gupta 	memcpy(dev->name, dev_name, RTE_DEV_NAME_MAX_LEN);
20445ef232aSNipun Gupta 	dev->device.name = dev->name;
20545ef232aSNipun Gupta 
20645ef232aSNipun Gupta 	/* parse driver */
20745ef232aSNipun Gupta 	snprintf(filename, sizeof(filename), "%s/driver", dirname);
20845ef232aSNipun Gupta 	ret = cdx_get_kernel_driver_by_path(filename, driver, sizeof(driver));
20945ef232aSNipun Gupta 	if (ret < 0) {
21045ef232aSNipun Gupta 		CDX_BUS_ERR("Fail to get kernel driver");
21187337f13SAbhijit Gangurde 		free(dev);
21287337f13SAbhijit Gangurde 		return -1;
21345ef232aSNipun Gupta 	}
21445ef232aSNipun Gupta 
215f29fb5caSNipun Gupta 	/* Allocate interrupt instance for cdx device */
216f29fb5caSNipun Gupta 	dev->intr_handle =
217f29fb5caSNipun Gupta 		rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
218f29fb5caSNipun Gupta 	if (dev->intr_handle == NULL) {
219f29fb5caSNipun Gupta 		CDX_BUS_ERR("Failed to create interrupt instance for %s",
220f29fb5caSNipun Gupta 			dev->device.name);
22187337f13SAbhijit Gangurde 		free(dev);
222f29fb5caSNipun Gupta 		return -ENOMEM;
223f29fb5caSNipun Gupta 	}
224f29fb5caSNipun Gupta 
22545ef232aSNipun Gupta 	/*
22645ef232aSNipun Gupta 	 * Check if device is bound to 'vfio-cdx' driver, so that user-space
22745ef232aSNipun Gupta 	 * can gracefully access the device.
22845ef232aSNipun Gupta 	 */
22945ef232aSNipun Gupta 	if (ret || strcmp(driver, "vfio-cdx")) {
23045ef232aSNipun Gupta 		ret = 0;
23145ef232aSNipun Gupta 		goto err;
23245ef232aSNipun Gupta 	}
23345ef232aSNipun Gupta 
23445ef232aSNipun Gupta 	/* get vendor id */
23545ef232aSNipun Gupta 	snprintf(filename, sizeof(filename), "%s/vendor", dirname);
23645ef232aSNipun Gupta 	if (eal_parse_sysfs_value(filename, &tmp) < 0) {
23745ef232aSNipun Gupta 		ret = -1;
23845ef232aSNipun Gupta 		goto err;
23945ef232aSNipun Gupta 	}
24045ef232aSNipun Gupta 	dev->id.vendor_id = (uint16_t)tmp;
24145ef232aSNipun Gupta 
24245ef232aSNipun Gupta 	/* get device id */
24345ef232aSNipun Gupta 	snprintf(filename, sizeof(filename), "%s/device", dirname);
24445ef232aSNipun Gupta 	if (eal_parse_sysfs_value(filename, &tmp) < 0) {
24587337f13SAbhijit Gangurde 		ret = -1;
24687337f13SAbhijit Gangurde 		goto err;
24745ef232aSNipun Gupta 	}
24845ef232aSNipun Gupta 	dev->id.device_id = (uint16_t)tmp;
24945ef232aSNipun Gupta 
25045ef232aSNipun Gupta 	cdx_add_device(dev);
25145ef232aSNipun Gupta 
25245ef232aSNipun Gupta 	return 0;
25345ef232aSNipun Gupta 
25445ef232aSNipun Gupta err:
25587337f13SAbhijit Gangurde 	rte_intr_instance_free(dev->intr_handle);
25645ef232aSNipun Gupta 	free(dev);
25745ef232aSNipun Gupta 	return ret;
25845ef232aSNipun Gupta }
25945ef232aSNipun Gupta 
26045ef232aSNipun Gupta /*
26145ef232aSNipun Gupta  * Scan the content of the CDX bus, and the devices in the devices
26245ef232aSNipun Gupta  * list.
26345ef232aSNipun Gupta  */
26445ef232aSNipun Gupta static int
cdx_scan(void)26545ef232aSNipun Gupta cdx_scan(void)
26645ef232aSNipun Gupta {
26745ef232aSNipun Gupta 	struct dirent *e;
26845ef232aSNipun Gupta 	DIR *dir;
26945ef232aSNipun Gupta 	char dirname[PATH_MAX];
27045ef232aSNipun Gupta 
27145ef232aSNipun Gupta 	dir = opendir(RTE_CDX_BUS_DEVICES_PATH);
27245ef232aSNipun Gupta 	if (dir == NULL) {
2739e4154beSDavid Marchand 		CDX_BUS_INFO("%s(): opendir failed: %s", __func__,
27445ef232aSNipun Gupta 			strerror(errno));
2759e4154beSDavid Marchand 		return 0;
27645ef232aSNipun Gupta 	}
27745ef232aSNipun Gupta 
27845ef232aSNipun Gupta 	while ((e = readdir(dir)) != NULL) {
27945ef232aSNipun Gupta 		if (e->d_name[0] == '.')
28045ef232aSNipun Gupta 			continue;
28145ef232aSNipun Gupta 
28245ef232aSNipun Gupta 		if (cdx_ignore_device(e->d_name))
28345ef232aSNipun Gupta 			continue;
28445ef232aSNipun Gupta 
28545ef232aSNipun Gupta 		snprintf(dirname, sizeof(dirname), "%s/%s",
28645ef232aSNipun Gupta 				RTE_CDX_BUS_DEVICES_PATH, e->d_name);
28745ef232aSNipun Gupta 
28845ef232aSNipun Gupta 		if (cdx_scan_one(dirname, e->d_name) < 0)
28945ef232aSNipun Gupta 			goto error;
29045ef232aSNipun Gupta 	}
29145ef232aSNipun Gupta 	closedir(dir);
29245ef232aSNipun Gupta 	return 0;
29345ef232aSNipun Gupta 
29445ef232aSNipun Gupta error:
29545ef232aSNipun Gupta 	closedir(dir);
29645ef232aSNipun Gupta 	return -1;
29745ef232aSNipun Gupta }
29845ef232aSNipun Gupta 
29945ef232aSNipun Gupta /* map a particular resource from a file */
30045ef232aSNipun Gupta void *
cdx_map_resource(void * requested_addr,int fd,uint64_t offset,size_t size,int additional_flags)30145ef232aSNipun Gupta cdx_map_resource(void *requested_addr, int fd, uint64_t offset, size_t size,
30245ef232aSNipun Gupta 		int additional_flags)
30345ef232aSNipun Gupta {
30445ef232aSNipun Gupta 	void *mapaddr;
30545ef232aSNipun Gupta 
30645ef232aSNipun Gupta 	/* Map the cdx MMIO memory resource of device */
30745ef232aSNipun Gupta 	mapaddr = rte_mem_map(requested_addr, size,
30845ef232aSNipun Gupta 		RTE_PROT_READ | RTE_PROT_WRITE,
30945ef232aSNipun Gupta 		RTE_MAP_SHARED | additional_flags, fd, offset);
31045ef232aSNipun Gupta 	if (mapaddr == NULL) {
31145ef232aSNipun Gupta 		CDX_BUS_ERR("%s(): cannot map resource(%d, %p, 0x%zx, 0x%"PRIx64"): %s (%p)",
31245ef232aSNipun Gupta 			__func__, fd, requested_addr, size, offset,
31345ef232aSNipun Gupta 			rte_strerror(rte_errno), mapaddr);
31445ef232aSNipun Gupta 	}
31545ef232aSNipun Gupta 	CDX_BUS_DEBUG("CDX MMIO memory mapped at %p", mapaddr);
31645ef232aSNipun Gupta 
31745ef232aSNipun Gupta 	return mapaddr;
31845ef232aSNipun Gupta }
31945ef232aSNipun Gupta 
32045ef232aSNipun Gupta /* unmap a particular resource */
32145ef232aSNipun Gupta void
cdx_unmap_resource(void * requested_addr,size_t size)32245ef232aSNipun Gupta cdx_unmap_resource(void *requested_addr, size_t size)
32345ef232aSNipun Gupta {
32445ef232aSNipun Gupta 	if (requested_addr == NULL)
32545ef232aSNipun Gupta 		return;
32645ef232aSNipun Gupta 
327a1c0d5d7SAbhijit Gangurde 	CDX_BUS_DEBUG("Unmapping CDX memory at %p", requested_addr);
328a1c0d5d7SAbhijit Gangurde 
32945ef232aSNipun Gupta 	/* Unmap the CDX memory resource of device */
33045ef232aSNipun Gupta 	if (rte_mem_unmap(requested_addr, size)) {
33145ef232aSNipun Gupta 		CDX_BUS_ERR("%s(): cannot mem unmap(%p, %#zx): %s", __func__,
33245ef232aSNipun Gupta 			requested_addr, size, rte_strerror(rte_errno));
33345ef232aSNipun Gupta 	}
33445ef232aSNipun Gupta }
33545ef232aSNipun Gupta /*
33645ef232aSNipun Gupta  * Match the CDX Driver and Device using device id and vendor id.
33745ef232aSNipun Gupta  */
33845ef232aSNipun Gupta static bool
cdx_match(const struct rte_cdx_driver * cdx_drv,const struct rte_cdx_device * cdx_dev)33945ef232aSNipun Gupta cdx_match(const struct rte_cdx_driver *cdx_drv,
34045ef232aSNipun Gupta 		const struct rte_cdx_device *cdx_dev)
34145ef232aSNipun Gupta {
34245ef232aSNipun Gupta 	const struct rte_cdx_id *id_table;
34345ef232aSNipun Gupta 
34445ef232aSNipun Gupta 	for (id_table = cdx_drv->id_table; id_table->vendor_id != 0;
34545ef232aSNipun Gupta 	     id_table++) {
34645ef232aSNipun Gupta 		/* check if device's identifiers match the driver's ones */
34745ef232aSNipun Gupta 		if (id_table->vendor_id != cdx_dev->id.vendor_id &&
34845ef232aSNipun Gupta 				id_table->vendor_id != RTE_CDX_ANY_ID)
34945ef232aSNipun Gupta 			continue;
35045ef232aSNipun Gupta 		if (id_table->device_id != cdx_dev->id.device_id &&
35145ef232aSNipun Gupta 				id_table->device_id != RTE_CDX_ANY_ID)
35245ef232aSNipun Gupta 			continue;
35345ef232aSNipun Gupta 
35445ef232aSNipun Gupta 		return 1;
35545ef232aSNipun Gupta 	}
35645ef232aSNipun Gupta 
35745ef232aSNipun Gupta 	return 0;
35845ef232aSNipun Gupta }
35945ef232aSNipun Gupta 
36045ef232aSNipun Gupta /*
36145ef232aSNipun Gupta  * If vendor id and device id match, call the probe() function of the
36245ef232aSNipun Gupta  * driver.
36345ef232aSNipun Gupta  */
36445ef232aSNipun Gupta static int
cdx_probe_one_driver(struct rte_cdx_driver * dr,struct rte_cdx_device * dev)36545ef232aSNipun Gupta cdx_probe_one_driver(struct rte_cdx_driver *dr,
36645ef232aSNipun Gupta 		struct rte_cdx_device *dev)
36745ef232aSNipun Gupta {
36845ef232aSNipun Gupta 	const char *dev_name = dev->name;
36945ef232aSNipun Gupta 	bool already_probed;
37045ef232aSNipun Gupta 	int ret;
37145ef232aSNipun Gupta 
37245ef232aSNipun Gupta 	/* The device is not blocked; Check if driver supports it */
37345ef232aSNipun Gupta 	if (!cdx_match(dr, dev))
37445ef232aSNipun Gupta 		/* Match of device and driver failed */
37545ef232aSNipun Gupta 		return 1;
37645ef232aSNipun Gupta 
37745ef232aSNipun Gupta 	already_probed = rte_dev_is_probed(&dev->device);
37845ef232aSNipun Gupta 	if (already_probed) {
37945ef232aSNipun Gupta 		CDX_BUS_INFO("Device %s is already probed", dev_name);
38045ef232aSNipun Gupta 		return -EEXIST;
38145ef232aSNipun Gupta 	}
38245ef232aSNipun Gupta 
38345ef232aSNipun Gupta 	CDX_BUS_DEBUG("  probe device %s using driver: %s", dev_name,
38445ef232aSNipun Gupta 		dr->driver.name);
38545ef232aSNipun Gupta 
38655bc9233SAbhijit Gangurde 	if (dr->drv_flags & RTE_CDX_DRV_NEED_MAPPING) {
38745ef232aSNipun Gupta 		ret = cdx_vfio_map_resource(dev);
38845ef232aSNipun Gupta 		if (ret != 0) {
38945ef232aSNipun Gupta 			CDX_BUS_ERR("CDX map device failed: %d", ret);
39045ef232aSNipun Gupta 			goto error_map_device;
39145ef232aSNipun Gupta 		}
39255bc9233SAbhijit Gangurde 	}
39345ef232aSNipun Gupta 
39445ef232aSNipun Gupta 	/* call the driver probe() function */
39545ef232aSNipun Gupta 	ret = dr->probe(dr, dev);
39645ef232aSNipun Gupta 	if (ret) {
39745ef232aSNipun Gupta 		CDX_BUS_ERR("Probe CDX driver: %s device: %s failed: %d",
39845ef232aSNipun Gupta 			dr->driver.name, dev_name, ret);
39945ef232aSNipun Gupta 		goto error_probe;
40045ef232aSNipun Gupta 	} else {
40145ef232aSNipun Gupta 		dev->device.driver = &dr->driver;
40245ef232aSNipun Gupta 	}
403eae7a04aSNipun Gupta 	dev->driver = dr;
40445ef232aSNipun Gupta 
40545ef232aSNipun Gupta 	return ret;
40645ef232aSNipun Gupta 
40745ef232aSNipun Gupta error_probe:
408*250b2b38SNikhil Agarwal 	cdx_vfio_unmap_resource(dev);
409f29fb5caSNipun Gupta 	rte_intr_instance_free(dev->intr_handle);
410f29fb5caSNipun Gupta 	dev->intr_handle = NULL;
41145ef232aSNipun Gupta error_map_device:
41245ef232aSNipun Gupta 	return ret;
41345ef232aSNipun Gupta }
41445ef232aSNipun Gupta 
41545ef232aSNipun Gupta /*
41645ef232aSNipun Gupta  * If vendor/device ID match, call the probe() function of all
41745ef232aSNipun Gupta  * registered driver for the given device. Return < 0 if initialization
41845ef232aSNipun Gupta  * failed, return 1 if no driver is found for this device.
41945ef232aSNipun Gupta  */
42045ef232aSNipun Gupta static int
cdx_probe_all_drivers(struct rte_cdx_device * dev)42145ef232aSNipun Gupta cdx_probe_all_drivers(struct rte_cdx_device *dev)
42245ef232aSNipun Gupta {
42345ef232aSNipun Gupta 	struct rte_cdx_driver *dr = NULL;
42445ef232aSNipun Gupta 	int rc = 0;
42545ef232aSNipun Gupta 
42645ef232aSNipun Gupta 	FOREACH_DRIVER_ON_CDXBUS(dr) {
42745ef232aSNipun Gupta 		rc = cdx_probe_one_driver(dr, dev);
42845ef232aSNipun Gupta 		if (rc < 0)
42945ef232aSNipun Gupta 			/* negative value is an error */
43045ef232aSNipun Gupta 			return rc;
43145ef232aSNipun Gupta 		if (rc > 0)
43245ef232aSNipun Gupta 			/* positive value means driver doesn't support it */
43345ef232aSNipun Gupta 			continue;
43445ef232aSNipun Gupta 		return 0;
43545ef232aSNipun Gupta 	}
43645ef232aSNipun Gupta 	return 1;
43745ef232aSNipun Gupta }
43845ef232aSNipun Gupta 
43945ef232aSNipun Gupta /*
44045ef232aSNipun Gupta  * Scan the content of the CDX bus, and call the probe() function for
44145ef232aSNipun Gupta  * all registered drivers that have a matching entry in its id_table
44245ef232aSNipun Gupta  * for discovered devices.
44345ef232aSNipun Gupta  */
44445ef232aSNipun Gupta static int
cdx_probe(void)44545ef232aSNipun Gupta cdx_probe(void)
44645ef232aSNipun Gupta {
44745ef232aSNipun Gupta 	struct rte_cdx_device *dev = NULL;
44845ef232aSNipun Gupta 	size_t probed = 0, failed = 0;
44945ef232aSNipun Gupta 	int ret = 0;
45045ef232aSNipun Gupta 
45145ef232aSNipun Gupta 	FOREACH_DEVICE_ON_CDXBUS(dev) {
45245ef232aSNipun Gupta 		probed++;
45345ef232aSNipun Gupta 
45445ef232aSNipun Gupta 		ret = cdx_probe_all_drivers(dev);
45545ef232aSNipun Gupta 		if (ret < 0) {
45645ef232aSNipun Gupta 			CDX_BUS_ERR("Requested device %s cannot be used",
45745ef232aSNipun Gupta 				dev->name);
45845ef232aSNipun Gupta 			rte_errno = errno;
45945ef232aSNipun Gupta 			failed++;
46045ef232aSNipun Gupta 		}
46145ef232aSNipun Gupta 	}
46245ef232aSNipun Gupta 
46345ef232aSNipun Gupta 	return (probed && probed == failed) ? -1 : 0;
46445ef232aSNipun Gupta }
46545ef232aSNipun Gupta 
46645ef232aSNipun Gupta static int
cdx_parse(const char * name,void * addr)46745ef232aSNipun Gupta cdx_parse(const char *name, void *addr)
46845ef232aSNipun Gupta {
46945ef232aSNipun Gupta 	const char **out = addr;
47045ef232aSNipun Gupta 	int ret;
47145ef232aSNipun Gupta 
47245ef232aSNipun Gupta 	ret = strncmp(name, CDX_DEV_PREFIX, strlen(CDX_DEV_PREFIX));
47345ef232aSNipun Gupta 
47445ef232aSNipun Gupta 	if (ret == 0 && addr)
47545ef232aSNipun Gupta 		*out = name;
47645ef232aSNipun Gupta 
47745ef232aSNipun Gupta 	return ret;
47845ef232aSNipun Gupta }
47945ef232aSNipun Gupta 
48045ef232aSNipun Gupta /* register a driver */
48145ef232aSNipun Gupta void
rte_cdx_register(struct rte_cdx_driver * driver)48245ef232aSNipun Gupta rte_cdx_register(struct rte_cdx_driver *driver)
48345ef232aSNipun Gupta {
48445ef232aSNipun Gupta 	TAILQ_INSERT_TAIL(&rte_cdx_bus.driver_list, driver, next);
48545ef232aSNipun Gupta 	driver->bus = &rte_cdx_bus;
48645ef232aSNipun Gupta }
48745ef232aSNipun Gupta 
48845ef232aSNipun Gupta /* unregister a driver */
48945ef232aSNipun Gupta void
rte_cdx_unregister(struct rte_cdx_driver * driver)49045ef232aSNipun Gupta rte_cdx_unregister(struct rte_cdx_driver *driver)
49145ef232aSNipun Gupta {
49245ef232aSNipun Gupta 	TAILQ_REMOVE(&rte_cdx_bus.driver_list, driver, next);
49345ef232aSNipun Gupta 	driver->bus = NULL;
49445ef232aSNipun Gupta }
49545ef232aSNipun Gupta 
49645ef232aSNipun Gupta static struct rte_device *
cdx_find_device(const struct rte_device * start,rte_dev_cmp_t cmp,const void * data)49745ef232aSNipun Gupta cdx_find_device(const struct rte_device *start, rte_dev_cmp_t cmp,
49845ef232aSNipun Gupta 		const void *data)
49945ef232aSNipun Gupta {
50045ef232aSNipun Gupta 	const struct rte_cdx_device *cdx_start;
50145ef232aSNipun Gupta 	struct rte_cdx_device *cdx_dev;
50245ef232aSNipun Gupta 
50345ef232aSNipun Gupta 	if (start != NULL) {
50445ef232aSNipun Gupta 		cdx_start = RTE_DEV_TO_CDX_DEV_CONST(start);
50545ef232aSNipun Gupta 		cdx_dev = TAILQ_NEXT(cdx_start, next);
50645ef232aSNipun Gupta 	} else {
50745ef232aSNipun Gupta 		cdx_dev = TAILQ_FIRST(&rte_cdx_bus.device_list);
50845ef232aSNipun Gupta 	}
50945ef232aSNipun Gupta 	while (cdx_dev != NULL) {
51045ef232aSNipun Gupta 		if (cmp(&cdx_dev->device, data) == 0)
51145ef232aSNipun Gupta 			return &cdx_dev->device;
51245ef232aSNipun Gupta 		cdx_dev = TAILQ_NEXT(cdx_dev, next);
51345ef232aSNipun Gupta 	}
51445ef232aSNipun Gupta 	return NULL;
51545ef232aSNipun Gupta }
51645ef232aSNipun Gupta 
517eae7a04aSNipun Gupta /* Remove a device from CDX bus */
518eae7a04aSNipun Gupta static void
cdx_remove_device(struct rte_cdx_device * cdx_dev)519eae7a04aSNipun Gupta cdx_remove_device(struct rte_cdx_device *cdx_dev)
520eae7a04aSNipun Gupta {
521eae7a04aSNipun Gupta 	TAILQ_REMOVE(&rte_cdx_bus.device_list, cdx_dev, next);
522eae7a04aSNipun Gupta }
523eae7a04aSNipun Gupta 
524eae7a04aSNipun Gupta /*
525eae7a04aSNipun Gupta  * If vendor/device ID match, call the remove() function of the
526eae7a04aSNipun Gupta  * driver.
527eae7a04aSNipun Gupta  */
528eae7a04aSNipun Gupta static int
cdx_detach_dev(struct rte_cdx_device * dev)529eae7a04aSNipun Gupta cdx_detach_dev(struct rte_cdx_device *dev)
530eae7a04aSNipun Gupta {
531eae7a04aSNipun Gupta 	struct rte_cdx_driver *dr;
532eae7a04aSNipun Gupta 	int ret = 0;
533eae7a04aSNipun Gupta 
534eae7a04aSNipun Gupta 	if (dev == NULL)
535eae7a04aSNipun Gupta 		return -EINVAL;
536eae7a04aSNipun Gupta 
537eae7a04aSNipun Gupta 	dr = dev->driver;
538eae7a04aSNipun Gupta 
539eae7a04aSNipun Gupta 	CDX_BUS_DEBUG("detach device %s using driver: %s",
540eae7a04aSNipun Gupta 		dev->device.name, dr->driver.name);
541eae7a04aSNipun Gupta 
542eae7a04aSNipun Gupta 	if (dr->remove) {
543eae7a04aSNipun Gupta 		ret = dr->remove(dev);
544eae7a04aSNipun Gupta 		if (ret < 0)
545eae7a04aSNipun Gupta 			return ret;
546eae7a04aSNipun Gupta 	}
547eae7a04aSNipun Gupta 
548eae7a04aSNipun Gupta 	/* clear driver structure */
549eae7a04aSNipun Gupta 	dev->driver = NULL;
550eae7a04aSNipun Gupta 	dev->device.driver = NULL;
551eae7a04aSNipun Gupta 
552eae7a04aSNipun Gupta 	rte_cdx_unmap_device(dev);
553eae7a04aSNipun Gupta 
554eae7a04aSNipun Gupta 	rte_intr_instance_free(dev->intr_handle);
555eae7a04aSNipun Gupta 	dev->intr_handle = NULL;
556eae7a04aSNipun Gupta 
557eae7a04aSNipun Gupta 	return 0;
558eae7a04aSNipun Gupta }
559eae7a04aSNipun Gupta 
560eae7a04aSNipun Gupta static int
cdx_plug(struct rte_device * dev)561eae7a04aSNipun Gupta cdx_plug(struct rte_device *dev)
562eae7a04aSNipun Gupta {
563eae7a04aSNipun Gupta 	return cdx_probe_all_drivers(RTE_DEV_TO_CDX_DEV(dev));
564eae7a04aSNipun Gupta }
565eae7a04aSNipun Gupta 
566eae7a04aSNipun Gupta static int
cdx_unplug(struct rte_device * dev)567eae7a04aSNipun Gupta cdx_unplug(struct rte_device *dev)
568eae7a04aSNipun Gupta {
569eae7a04aSNipun Gupta 	struct rte_cdx_device *cdx_dev;
570eae7a04aSNipun Gupta 	int ret;
571eae7a04aSNipun Gupta 
572eae7a04aSNipun Gupta 	cdx_dev = RTE_DEV_TO_CDX_DEV(dev);
573eae7a04aSNipun Gupta 	ret = cdx_detach_dev(cdx_dev);
574eae7a04aSNipun Gupta 	if (ret == 0) {
575eae7a04aSNipun Gupta 		cdx_remove_device(cdx_dev);
576eae7a04aSNipun Gupta 		rte_devargs_remove(dev->devargs);
577eae7a04aSNipun Gupta 		free(cdx_dev);
578eae7a04aSNipun Gupta 	}
579eae7a04aSNipun Gupta 	return ret;
580eae7a04aSNipun Gupta }
581eae7a04aSNipun Gupta 
58238554b1dSNipun Gupta static int
cdx_dma_map(struct rte_device * dev,void * addr,uint64_t iova,size_t len)58338554b1dSNipun Gupta cdx_dma_map(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
58438554b1dSNipun Gupta {
58538554b1dSNipun Gupta 	RTE_SET_USED(dev);
58638554b1dSNipun Gupta 
58738554b1dSNipun Gupta 	return rte_vfio_container_dma_map(RTE_VFIO_DEFAULT_CONTAINER_FD,
58838554b1dSNipun Gupta 					  (uintptr_t)addr, iova, len);
58938554b1dSNipun Gupta }
59038554b1dSNipun Gupta 
59138554b1dSNipun Gupta static int
cdx_dma_unmap(struct rte_device * dev,void * addr,uint64_t iova,size_t len)59238554b1dSNipun Gupta cdx_dma_unmap(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
59338554b1dSNipun Gupta {
59438554b1dSNipun Gupta 	RTE_SET_USED(dev);
59538554b1dSNipun Gupta 
59638554b1dSNipun Gupta 	return rte_vfio_container_dma_unmap(RTE_VFIO_DEFAULT_CONTAINER_FD,
59738554b1dSNipun Gupta 					    (uintptr_t)addr, iova, len);
59838554b1dSNipun Gupta }
59938554b1dSNipun Gupta 
60038554b1dSNipun Gupta static enum rte_iova_mode
cdx_get_iommu_class(void)60138554b1dSNipun Gupta cdx_get_iommu_class(void)
60238554b1dSNipun Gupta {
60338554b1dSNipun Gupta 	if (TAILQ_EMPTY(&rte_cdx_bus.device_list))
60438554b1dSNipun Gupta 		return RTE_IOVA_DC;
60538554b1dSNipun Gupta 
60638554b1dSNipun Gupta 	return RTE_IOVA_VA;
60738554b1dSNipun Gupta }
60838554b1dSNipun Gupta 
609eae7a04aSNipun Gupta static int
cdx_dev_match(const struct rte_device * dev,const void * _kvlist)610eae7a04aSNipun Gupta cdx_dev_match(const struct rte_device *dev,
611eae7a04aSNipun Gupta 		const void *_kvlist)
612eae7a04aSNipun Gupta {
613eae7a04aSNipun Gupta 	const struct rte_kvargs *kvlist = _kvlist;
614eae7a04aSNipun Gupta 	const char *key = cdx_params_keys[RTE_CDX_PARAM_NAME];
615eae7a04aSNipun Gupta 	const char *name;
616eae7a04aSNipun Gupta 
617eae7a04aSNipun Gupta 	/* no kvlist arg, all devices match */
618eae7a04aSNipun Gupta 	if (kvlist == NULL)
619eae7a04aSNipun Gupta 		return 0;
620eae7a04aSNipun Gupta 
621eae7a04aSNipun Gupta 	/* if key is present in kvlist and does not match, filter device */
622eae7a04aSNipun Gupta 	name = rte_kvargs_get(kvlist, key);
623eae7a04aSNipun Gupta 	if (name != NULL && strcmp(name, dev->name))
624eae7a04aSNipun Gupta 		return -1;
625eae7a04aSNipun Gupta 
626eae7a04aSNipun Gupta 	return 0;
627eae7a04aSNipun Gupta }
628eae7a04aSNipun Gupta 
629eae7a04aSNipun Gupta static void *
cdx_dev_iterate(const void * start,const char * str,const struct rte_dev_iterator * it __rte_unused)630eae7a04aSNipun Gupta cdx_dev_iterate(const void *start,
631eae7a04aSNipun Gupta 		const char *str,
632eae7a04aSNipun Gupta 		const struct rte_dev_iterator *it __rte_unused)
633eae7a04aSNipun Gupta {
634eae7a04aSNipun Gupta 	rte_bus_find_device_t find_device;
635eae7a04aSNipun Gupta 	struct rte_kvargs *kvargs = NULL;
636eae7a04aSNipun Gupta 	struct rte_device *dev;
637eae7a04aSNipun Gupta 
638eae7a04aSNipun Gupta 	if (str != NULL) {
639eae7a04aSNipun Gupta 		kvargs = rte_kvargs_parse(str, cdx_params_keys);
640eae7a04aSNipun Gupta 		if (kvargs == NULL) {
641eae7a04aSNipun Gupta 			CDX_BUS_ERR("cannot parse argument list %s", str);
642eae7a04aSNipun Gupta 			rte_errno = EINVAL;
643eae7a04aSNipun Gupta 			return NULL;
644eae7a04aSNipun Gupta 		}
645eae7a04aSNipun Gupta 	}
646eae7a04aSNipun Gupta 	find_device = rte_cdx_bus.bus.find_device;
647eae7a04aSNipun Gupta 	dev = find_device(start, cdx_dev_match, kvargs);
648eae7a04aSNipun Gupta 	rte_kvargs_free(kvargs);
649eae7a04aSNipun Gupta 	return dev;
650eae7a04aSNipun Gupta }
651eae7a04aSNipun Gupta 
65245ef232aSNipun Gupta struct rte_cdx_bus rte_cdx_bus = {
65345ef232aSNipun Gupta 	.bus = {
65445ef232aSNipun Gupta 		.scan = cdx_scan,
65545ef232aSNipun Gupta 		.probe = cdx_probe,
65645ef232aSNipun Gupta 		.find_device = cdx_find_device,
657eae7a04aSNipun Gupta 		.plug = cdx_plug,
658eae7a04aSNipun Gupta 		.unplug = cdx_unplug,
65945ef232aSNipun Gupta 		.parse = cdx_parse,
66038554b1dSNipun Gupta 		.dma_map = cdx_dma_map,
66138554b1dSNipun Gupta 		.dma_unmap = cdx_dma_unmap,
66238554b1dSNipun Gupta 		.get_iommu_class = cdx_get_iommu_class,
663eae7a04aSNipun Gupta 		.dev_iterate = cdx_dev_iterate,
66445ef232aSNipun Gupta 	},
66545ef232aSNipun Gupta 	.device_list = TAILQ_HEAD_INITIALIZER(rte_cdx_bus.device_list),
66645ef232aSNipun Gupta 	.driver_list = TAILQ_HEAD_INITIALIZER(rte_cdx_bus.driver_list),
66745ef232aSNipun Gupta };
66845ef232aSNipun Gupta 
66945ef232aSNipun Gupta RTE_REGISTER_BUS(cdx, rte_cdx_bus.bus);
67045ef232aSNipun Gupta RTE_LOG_REGISTER_DEFAULT(cdx_logtype_bus, NOTICE);
671