Hi, A gentle reminder, any other comments regarding this change?
>-----Original Message----- >From: Tomasz Duszynski <tduszyn...@marvell.com> >Sent: Wednesday, March 1, 2023 8:59 PM >To: dev@dpdk.org; Thomas Monjalon <tho...@monjalon.net>; Tomasz Duszynski ><tduszyn...@marvell.com> >Cc: chenbo....@intel.com; Jerin Jacob Kollanukkaran <jer...@marvell.com>; >step...@networkplumber.org; david.march...@redhat.com >Subject: [PATCH v4] bus: add platform bus > >Platform bus is a software bus under Linux that manages devices which >generally do not have built- >in discovery mechanisms. Linux normally learns about platform devices directly >from device-tree >during boot-up phase. > >Up to this point, whenever some userspace app needed control over platform >device or a range of >thereof some sort of driver being a mixture of vdev/rawdev was required. > >In order to simplify this task, provide an auto-probe experience and separate >bus logic from the >driver itself, add platform bus support. > >Currently devices backed up by vfio-platform kernel driver are supported. > >Signed-off-by: Tomasz Duszynski <tduszyn...@marvell.com> >--- >v4: >- add more documentation >- address failing compilation on systems running pre vfio-platform kernels >v3: >- fix device blocking >- fix conditional compilation based on VFIO_PRESENT >v2: >- take a step back and do not add eal changes that simplify parsing > sysfs string. That should be moved to a more generic library and > submitted separately. >- compile code conditionally based on VFIO_PRESENT > > MAINTAINERS | 4 + > doc/guides/linux_gsg/linux_drivers.rst | 45 ++ > doc/guides/rel_notes/release_23_03.rst | 5 + > drivers/bus/meson.build | 1 + > drivers/bus/platform/bus_platform_driver.h | 193 ++++++ > drivers/bus/platform/meson.build | 16 + > drivers/bus/platform/platform.c | 645 +++++++++++++++++++++ > drivers/bus/platform/platform_params.c | 75 +++ > drivers/bus/platform/private.h | 48 ++ > drivers/bus/platform/version.map | 10 + > 10 files changed, 1042 insertions(+) > create mode 100644 drivers/bus/platform/bus_platform_driver.h > create mode 100644 drivers/bus/platform/meson.build create mode 100644 >drivers/bus/platform/platform.c create mode 100644 >drivers/bus/platform/platform_params.c > create mode 100644 drivers/bus/platform/private.h create mode 100644 >drivers/bus/platform/version.map > >diff --git a/MAINTAINERS b/MAINTAINERS >index ffbf91296e..7aea426b53 100644 >--- a/MAINTAINERS >+++ b/MAINTAINERS >@@ -581,6 +581,10 @@ VDEV bus driver > F: drivers/bus/vdev/ > F: app/test/test_vdev.c > >+Platform bus driver >+M: Tomasz Duszynski <tduszyn...@marvell.com> >+F: drivers/bus/platform >+ > VMBUS bus driver > M: Long Li <lon...@microsoft.com> > F: drivers/bus/vmbus/ >diff --git a/doc/guides/linux_gsg/linux_drivers.rst >b/doc/guides/linux_gsg/linux_drivers.rst >index 2f3f079aab..5e8a3ae810 100644 >--- a/doc/guides/linux_gsg/linux_drivers.rst >+++ b/doc/guides/linux_gsg/linux_drivers.rst >@@ -296,6 +296,51 @@ If ``CONFIG_VFIO_NOIOMMU`` is not enabled in the kernel >configuration, VFIO >driver will not support the no-IOMMU mode, and other alternatives (such as >UIO drivers) will have >to be used. > >+VFIO Platform >+------------- >+ >+VFIO Platform is a kernel driver that extends capabilities of VFIO by >+adding support for platform devices that reside behind an IOMMU. Linux >+usually learns about platform devices directly from device tree during >boot-up phase. >+Unlike, for example, PCI devices which have necessary information built-in. >+ >+To make use of VFIO platform, the ``vfio-platform`` module must be loaded >first: >+ >+.. code-block:: console >+ >+ sudo modprobe vfio-platform >+ >+.. note:: >+ >+ By default ``vfio-platform`` assumes that platform device has dedicated >reset >+ driver. If such driver is missing or device does not require one this >+ option can be turned off by setting ``reset_required=0`` module parameter. >+ >+Afterwards platform device needs to be bound to ``vfio-platform``. This >+is standard procedure requiring two steps. First ``driver_override``, >+which is available inside platform device directory, needs to be set to >+``vfio-platform``: >+ >+.. code-block:: console >+ >+ sudo echo vfio-platform > >+ /sys/bus/platform/devices/DEV/driver_override >+ >+Next ``DEV`` device must be bound to ``vfio-platform`` driver: >+ >+.. code-block:: console >+ >+ sudo echo DEV > /sys/bus/platform/drivers/vfio-platform/bind >+ >+On application startup DPDK platform bus driver scans >+``/sys/bus/platform/devices`` searching for devices that have >+``driver`` symbolic link pointing to ``vfio-platform`` driver. Finally, >+scanned devices are matched against available PMDs. Matching is >+successful if either PMD name or PMD alias matches kernel driver name or PMD >name matches platform >device name, all in that order. >+ >+VFIO Platform depends on ARM/ARM64 and is usually enabled on >+distributions running on these systems. Consult your distributions >+documentation to make sure that is the case. >+ > .. _bifurcated_driver: > > Bifurcated Driver >diff --git a/doc/guides/rel_notes/release_23_03.rst >b/doc/guides/rel_notes/release_23_03.rst >index 49c18617a5..4bde86165f 100644 >--- a/doc/guides/rel_notes/release_23_03.rst >+++ b/doc/guides/rel_notes/release_23_03.rst >@@ -197,6 +197,11 @@ New Features > * Added support to capture packets at each graph node with packet metadata > and > node name. > >+* **Added platform bus support.** >+ >+ A platform bus provides a way to use Linux platform devices which >+ are compatible with vfio-platform kernel driver. >+ > > Removed Items > ------------- >diff --git a/drivers/bus/meson.build b/drivers/bus/meson.build index >45eab5233d..6d2520c543 100644 >--- a/drivers/bus/meson.build >+++ b/drivers/bus/meson.build >@@ -7,6 +7,7 @@ drivers = [ > 'fslmc', > 'ifpga', > 'pci', >+ 'platform', > 'vdev', > 'vmbus', > ] >diff --git a/drivers/bus/platform/bus_platform_driver.h >b/drivers/bus/platform/bus_platform_driver.h >new file mode 100644 >index 0000000000..a7445f77de >--- /dev/null >+++ b/drivers/bus/platform/bus_platform_driver.h >@@ -0,0 +1,193 @@ >+/* SPDX-License-Identifier: BSD-3-Clause >+ * Copyright(C) 2023 Marvell. >+ */ >+ >+#ifndef _BUS_PLATFORM_DRIVER_H_ >+#define _BUS_PLATFORM_DRIVER_H_ >+ >+/** >+ * @file >+ * Platform bus interface. >+ */ >+ >+#ifdef __cplusplus >+extern "C" { >+#endif >+ >+#include <stddef.h> >+#include <stdint.h> >+ >+#include <dev_driver.h> >+#include <rte_common.h> >+#include <rte_dev.h> >+#include <rte_os.h> >+#include <rte_vfio.h> >+ >+/* Forward declarations */ >+struct rte_platform_bus; >+struct rte_platform_device; >+struct rte_platform_driver; >+ >+/** >+ * Initialization function for the driver called during platform device >probing. >+ * >+ * @param pdev >+ * Pointer to the platform device. >+ * @return >+ * 0 on success, negative value otherwise. >+ */ >+typedef int (rte_platform_probe_t)(struct rte_platform_device *pdev); >+ >+/** >+ * Removal function for the driver called during platform device removal. >+ * >+ * @param pdev >+ * Pointer to the platform device. >+ * @return >+ * 0 on success, negative value otherwise. >+ */ >+typedef int (rte_platform_remove_t)(struct rte_platform_device *pdev); >+ >+/** >+ * Driver specific DMA mapping. >+ * >+ * @param pdev >+ * Pointer to the platform device. >+ * @param addr >+ * Starting virtual address of memory to be mapped. >+ * @param iova >+ * Starting IOVA address of memory to be mapped. >+ * @param len >+ * Length of memory segment being mapped. >+ * @return >+ * - 0 on success, negative value and rte_errno is set otherwise. >+ */ >+typedef int (rte_platform_dma_map_t)(struct rte_platform_device *pdev, void >*addr, uint64_t iova, >+ size_t len); >+ >+/** >+ * Driver specific DMA unmapping. >+ * >+ * @param pdev >+ * Pointer to the platform device. >+ * @param addr >+ * Starting virtual address of memory to be mapped. >+ * @param iova >+ * Starting IOVA address of memory to be mapped. >+ * @param len >+ * Length of memory segment being mapped. >+ * @return >+ * - 0 on success, negative value and rte_errno is set otherwise. >+ */ >+typedef int (rte_platform_dma_unmap_t)(struct rte_platform_device *pdev, void >*addr, uint64_t >iova, >+ size_t len); >+ >+/** >+ * A structure describing a platform device resource. >+ */ >+struct rte_platform_resource { >+ char *name; /**< Resource name specified via reg-names prop in >device-tree */ >+ struct rte_mem_resource mem; /**< Memory resource */ }; >+ >+/** >+ * A structure describing a platform device. >+ */ >+struct rte_platform_device { >+ RTE_TAILQ_ENTRY(rte_platform_device) next; /**< Next attached platform >device */ >+ struct rte_device device; /**< Core device */ >+ struct rte_platform_driver *driver; /**< Matching device driver */ >+ char name[RTE_DEV_NAME_MAX_LEN]; /**< Device name */ >+ unsigned int num_resource; /**< Number of device resources */ >+ struct rte_platform_resource *resource; /**< Device resources */ >+ int dev_fd; /**< VFIO device fd */ >+}; >+ >+/** >+ * A structure describing a platform device driver. >+ */ >+struct rte_platform_driver { >+ RTE_TAILQ_ENTRY(rte_platform_driver) next; /**< Next available platform >driver */ >+ struct rte_driver driver; /**< Core driver */ >+ rte_platform_probe_t *probe; /**< Device probe function */ >+ rte_platform_remove_t *remove; /**< Device remove function */ >+ rte_platform_dma_map_t *dma_map; /**< Device DMA map function */ >+ rte_platform_dma_unmap_t *dma_unmap; /**< Device DMA unmap function */ >+ uint32_t drv_flags; /**< Driver flags RTE_PLATFORM_DRV_* */ }; >+ >+/** Device driver needs IOVA as VA and cannot work with IOVA as PA */ >+#define RTE_PLATFORM_DRV_NEED_IOVA_AS_VA 0x0001 >+ >+/** >+ * @internal >+ * Helper macros used to convert core device to platform device. >+ */ >+#define RTE_DEV_TO_PLATFORM_DEV(ptr) \ >+ container_of(ptr, struct rte_platform_device, device) >+ >+#define RTE_DEV_TO_PLATFORM_DEV_CONST(ptr) \ >+ container_of(ptr, const struct rte_platform_device, device) >+ >+/** Helper for platform driver registration. */ #define >+RTE_PMD_REGISTER_PLATFORM(nm, platform_drv) \ static const char >+*pdrvinit_ ## nm ## _alias; \ RTE_INIT(pdrvinitfn_ ##nm) \ { \ >+ (platform_drv).driver.name = RTE_STR(nm); \ >+ (platform_drv).driver.alias = pdrvinit_ ## nm ## _alias; \ >+ rte_platform_register(&(platform_drv)); \ } \ RTE_PMD_EXPORT_NAME(nm, >+__COUNTER__) >+ >+/** Helper for setting platform driver alias. */ #define >+RTE_PMD_REGISTER_ALIAS(nm, alias) \ static const char *pdrvinit_ ## nm >+## _alias = RTE_STR(alias) >+ >+#ifdef VFIO_PRESENT >+ >+/** >+ * Register a platform device driver. >+ * >+ * @warning >+ * @b EXPERIMENTAL: this API may change without prior notice. >+ * >+ * @param pdrv >+ * A pointer to a rte_platform_driver structure describing driver to be >registered. >+ */ >+__rte_internal >+void rte_platform_register(struct rte_platform_driver *pdrv); >+ >+/** >+ * Unregister a platform device driver. >+ * >+ * @warning >+ * @b EXPERIMENTAL: this API may change without prior notice. >+ * >+ * @param pdrv >+ * A pointer to a rte_platform_driver structure describing driver to be >unregistered. >+ */ >+__rte_internal >+void rte_platform_unregister(struct rte_platform_driver *pdrv); >+ >+#else >+ >+__rte_internal >+static inline void >+rte_platform_register(struct rte_platform_driver *pdrv __rte_unused) { >+} >+ >+__rte_internal >+static inline void >+rte_platform_unregister(struct rte_platform_driver *pdrv __rte_unused) >+{ } >+ >+#endif /* VFIO_PRESENT */ >+ >+#ifdef __cplusplus >+} >+#endif >+ >+#endif /* _BUS_PLATFORM_DRIVER_H_ */ >diff --git a/drivers/bus/platform/meson.build >b/drivers/bus/platform/meson.build >new file mode 100644 >index 0000000000..417d7b81f8 >--- /dev/null >+++ b/drivers/bus/platform/meson.build >@@ -0,0 +1,16 @@ >+# SPDX-License-Identifier: BSD-3-Clause # Copyright(C) 2023 Marvell. >+# >+ >+if not is_linux >+ build = false >+ reason = 'only supported on Linux' >+ subdir_done() >+endif >+ >+deps += ['kvargs'] >+sources = files( >+ 'platform_params.c', >+ 'platform.c', >+) >+driver_sdk_headers += files('bus_platform_driver.h') >diff --git a/drivers/bus/platform/platform.c b/drivers/bus/platform/platform.c >new file mode 100644 >index 0000000000..536d9524c6 >--- /dev/null >+++ b/drivers/bus/platform/platform.c >@@ -0,0 +1,645 @@ >+/* SPDX-License-Identifier: BSD-3-Clause >+ * Copyright(C) 2023 Marvell. >+ */ >+ >+#include <dirent.h> >+#include <inttypes.h> >+#include <stdlib.h> >+#include <string.h> >+#include <sys/ioctl.h> >+#include <sys/mman.h> >+#include <sys/queue.h> >+#include <unistd.h> >+ >+#include <bus_driver.h> >+#include <bus_platform_driver.h> >+#include <eal_filesystem.h> >+#include <rte_bus.h> >+#include <rte_devargs.h> >+#include <rte_errno.h> >+#include <rte_log.h> >+#include <rte_memory.h> >+#include <rte_string_fns.h> >+#include <rte_vfio.h> >+ >+#include "private.h" >+ >+#ifdef VFIO_PRESENT >+ >+#define PLATFORM_BUS_DEVICES_PATH "/sys/bus/platform/devices" >+ >+void >+rte_platform_register(struct rte_platform_driver *pdrv) { >+ TAILQ_INSERT_TAIL(&platform_bus.driver_list, pdrv, next); } >+ >+void >+rte_platform_unregister(struct rte_platform_driver *pdrv) { >+ TAILQ_REMOVE(&platform_bus.driver_list, pdrv, next); } >+ >+static struct rte_devargs * >+dev_devargs(const char *dev_name) >+{ >+ struct rte_devargs *devargs; >+ >+ RTE_EAL_DEVARGS_FOREACH("platform", devargs) { >+ if (!strcmp(devargs->name, dev_name)) >+ return devargs; >+ } >+ >+ return NULL; >+} >+ >+static bool >+dev_allowed(const char *dev_name) >+{ >+ struct rte_devargs *devargs; >+ >+ devargs = dev_devargs(dev_name); >+ if (devargs == NULL) >+ return true; >+ >+ switch (platform_bus.bus.conf.scan_mode) { >+ case RTE_BUS_SCAN_UNDEFINED: >+ case RTE_BUS_SCAN_ALLOWLIST: >+ if (devargs->policy == RTE_DEV_ALLOWED) >+ return true; >+ break; >+ case RTE_BUS_SCAN_BLOCKLIST: >+ if (devargs->policy == RTE_DEV_BLOCKED) >+ return false; >+ break; >+ } >+ >+ return true; >+} >+ >+static int >+dev_add(const char *dev_name) >+{ >+ struct rte_platform_device *pdev, *tmp; >+ char path[PATH_MAX]; >+ unsigned long val; >+ >+ pdev = calloc(1, sizeof(*pdev)); >+ if (pdev == NULL) >+ return -ENOMEM; >+ >+ rte_strscpy(pdev->name, dev_name, sizeof(pdev->name)); >+ pdev->device.name = pdev->name; >+ pdev->device.devargs = dev_devargs(dev_name); >+ pdev->device.bus = &platform_bus.bus; >+ snprintf(path, sizeof(path), PLATFORM_BUS_DEVICES_PATH "/%s/numa_node", >dev_name); >+ pdev->device.numa_node = eal_parse_sysfs_value(path, &val) ? >+rte_socket_id() : val; >+ >+ FOREACH_DEVICE_ON_PLATFORM_BUS(tmp) { >+ if (!strcmp(tmp->name, pdev->name)) { >+ PLATFORM_LOG(INFO, "device %s already added\n", >pdev->name); >+ >+ if (tmp->device.devargs != pdev->device.devargs) >+ rte_devargs_remove(pdev->device.devargs); >+ >+ free(pdev); >+ } >+ } >+ >+ TAILQ_INSERT_HEAD(&platform_bus.device_list, pdev, next); >+ >+ PLATFORM_LOG(INFO, "adding device %s to the list\n", dev_name); >+ >+ return 0; >+} >+ >+static char * >+dev_kernel_driver_name(const char *dev_name) { >+ char path[PATH_MAX], buf[BUFSIZ] = { }; >+ char *kdrv; >+ int ret; >+ >+ snprintf(path, sizeof(path), PLATFORM_BUS_DEVICES_PATH "/%s/driver", >dev_name); >+ /* save space for NUL */ >+ ret = readlink(path, buf, sizeof(buf) - 1); >+ if (ret <= 0) >+ return NULL; >+ >+ /* last token is kernel driver name */ >+ kdrv = strrchr(buf, '/'); >+ if (kdrv != NULL) >+ return strdup(kdrv + 1); >+ >+ return NULL; >+} >+ >+static bool >+dev_is_bound_vfio_platform(const char *dev_name) { >+ char *kdrv; >+ int ret; >+ >+ kdrv = dev_kernel_driver_name(dev_name); >+ if (!kdrv) >+ return false; >+ >+ ret = strcmp(kdrv, "vfio-platform"); >+ free(kdrv); >+ >+ return ret == 0; >+} >+ >+static int >+platform_bus_scan(void) >+{ >+ const struct dirent *ent; >+ const char *dev_name; >+ int ret = 0; >+ DIR *dp; >+ >+ dp = opendir(PLATFORM_BUS_DEVICES_PATH); >+ if (dp == NULL) { >+ PLATFORM_LOG(INFO, "failed to open %s\n", >PLATFORM_BUS_DEVICES_PATH); >+ return -errno; >+ } >+ >+ while ((ent = readdir(dp))) { >+ dev_name = ent->d_name; >+ if (dev_name[0] == '.') >+ continue; >+ >+ if (!dev_allowed(dev_name)) >+ continue; >+ >+ if (!dev_is_bound_vfio_platform(dev_name)) >+ continue; >+ >+ ret = dev_add(dev_name); >+ if (ret) >+ break; >+ } >+ >+ closedir(dp); >+ >+ return ret; >+} >+ >+static int >+device_map_resource_offset(struct rte_platform_device *pdev, struct >rte_platform_resource *res, >+ size_t offset) >+{ >+ res->mem.addr = mmap(NULL, res->mem.len, PROT_READ | PROT_WRITE, >MAP_SHARED, pdev->dev_fd, >+ offset); >+ if (res->mem.addr == MAP_FAILED) >+ return -errno; >+ >+ PLATFORM_LOG(DEBUG, "adding resource va = %p len = %"PRIu64" name = >%s\n", res->mem.addr, >+ res->mem.len, res->name); >+ >+ return 0; >+} >+ >+static void >+device_unmap_resources(struct rte_platform_device *pdev) { >+ struct rte_platform_resource *res; >+ unsigned int i; >+ >+ for (i = 0; i < pdev->num_resource; i++) { >+ res = &pdev->resource[i]; >+ munmap(res->mem.addr, res->mem.len); >+ free(res->name); >+ } >+ >+ free(pdev->resource); >+ pdev->resource = NULL; >+ pdev->num_resource = 0; >+} >+ >+static int >+read_sysfs_string(const char *path, char *buf, size_t size) { >+ FILE *f; >+ char *p; >+ >+ f = fopen(path, "r"); >+ if (f == NULL) >+ return -errno; >+ >+ if (fgets(buf, size, f) == NULL) { >+ fclose(f); >+ return -ENODATA; >+ } >+ >+ fclose(f); >+ >+ p = strrchr(buf, '\n'); >+ if (p != NULL) >+ *p = '\0'; >+ >+ return 0; >+} >+ >+static char * >+of_resource_name(const char *dev_name, int index) { >+ char path[PATH_MAX], buf[BUFSIZ] = { }; >+ int num = 0, ret; >+ char *name; >+ >+ snprintf(path, sizeof(path), PLATFORM_BUS_DEVICES_PATH >"/%s/of_node/reg-names", dev_name); >+ ret = read_sysfs_string(path, buf, sizeof(buf)); >+ if (ret) >+ return NULL; >+ >+ for (name = buf; name; name += strlen(name) + 1) { >+ if (num++ != index) >+ continue; >+ return strdup(name); >+ } >+ >+ return NULL; >+} >+ >+static int >+device_map_resources(struct rte_platform_device *pdev, unsigned int >+num) { >+ struct rte_platform_resource *res; >+ unsigned int i; >+ int ret; >+ >+ if (num == 0) >+ PLATFORM_LOG(WARNING, "device %s has no resources\n", >pdev->name); >+ >+ pdev->resource = calloc(num, sizeof(*pdev->resource)); >+ if (pdev->resource == NULL) >+ return -ENOMEM; >+ >+ for (i = 0; i < num; i++) { >+ struct vfio_region_info reg_info = { >+ .argsz = sizeof(reg_info), >+ .index = i, >+ }; >+ >+ ret = ioctl(pdev->dev_fd, VFIO_DEVICE_GET_REGION_INFO, >®_info); >+ if (ret) { >+ PLATFORM_LOG(ERR, "failed to get region info at %d\n", >i); >+ ret = -errno; >+ goto out; >+ } >+ >+ res = &pdev->resource[i]; >+ res->name = of_resource_name(pdev->name, reg_info.index); >+ res->mem.len = reg_info.size; >+ ret = device_map_resource_offset(pdev, res, reg_info.offset); >+ if (ret) { >+ PLATFORM_LOG(ERR, "failed to ioremap resource at %d\n", >i); >+ goto out; >+ } >+ >+ pdev->num_resource++; >+ } >+ >+ return 0; >+out: >+ device_unmap_resources(pdev); >+ >+ return ret; >+} >+ >+static void >+device_cleanup(struct rte_platform_device *pdev) { >+ device_unmap_resources(pdev); >+ rte_vfio_release_device(PLATFORM_BUS_DEVICES_PATH, pdev->name, >+pdev->dev_fd); } >+ >+static int >+device_setup(struct rte_platform_device *pdev) { >+ struct vfio_device_info dev_info = { .argsz = sizeof(dev_info), }; >+ const char *name = pdev->name; >+ int ret; >+ >+ ret = rte_vfio_setup_device(PLATFORM_BUS_DEVICES_PATH, name, >&pdev->dev_fd, &dev_info); >+ if (ret) { >+ PLATFORM_LOG(ERR, "failed to setup %s\n", name); >+ return -ENODEV; >+ } >+ >+ /* This is an extra check to confirm that platform device was >initialized >+ * by a kernel vfio-platform driver. On kernels that predate >vfio-platform >+ * driver this flag obviously does not exist. In such scenarios this >+ * check needs to be removed otherwise compilation fails. >+ * >+ * Now, on such old kernels code will never reach here because >+ * there is another check much earlier which verifies whether >+ * device has been bound to vfio-platform driver. >+ */ >+#ifdef VFIO_DEVICE_FLAGS_PLATFORM >+ if (!(dev_info.flags & VFIO_DEVICE_FLAGS_PLATFORM)) { >+ PLATFORM_LOG(ERR, "device not backed by vfio-platform\n"); >+ ret = -ENOTSUP; >+ goto out; >+ } >+#endif >+ >+ ret = device_map_resources(pdev, dev_info.num_regions); >+ if (ret) { >+ PLATFORM_LOG(ERR, "failed to setup platform resources\n"); >+ goto out; >+ } >+ >+ return 0; >+out: >+ device_cleanup(pdev); >+ >+ return ret; >+} >+ >+static int >+driver_call_probe(struct rte_platform_driver *pdrv, struct >+rte_platform_device *pdev) { >+ int ret; >+ >+ if (rte_dev_is_probed(&pdev->device)) >+ return -EBUSY; >+ >+ if (pdrv->probe != NULL) { >+ pdev->driver = pdrv; >+ ret = pdrv->probe(pdev); >+ if (ret) >+ return ret; >+ } >+ >+ pdev->device.driver = &pdrv->driver; >+ >+ return 0; >+} >+ >+static int >+driver_probe_device(struct rte_platform_driver *pdrv, struct >+rte_platform_device *pdev) { >+ enum rte_iova_mode iova_mode; >+ int ret; >+ >+ iova_mode = rte_eal_iova_mode(); >+ if (pdrv->drv_flags & RTE_PLATFORM_DRV_NEED_IOVA_AS_VA && iova_mode != >RTE_IOVA_VA) { >+ PLATFORM_LOG(ERR, "driver %s expects VA IOVA mode but current >mode is PA\n", >+ pdrv->driver.name); >+ return -EINVAL; >+ } >+ >+ ret = device_setup(pdev); >+ if (ret) >+ return ret; >+ >+ ret = driver_call_probe(pdrv, pdev); >+ if (ret) >+ device_cleanup(pdev); >+ >+ return ret; >+} >+ >+static bool >+driver_match_device(struct rte_platform_driver *pdrv, struct >+rte_platform_device *pdev) { >+ bool match = false; >+ char *kdrv; >+ >+ kdrv = dev_kernel_driver_name(pdev->name); >+ if (!kdrv) >+ return false; >+ >+ /* match by driver name */ >+ if (!strcmp(kdrv, pdrv->driver.name)) { >+ match = true; >+ goto out; >+ } >+ >+ /* match by driver alias */ >+ if (pdrv->driver.alias != NULL && !strcmp(kdrv, pdrv->driver.alias)) { >+ match = true; >+ goto out; >+ } >+ >+ /* match by device name */ >+ if (!strcmp(pdev->name, pdrv->driver.name)) >+ match = true; >+ >+out: >+ free(kdrv); >+ >+ return match; >+} >+ >+static int >+device_attach(struct rte_platform_device *pdev) { >+ struct rte_platform_driver *pdrv; >+ >+ FOREACH_DRIVER_ON_PLATFORM_BUS(pdrv) { >+ if (driver_match_device(pdrv, pdev)) >+ break; >+ } >+ >+ if (pdrv == NULL) >+ return -ENODEV; >+ >+ return driver_probe_device(pdrv, pdev); } >+ >+static int >+platform_bus_probe(void) >+{ >+ struct rte_platform_device *pdev; >+ int ret; >+ >+ FOREACH_DEVICE_ON_PLATFORM_BUS(pdev) { >+ ret = device_attach(pdev); >+ if (ret == -EBUSY) { >+ PLATFORM_LOG(DEBUG, "device %s already probed\n", >pdev->name); >+ continue; >+ } >+ if (ret) >+ PLATFORM_LOG(ERR, "failed to probe %s\n", pdev->name); >+ } >+ >+ return 0; >+} >+ >+static struct rte_device * >+platform_bus_find_device(const struct rte_device *start, rte_dev_cmp_t >+cmp, const void *data) { >+ struct rte_platform_device *pdev; >+ >+ pdev = start ? RTE_TAILQ_NEXT(RTE_DEV_TO_PLATFORM_DEV_CONST(start), >next) : >+ RTE_TAILQ_FIRST(&platform_bus.device_list); >+ while (pdev) { >+ if (cmp(&pdev->device, data) == 0) >+ return &pdev->device; >+ >+ pdev = RTE_TAILQ_NEXT(pdev, next); >+ } >+ >+ return NULL; >+} >+ >+static int >+platform_bus_plug(struct rte_device *dev) { >+ struct rte_platform_device *pdev; >+ >+ if (!dev_allowed(dev->name)) >+ return -EPERM; >+ >+ if (!dev_is_bound_vfio_platform(dev->name)) >+ return -EPERM; >+ >+ pdev = RTE_DEV_TO_PLATFORM_DEV(dev); >+ if (pdev == NULL) >+ return -EINVAL; >+ >+ return device_attach(pdev); >+} >+ >+static void >+device_release_driver(struct rte_platform_device *pdev) { >+ struct rte_platform_driver *pdrv; >+ int ret; >+ >+ pdrv = pdev->driver; >+ if (pdrv != NULL && pdrv->remove != NULL) { >+ ret = pdrv->remove(pdev); >+ if (ret) >+ PLATFORM_LOG(WARNING, "failed to remove %s\n", >pdev->name); >+ } >+ >+ pdev->device.driver = NULL; >+ pdev->driver = NULL; >+} >+ >+static int >+platform_bus_unplug(struct rte_device *dev) { >+ struct rte_platform_device *pdev; >+ >+ pdev = RTE_DEV_TO_PLATFORM_DEV(dev); >+ if (pdev == NULL) >+ return -EINVAL; >+ >+ device_release_driver(pdev); >+ device_cleanup(pdev); >+ rte_devargs_remove(pdev->device.devargs); >+ free(pdev); >+ >+ return 0; >+} >+ >+static int >+platform_bus_parse(const char *name, void *addr) { >+ struct rte_platform_device pdev = { }; >+ struct rte_platform_driver *pdrv; >+ const char **out = addr; >+ >+ rte_strscpy(pdev.name, name, sizeof(pdev.name)); >+ >+ FOREACH_DRIVER_ON_PLATFORM_BUS(pdrv) { >+ if (driver_match_device(pdrv, &pdev)) >+ break; >+ } >+ >+ if (pdrv != NULL && addr != NULL) >+ *out = name; >+ >+ return pdrv != NULL ? 0 : -ENODEV; >+} >+ >+static int >+platform_bus_dma_map(struct rte_device *dev, void *addr, uint64_t iova, >+size_t len) { >+ struct rte_platform_device *pdev; >+ >+ pdev = RTE_DEV_TO_PLATFORM_DEV(dev); >+ if (pdev == NULL || pdev->driver == NULL) { >+ rte_errno = EINVAL; >+ return -1; >+ } >+ >+ if (pdev->driver->dma_map != NULL) >+ return pdev->driver->dma_map(pdev, addr, iova, len); >+ >+ return rte_vfio_container_dma_map(RTE_VFIO_DEFAULT_CONTAINER_FD, >+(uint64_t)addr, iova, len); } >+ >+static int >+platform_bus_dma_unmap(struct rte_device *dev, void *addr, uint64_t >+iova, size_t len) { >+ struct rte_platform_device *pdev; >+ >+ pdev = RTE_DEV_TO_PLATFORM_DEV(dev); >+ if (pdev == NULL || pdev->driver == NULL) { >+ rte_errno = EINVAL; >+ return -1; >+ } >+ >+ if (pdev->driver->dma_unmap != NULL) >+ return pdev->driver->dma_unmap(pdev, addr, iova, len); >+ >+ return rte_vfio_container_dma_unmap(RTE_VFIO_DEFAULT_CONTAINER_FD, >(uint64_t)addr, iova, >+ len); >+} >+ >+static enum rte_iova_mode >+platform_bus_get_iommu_class(void) >+{ >+ struct rte_platform_driver *pdrv; >+ struct rte_platform_device *pdev; >+ >+ FOREACH_DEVICE_ON_PLATFORM_BUS(pdev) { >+ pdrv = pdev->driver; >+ if (pdrv != NULL && pdrv->drv_flags & >RTE_PLATFORM_DRV_NEED_IOVA_AS_VA) >+ return RTE_IOVA_VA; >+ } >+ >+ return RTE_IOVA_DC; >+} >+ >+static int >+platform_bus_cleanup(void) >+{ >+ struct rte_platform_device *pdev, *tmp; >+ >+ RTE_TAILQ_FOREACH_SAFE(pdev, &platform_bus.device_list, next, tmp) { >+ platform_bus_unplug(&pdev->device); >+ TAILQ_REMOVE(&platform_bus.device_list, pdev, next); >+ } >+ >+ return 0; >+} >+ >+struct rte_platform_bus platform_bus = { >+ .bus = { >+ .scan = platform_bus_scan, >+ .probe = platform_bus_probe, >+ .find_device = platform_bus_find_device, >+ .plug = platform_bus_plug, >+ .unplug = platform_bus_unplug, >+ .parse = platform_bus_parse, >+ .dma_map = platform_bus_dma_map, >+ .dma_unmap = platform_bus_dma_unmap, >+ .get_iommu_class = platform_bus_get_iommu_class, >+ .dev_iterate = platform_bus_dev_iterate, >+ .cleanup = platform_bus_cleanup, >+ }, >+ .device_list = TAILQ_HEAD_INITIALIZER(platform_bus.device_list), >+ .driver_list = TAILQ_HEAD_INITIALIZER(platform_bus.driver_list), >+}; >+ >+RTE_REGISTER_BUS(platform, platform_bus.bus); >+RTE_LOG_REGISTER_DEFAULT(platform_bus_logtype, NOTICE); >+ >+#endif /* VFIO_PRESENT */ >diff --git a/drivers/bus/platform/platform_params.c >b/drivers/bus/platform/platform_params.c >new file mode 100644 >index 0000000000..edc246b53e >--- /dev/null >+++ b/drivers/bus/platform/platform_params.c >@@ -0,0 +1,75 @@ >+/* SPDX-License-Identifier: BSD-3-Clause >+ * Copyright(C) 2023 Marvell. >+ */ >+ >+#include <string.h> >+#include <errno.h> >+ >+#include <rte_bus.h> >+#include <rte_common.h> >+#include <rte_dev.h> >+#include <rte_errno.h> >+#include <rte_kvargs.h> >+#include <rte_vfio.h> >+ >+#include "bus_platform_driver.h" >+#include "private.h" >+ >+#ifdef VFIO_PRESENT >+ >+enum platform_params { >+ RTE_PLATFORM_PARAM_NAME, >+}; >+ >+static const char * const platform_params_keys[] = { >+ [RTE_PLATFORM_PARAM_NAME] = "name", >+ NULL >+}; >+ >+static int >+platform_dev_match(const struct rte_device *dev, const void *_kvlist) { >+ const char *key = platform_params_keys[RTE_PLATFORM_PARAM_NAME]; >+ const struct rte_kvargs *kvlist = _kvlist; >+ const char *name; >+ >+ /* no kvlist arg, all devices match */ >+ if (kvlist == NULL) >+ return 0; >+ >+ /* if key is present in kvlist and does not match, filter device */ >+ name = rte_kvargs_get(kvlist, key); >+ if (name != NULL && strcmp(name, dev->name)) >+ return -1; >+ >+ return 0; >+} >+ >+void * >+platform_bus_dev_iterate(const void *start, const char *str, >+ const struct rte_dev_iterator *it __rte_unused) { >+ rte_bus_find_device_t find_device; >+ struct rte_kvargs *kvargs = NULL; >+ struct rte_device *dev; >+ >+ if (str != NULL) { >+ kvargs = rte_kvargs_parse(str, platform_params_keys); >+ if (!kvargs) { >+ PLATFORM_LOG(ERR, "cannot parse argument list %s", str); >+ rte_errno = EINVAL; >+ return NULL; >+ } >+ } >+ >+ find_device = platform_bus.bus.find_device; >+ if (find_device == NULL) >+ return NULL; >+ >+ dev = platform_bus.bus.find_device(start, platform_dev_match, kvargs); >+ rte_kvargs_free(kvargs); >+ >+ return dev; >+} >+ >+#endif /* VFIO_PRESENT */ >diff --git a/drivers/bus/platform/private.h b/drivers/bus/platform/private.h >new file mode 100644 >index 0000000000..dcd992f8a7 >--- /dev/null >+++ b/drivers/bus/platform/private.h >@@ -0,0 +1,48 @@ >+/* SPDX-License-Identifier: BSD-3-Clause >+ * Copyright(C) 2023 Marvell. >+ */ >+ >+#ifndef _PLATFORM_PRIVATE_H_ >+#define _PLATFORM_PRIVATE_H_ >+ >+#include <bus_driver.h> >+#include <rte_bus.h> >+#include <rte_common.h> >+#include <rte_dev.h> >+#include <rte_log.h> >+#include <rte_os.h> >+ >+#include "bus_platform_driver.h" >+ >+extern struct rte_platform_bus platform_bus; extern int >+platform_bus_logtype; >+ >+/* Platform bus iterators. */ >+#define FOREACH_DEVICE_ON_PLATFORM_BUS(p) \ >+ RTE_TAILQ_FOREACH(p, &(platform_bus.device_list), next) >+ >+#define FOREACH_DRIVER_ON_PLATFORM_BUS(p) \ >+ RTE_TAILQ_FOREACH(p, &(platform_bus.driver_list), next) >+ >+/* >+ * Structure describing platform bus. >+ */ >+struct rte_platform_bus { >+ struct rte_bus bus; /* Core bus */ >+ RTE_TAILQ_HEAD(, rte_platform_device) device_list; /* List of bus >devices */ >+ RTE_TAILQ_HEAD(, rte_platform_driver) driver_list; /* List of bus >+drivers */ }; >+ >+#define PLATFORM_LOG(level, ...) \ >+ rte_log(RTE_LOG_ ## level, platform_bus_logtype, \ >+ RTE_FMT("platform bus: " RTE_FMT_HEAD(__VA_ARGS__,), \ >+ RTE_FMT_TAIL(__VA_ARGS__,))) >+ >+/* >+ * Iterate registered platform devices and find one that matches provided >string. >+ */ >+void * >+platform_bus_dev_iterate(const void *start, const char *str, >+ const struct rte_dev_iterator *it __rte_unused); >+ >+#endif /* _PLATFORM_PRIVATE_H_ */ >diff --git a/drivers/bus/platform/version.map >b/drivers/bus/platform/version.map >new file mode 100644 >index 0000000000..bacce4da08 >--- /dev/null >+++ b/drivers/bus/platform/version.map >@@ -0,0 +1,10 @@ >+DPDK_23 { >+ local: *; >+}; >+ >+INTERNAL { >+ global: >+ >+ rte_platform_register; >+ rte_platform_unregister; >+}; >-- >2.34.1