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