[v2] bus: add platform bus

Message ID 20230205183832.842668-1-tduszynski@marvell.com (mailing list archive)
State Changes Requested, archived
Delegated to: Thomas Monjalon
Headers
Series [v2] bus: add platform bus |

Checks

Context Check Description
ci/checkpatch warning coding style issues
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/iol-mellanox-Performance success Performance Testing PASS
ci/iol-broadcom-Functional success Functional Testing PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/github-robot: build success github build: passed
ci/iol-aarch64-unit-testing success Testing PASS
ci/iol-testing success Testing PASS
ci/iol-x86_64-unit-testing success Testing PASS
ci/Intel-compilation success Compilation OK
ci/intel-Testing success Testing PASS

Commit Message

Tomasz Duszynski Feb. 5, 2023, 6:38 p.m. UTC
  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 <tduszynski@marvell.com>
---
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/rel_notes/release_23_03.rst     |   5 +
 drivers/bus/meson.build                    |   1 +
 drivers/bus/platform/bus_platform_driver.h | 192 +++++++
 drivers/bus/platform/meson.build           |  16 +
 drivers/bus/platform/platform.c            | 632 +++++++++++++++++++++
 drivers/bus/platform/platform_params.c     |  74 +++
 drivers/bus/platform/private.h             |  48 ++
 drivers/bus/platform/version.map           |  10 +
 9 files changed, 982 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

--
2.34.1
  

Comments

David Marchand Feb. 6, 2023, 2:28 p.m. UTC | #1
On Sun, Feb 5, 2023 at 7:39 PM Tomasz Duszynski <tduszynski@marvell.com> wrote:
>
> 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 <tduszynski@marvell.com>
> ---
> 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

I must be missing something, but in the form proposed in v2, this
won't work, see below.

[snip]


> diff --git a/drivers/bus/platform/platform.c b/drivers/bus/platform/platform.c
> new file mode 100644
> index 0000000000..9f06327b77
> --- /dev/null
> +++ b/drivers/bus/platform/platform.c
> @@ -0,0 +1,632 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(C) 2023 Marvell.
> + */
> +
> +#ifdef VFIO_PRESENT
> +

VFIO_PRESENT is defined in rte_vfio.h and nothing else defines it.
So the rest of this file is a noop.
What did I miss?

So either you need to include <rte_vfio.h> first, or we could move the
VFIO_PRESENT detection stuff from this EAL header, to meson.


I did not look further into this patch, since this added code is
probably not tested (since not compiled).
  
Tomasz Duszynski Feb. 7, 2023, 2:14 p.m. UTC | #2
Hi David, 

>-----Original Message-----
>From: David Marchand <david.marchand@redhat.com>
>Sent: Monday, February 6, 2023 3:29 PM
>To: Tomasz Duszynski <tduszynski@marvell.com>
>Cc: dev@dpdk.org; Thomas Monjalon <thomas@monjalon.net>; chenbo.xia@intel.com; Jerin Jacob
>Kollanukkaran <jerinj@marvell.com>; stephen@networkplumber.org
>Subject: [EXT] Re: [PATCH v2] bus: add platform bus
>
>External Email
>
>----------------------------------------------------------------------
>On Sun, Feb 5, 2023 at 7:39 PM Tomasz Duszynski <tduszynski@marvell.com> wrote:
>>
>> 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 <tduszynski@marvell.com>
>> ---
>> 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
>
>I must be missing something, but in the form proposed in v2, this won't work, see below.
>
>[snip]
>

No you did not miss anything. Prior to submission I realized that if dpdk uses pretty
old kernel there might be no vfio uapi header present hence added that define. 
Yeah complete no-brainer. Will fix that in v3. 

>
>> diff --git a/drivers/bus/platform/platform.c
>> b/drivers/bus/platform/platform.c new file mode 100644 index
>> 0000000000..9f06327b77
>> --- /dev/null
>> +++ b/drivers/bus/platform/platform.c
>> @@ -0,0 +1,632 @@
>> +/* SPDX-License-Identifier: BSD-3-Clause
>> + * Copyright(C) 2023 Marvell.
>> + */
>> +
>> +#ifdef VFIO_PRESENT
>> +
>
>VFIO_PRESENT is defined in rte_vfio.h and nothing else defines it.
>So the rest of this file is a noop.
>What did I miss?
>
>So either you need to include <rte_vfio.h> first, or we could move the VFIO_PRESENT detection stuff
>from this EAL header, to meson.
>
>
>I did not look further into this patch, since this added code is probably not tested (since not
>compiled).
>
>
>--
>David Marchand
  

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 9a0f416d2e..b02666710c 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 <tduszynski@marvell.com>
+F: drivers/bus/platform
+
 VMBUS bus driver
 M: Long Li <longli@microsoft.com>
 F: drivers/bus/vmbus/
diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index 6c42c27886..c7ff097c94 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -59,6 +59,11 @@  New Features

   * Added multi-process support.

+* **Added platform bus support.**
+
+  A platform bus provides a way to use Linux platform devices which
+  are compatible with vfio-platform kernel driver.
+
 * **Updated Corigine nfp driver.**

   * Added support for meter options.
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..c8c074f3e8
--- /dev/null
+++ b/drivers/bus/platform/bus_platform_driver.h
@@ -0,0 +1,192 @@ 
+/* 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>
+
+/* 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..9f06327b77
--- /dev/null
+++ b/drivers/bus/platform/platform.c
@@ -0,0 +1,632 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#ifdef VFIO_PRESENT
+
+#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"
+
+#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_PRIVATE, 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, &reg_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;
+	}
+
+	if (!(dev_info.flags & VFIO_DEVICE_FLAGS_PLATFORM)) {
+		PLATFORM_LOG(ERR, "device not backed by vfio-platform\n");
+		ret = -ENOTSUP;
+		goto out;
+	}
+
+	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) {
+		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;
+	const char **out = addr;
+
+	FOREACH_DEVICE_ON_PLATFORM_BUS(pdev) {
+		if (!strcmp(name, pdev->name))
+			break;
+	}
+
+	if (pdev && addr)
+		*out = name;
+
+	return pdev ? 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_bus, 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..269683952a
--- /dev/null
+++ b/drivers/bus/platform/platform_params.c
@@ -0,0 +1,74 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#ifdef VFIO_PRESENT
+
+#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 "bus_platform_driver.h"
+#include "private.h"
+
+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;
+};