xref: /dpdk/drivers/bus/platform/platform.c (revision e99981af34632ecce3bac82d05db97b08308f9b5)
117c839f7STomasz Duszynski /* SPDX-License-Identifier: BSD-3-Clause
217c839f7STomasz Duszynski  * Copyright(C) 2023 Marvell.
317c839f7STomasz Duszynski  */
417c839f7STomasz Duszynski 
517c839f7STomasz Duszynski #include <dirent.h>
617c839f7STomasz Duszynski #include <inttypes.h>
717c839f7STomasz Duszynski #include <stdlib.h>
817c839f7STomasz Duszynski #include <string.h>
917c839f7STomasz Duszynski #include <sys/ioctl.h>
1017c839f7STomasz Duszynski #include <sys/mman.h>
1117c839f7STomasz Duszynski #include <sys/queue.h>
1217c839f7STomasz Duszynski #include <unistd.h>
1317c839f7STomasz Duszynski 
1417c839f7STomasz Duszynski #include <bus_driver.h>
1517c839f7STomasz Duszynski #include <bus_platform_driver.h>
1617c839f7STomasz Duszynski #include <eal_filesystem.h>
1717c839f7STomasz Duszynski #include <rte_bus.h>
1817c839f7STomasz Duszynski #include <rte_devargs.h>
1917c839f7STomasz Duszynski #include <rte_errno.h>
2017c839f7STomasz Duszynski #include <rte_log.h>
2117c839f7STomasz Duszynski #include <rte_memory.h>
2217c839f7STomasz Duszynski #include <rte_string_fns.h>
2317c839f7STomasz Duszynski #include <rte_vfio.h>
2417c839f7STomasz Duszynski 
2517c839f7STomasz Duszynski #include "private.h"
2617c839f7STomasz Duszynski 
2717c839f7STomasz Duszynski #ifdef VFIO_PRESENT
2817c839f7STomasz Duszynski 
2917c839f7STomasz Duszynski #define PLATFORM_BUS_DEVICES_PATH "/sys/bus/platform/devices"
3017c839f7STomasz Duszynski 
3117c839f7STomasz Duszynski void
3217c839f7STomasz Duszynski rte_platform_register(struct rte_platform_driver *pdrv)
3317c839f7STomasz Duszynski {
3417c839f7STomasz Duszynski 	TAILQ_INSERT_TAIL(&platform_bus.driver_list, pdrv, next);
3517c839f7STomasz Duszynski }
3617c839f7STomasz Duszynski 
3717c839f7STomasz Duszynski void
3817c839f7STomasz Duszynski rte_platform_unregister(struct rte_platform_driver *pdrv)
3917c839f7STomasz Duszynski {
4017c839f7STomasz Duszynski 	TAILQ_REMOVE(&platform_bus.driver_list, pdrv, next);
4117c839f7STomasz Duszynski }
4217c839f7STomasz Duszynski 
4317c839f7STomasz Duszynski static struct rte_devargs *
4417c839f7STomasz Duszynski dev_devargs(const char *dev_name)
4517c839f7STomasz Duszynski {
4617c839f7STomasz Duszynski 	struct rte_devargs *devargs;
4717c839f7STomasz Duszynski 
4817c839f7STomasz Duszynski 	RTE_EAL_DEVARGS_FOREACH("platform", devargs) {
4917c839f7STomasz Duszynski 		if (!strcmp(devargs->name, dev_name))
5017c839f7STomasz Duszynski 			return devargs;
5117c839f7STomasz Duszynski 	}
5217c839f7STomasz Duszynski 
5317c839f7STomasz Duszynski 	return NULL;
5417c839f7STomasz Duszynski }
5517c839f7STomasz Duszynski 
5617c839f7STomasz Duszynski static bool
5717c839f7STomasz Duszynski dev_allowed(const char *dev_name)
5817c839f7STomasz Duszynski {
5917c839f7STomasz Duszynski 	struct rte_devargs *devargs;
6017c839f7STomasz Duszynski 
6117c839f7STomasz Duszynski 	devargs = dev_devargs(dev_name);
6217c839f7STomasz Duszynski 	if (devargs == NULL)
6317c839f7STomasz Duszynski 		return true;
6417c839f7STomasz Duszynski 
6517c839f7STomasz Duszynski 	switch (platform_bus.bus.conf.scan_mode) {
6617c839f7STomasz Duszynski 	case RTE_BUS_SCAN_UNDEFINED:
6717c839f7STomasz Duszynski 	case RTE_BUS_SCAN_ALLOWLIST:
6817c839f7STomasz Duszynski 		if (devargs->policy == RTE_DEV_ALLOWED)
6917c839f7STomasz Duszynski 			return true;
7017c839f7STomasz Duszynski 		break;
7117c839f7STomasz Duszynski 	case RTE_BUS_SCAN_BLOCKLIST:
7217c839f7STomasz Duszynski 		if (devargs->policy == RTE_DEV_BLOCKED)
7317c839f7STomasz Duszynski 			return false;
7417c839f7STomasz Duszynski 		break;
7517c839f7STomasz Duszynski 	}
7617c839f7STomasz Duszynski 
7717c839f7STomasz Duszynski 	return true;
7817c839f7STomasz Duszynski }
7917c839f7STomasz Duszynski 
8017c839f7STomasz Duszynski static int
8117c839f7STomasz Duszynski dev_add(const char *dev_name)
8217c839f7STomasz Duszynski {
8317c839f7STomasz Duszynski 	struct rte_platform_device *pdev, *tmp;
8417c839f7STomasz Duszynski 	char path[PATH_MAX];
8517c839f7STomasz Duszynski 	unsigned long val;
8617c839f7STomasz Duszynski 
8717c839f7STomasz Duszynski 	pdev = calloc(1, sizeof(*pdev));
8817c839f7STomasz Duszynski 	if (pdev == NULL)
8917c839f7STomasz Duszynski 		return -ENOMEM;
9017c839f7STomasz Duszynski 
9117c839f7STomasz Duszynski 	rte_strscpy(pdev->name, dev_name, sizeof(pdev->name));
9217c839f7STomasz Duszynski 	pdev->device.name = pdev->name;
9317c839f7STomasz Duszynski 	pdev->device.devargs = dev_devargs(dev_name);
9417c839f7STomasz Duszynski 	pdev->device.bus = &platform_bus.bus;
9517c839f7STomasz Duszynski 	snprintf(path, sizeof(path), PLATFORM_BUS_DEVICES_PATH "/%s/numa_node", dev_name);
9617c839f7STomasz Duszynski 	pdev->device.numa_node = eal_parse_sysfs_value(path, &val) ? rte_socket_id() : val;
9717c839f7STomasz Duszynski 
9817c839f7STomasz Duszynski 	FOREACH_DEVICE_ON_PLATFORM_BUS(tmp) {
9917c839f7STomasz Duszynski 		if (!strcmp(tmp->name, pdev->name)) {
100*e99981afSDavid Marchand 			PLATFORM_LOG_LINE(INFO, "device %s already added", pdev->name);
10117c839f7STomasz Duszynski 
10217c839f7STomasz Duszynski 			if (tmp->device.devargs != pdev->device.devargs)
10317c839f7STomasz Duszynski 				rte_devargs_remove(pdev->device.devargs);
10417c839f7STomasz Duszynski 
10517c839f7STomasz Duszynski 			free(pdev);
106bb1f2f5bSTomasz Duszynski 			return -EEXIST;
10717c839f7STomasz Duszynski 		}
10817c839f7STomasz Duszynski 	}
10917c839f7STomasz Duszynski 
11017c839f7STomasz Duszynski 	TAILQ_INSERT_HEAD(&platform_bus.device_list, pdev, next);
11117c839f7STomasz Duszynski 
112*e99981afSDavid Marchand 	PLATFORM_LOG_LINE(INFO, "adding device %s to the list", dev_name);
11317c839f7STomasz Duszynski 
11417c839f7STomasz Duszynski 	return 0;
11517c839f7STomasz Duszynski }
11617c839f7STomasz Duszynski 
11717c839f7STomasz Duszynski static char *
11817c839f7STomasz Duszynski dev_kernel_driver_name(const char *dev_name)
11917c839f7STomasz Duszynski {
12017c839f7STomasz Duszynski 	char path[PATH_MAX], buf[BUFSIZ] = { };
12117c839f7STomasz Duszynski 	char *kdrv;
12217c839f7STomasz Duszynski 	int ret;
12317c839f7STomasz Duszynski 
12417c839f7STomasz Duszynski 	snprintf(path, sizeof(path), PLATFORM_BUS_DEVICES_PATH "/%s/driver", dev_name);
12517c839f7STomasz Duszynski 	/* save space for NUL */
12617c839f7STomasz Duszynski 	ret = readlink(path, buf, sizeof(buf) - 1);
12717c839f7STomasz Duszynski 	if (ret <= 0)
12817c839f7STomasz Duszynski 		return NULL;
12917c839f7STomasz Duszynski 
13017c839f7STomasz Duszynski 	/* last token is kernel driver name */
13117c839f7STomasz Duszynski 	kdrv = strrchr(buf, '/');
13217c839f7STomasz Duszynski 	if (kdrv != NULL)
13317c839f7STomasz Duszynski 		return strdup(kdrv + 1);
13417c839f7STomasz Duszynski 
13517c839f7STomasz Duszynski 	return NULL;
13617c839f7STomasz Duszynski }
13717c839f7STomasz Duszynski 
13817c839f7STomasz Duszynski static bool
13917c839f7STomasz Duszynski dev_is_bound_vfio_platform(const char *dev_name)
14017c839f7STomasz Duszynski {
14117c839f7STomasz Duszynski 	char *kdrv;
14217c839f7STomasz Duszynski 	int ret;
14317c839f7STomasz Duszynski 
14417c839f7STomasz Duszynski 	kdrv = dev_kernel_driver_name(dev_name);
14517c839f7STomasz Duszynski 	if (!kdrv)
14617c839f7STomasz Duszynski 		return false;
14717c839f7STomasz Duszynski 
14817c839f7STomasz Duszynski 	ret = strcmp(kdrv, "vfio-platform");
14917c839f7STomasz Duszynski 	free(kdrv);
15017c839f7STomasz Duszynski 
15117c839f7STomasz Duszynski 	return ret == 0;
15217c839f7STomasz Duszynski }
15317c839f7STomasz Duszynski 
15417c839f7STomasz Duszynski static int
15517c839f7STomasz Duszynski platform_bus_scan(void)
15617c839f7STomasz Duszynski {
15717c839f7STomasz Duszynski 	const struct dirent *ent;
15817c839f7STomasz Duszynski 	const char *dev_name;
15917c839f7STomasz Duszynski 	int ret = 0;
16017c839f7STomasz Duszynski 	DIR *dp;
16117c839f7STomasz Duszynski 
16217c839f7STomasz Duszynski 	dp = opendir(PLATFORM_BUS_DEVICES_PATH);
16317c839f7STomasz Duszynski 	if (dp == NULL) {
164*e99981afSDavid Marchand 		PLATFORM_LOG_LINE(INFO, "failed to open %s", PLATFORM_BUS_DEVICES_PATH);
16517c839f7STomasz Duszynski 		return -errno;
16617c839f7STomasz Duszynski 	}
16717c839f7STomasz Duszynski 
16817c839f7STomasz Duszynski 	while ((ent = readdir(dp))) {
16917c839f7STomasz Duszynski 		dev_name = ent->d_name;
17017c839f7STomasz Duszynski 		if (dev_name[0] == '.')
17117c839f7STomasz Duszynski 			continue;
17217c839f7STomasz Duszynski 
17317c839f7STomasz Duszynski 		if (!dev_allowed(dev_name))
17417c839f7STomasz Duszynski 			continue;
17517c839f7STomasz Duszynski 
17617c839f7STomasz Duszynski 		if (!dev_is_bound_vfio_platform(dev_name))
17717c839f7STomasz Duszynski 			continue;
17817c839f7STomasz Duszynski 
17917c839f7STomasz Duszynski 		ret = dev_add(dev_name);
18017c839f7STomasz Duszynski 		if (ret)
18117c839f7STomasz Duszynski 			break;
18217c839f7STomasz Duszynski 	}
18317c839f7STomasz Duszynski 
18417c839f7STomasz Duszynski 	closedir(dp);
18517c839f7STomasz Duszynski 
18617c839f7STomasz Duszynski 	return ret;
18717c839f7STomasz Duszynski }
18817c839f7STomasz Duszynski 
18917c839f7STomasz Duszynski static int
19017c839f7STomasz Duszynski device_map_resource_offset(struct rte_platform_device *pdev, struct rte_platform_resource *res,
19117c839f7STomasz Duszynski 			   size_t offset)
19217c839f7STomasz Duszynski {
19317c839f7STomasz Duszynski 	res->mem.addr = mmap(NULL, res->mem.len, PROT_READ | PROT_WRITE, MAP_SHARED, pdev->dev_fd,
19417c839f7STomasz Duszynski 			     offset);
19517c839f7STomasz Duszynski 	if (res->mem.addr == MAP_FAILED)
19617c839f7STomasz Duszynski 		return -errno;
19717c839f7STomasz Duszynski 
198*e99981afSDavid Marchand 	PLATFORM_LOG_LINE(DEBUG, "adding resource va = %p len = %"PRIu64" name = %s", res->mem.addr,
19917c839f7STomasz Duszynski 		     res->mem.len, res->name);
20017c839f7STomasz Duszynski 
20117c839f7STomasz Duszynski 	return 0;
20217c839f7STomasz Duszynski }
20317c839f7STomasz Duszynski 
20417c839f7STomasz Duszynski static void
20517c839f7STomasz Duszynski device_unmap_resources(struct rte_platform_device *pdev)
20617c839f7STomasz Duszynski {
20717c839f7STomasz Duszynski 	struct rte_platform_resource *res;
20817c839f7STomasz Duszynski 	unsigned int i;
20917c839f7STomasz Duszynski 
21017c839f7STomasz Duszynski 	for (i = 0; i < pdev->num_resource; i++) {
21117c839f7STomasz Duszynski 		res = &pdev->resource[i];
21217c839f7STomasz Duszynski 		munmap(res->mem.addr, res->mem.len);
21317c839f7STomasz Duszynski 		free(res->name);
21417c839f7STomasz Duszynski 	}
21517c839f7STomasz Duszynski 
21617c839f7STomasz Duszynski 	free(pdev->resource);
21717c839f7STomasz Duszynski 	pdev->resource = NULL;
21817c839f7STomasz Duszynski 	pdev->num_resource = 0;
21917c839f7STomasz Duszynski }
22017c839f7STomasz Duszynski 
22117c839f7STomasz Duszynski static int
22217c839f7STomasz Duszynski read_sysfs_string(const char *path, char *buf, size_t size)
22317c839f7STomasz Duszynski {
22417c839f7STomasz Duszynski 	FILE *f;
22517c839f7STomasz Duszynski 	char *p;
22617c839f7STomasz Duszynski 
22717c839f7STomasz Duszynski 	f = fopen(path, "r");
22817c839f7STomasz Duszynski 	if (f == NULL)
22917c839f7STomasz Duszynski 		return -errno;
23017c839f7STomasz Duszynski 
23117c839f7STomasz Duszynski 	if (fgets(buf, size, f) == NULL) {
23217c839f7STomasz Duszynski 		fclose(f);
23317c839f7STomasz Duszynski 		return -ENODATA;
23417c839f7STomasz Duszynski 	}
23517c839f7STomasz Duszynski 
23617c839f7STomasz Duszynski 	fclose(f);
23717c839f7STomasz Duszynski 
23817c839f7STomasz Duszynski 	p = strrchr(buf, '\n');
23917c839f7STomasz Duszynski 	if (p != NULL)
24017c839f7STomasz Duszynski 		*p = '\0';
24117c839f7STomasz Duszynski 
24217c839f7STomasz Duszynski 	return 0;
24317c839f7STomasz Duszynski }
24417c839f7STomasz Duszynski 
24517c839f7STomasz Duszynski static char *
24617c839f7STomasz Duszynski of_resource_name(const char *dev_name, int index)
24717c839f7STomasz Duszynski {
24817c839f7STomasz Duszynski 	char path[PATH_MAX], buf[BUFSIZ] = { };
24917c839f7STomasz Duszynski 	int num = 0, ret;
25017c839f7STomasz Duszynski 	char *name;
25117c839f7STomasz Duszynski 
25217c839f7STomasz Duszynski 	snprintf(path, sizeof(path), PLATFORM_BUS_DEVICES_PATH "/%s/of_node/reg-names", dev_name);
25399b4e1b8STomasz Duszynski 	ret = read_sysfs_string(path, buf, sizeof(buf) - 1);
25417c839f7STomasz Duszynski 	if (ret)
25517c839f7STomasz Duszynski 		return NULL;
25617c839f7STomasz Duszynski 
25799b4e1b8STomasz Duszynski 	for (name = buf; *name != 0; name += strlen(name) + 1) {
25817c839f7STomasz Duszynski 		if (num++ != index)
25917c839f7STomasz Duszynski 			continue;
26017c839f7STomasz Duszynski 		return strdup(name);
26117c839f7STomasz Duszynski 	}
26217c839f7STomasz Duszynski 
26317c839f7STomasz Duszynski 	return NULL;
26417c839f7STomasz Duszynski }
26517c839f7STomasz Duszynski 
26617c839f7STomasz Duszynski static int
26717c839f7STomasz Duszynski device_map_resources(struct rte_platform_device *pdev, unsigned int num)
26817c839f7STomasz Duszynski {
26917c839f7STomasz Duszynski 	struct rte_platform_resource *res;
27017c839f7STomasz Duszynski 	unsigned int i;
27117c839f7STomasz Duszynski 	int ret;
27217c839f7STomasz Duszynski 
2735a3320cbSTomasz Duszynski 	if (num == 0) {
274*e99981afSDavid Marchand 		PLATFORM_LOG_LINE(WARNING, "device %s has no resources", pdev->name);
2755a3320cbSTomasz Duszynski 		return 0;
2765a3320cbSTomasz Duszynski 	}
27717c839f7STomasz Duszynski 
27817c839f7STomasz Duszynski 	pdev->resource = calloc(num, sizeof(*pdev->resource));
27917c839f7STomasz Duszynski 	if (pdev->resource == NULL)
28017c839f7STomasz Duszynski 		return -ENOMEM;
28117c839f7STomasz Duszynski 
28217c839f7STomasz Duszynski 	for (i = 0; i < num; i++) {
28317c839f7STomasz Duszynski 		struct vfio_region_info reg_info = {
28417c839f7STomasz Duszynski 			.argsz = sizeof(reg_info),
28517c839f7STomasz Duszynski 			.index = i,
28617c839f7STomasz Duszynski 		};
28717c839f7STomasz Duszynski 
28817c839f7STomasz Duszynski 		ret = ioctl(pdev->dev_fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info);
28917c839f7STomasz Duszynski 		if (ret) {
290*e99981afSDavid Marchand 			PLATFORM_LOG_LINE(ERR, "failed to get region info at %d", i);
29117c839f7STomasz Duszynski 			ret = -errno;
29217c839f7STomasz Duszynski 			goto out;
29317c839f7STomasz Duszynski 		}
29417c839f7STomasz Duszynski 
29517c839f7STomasz Duszynski 		res = &pdev->resource[i];
29617c839f7STomasz Duszynski 		res->name = of_resource_name(pdev->name, reg_info.index);
29717c839f7STomasz Duszynski 		res->mem.len = reg_info.size;
29817c839f7STomasz Duszynski 		ret = device_map_resource_offset(pdev, res, reg_info.offset);
29917c839f7STomasz Duszynski 		if (ret) {
300*e99981afSDavid Marchand 			PLATFORM_LOG_LINE(ERR, "failed to ioremap resource at %d", i);
30117c839f7STomasz Duszynski 			goto out;
30217c839f7STomasz Duszynski 		}
30317c839f7STomasz Duszynski 
30417c839f7STomasz Duszynski 		pdev->num_resource++;
30517c839f7STomasz Duszynski 	}
30617c839f7STomasz Duszynski 
30717c839f7STomasz Duszynski 	return 0;
30817c839f7STomasz Duszynski out:
30917c839f7STomasz Duszynski 	device_unmap_resources(pdev);
31017c839f7STomasz Duszynski 
31117c839f7STomasz Duszynski 	return ret;
31217c839f7STomasz Duszynski }
31317c839f7STomasz Duszynski 
31417c839f7STomasz Duszynski static void
31517c839f7STomasz Duszynski device_cleanup(struct rte_platform_device *pdev)
31617c839f7STomasz Duszynski {
31717c839f7STomasz Duszynski 	device_unmap_resources(pdev);
31817c839f7STomasz Duszynski 	rte_vfio_release_device(PLATFORM_BUS_DEVICES_PATH, pdev->name, pdev->dev_fd);
31917c839f7STomasz Duszynski }
32017c839f7STomasz Duszynski 
32117c839f7STomasz Duszynski static int
32217c839f7STomasz Duszynski device_setup(struct rte_platform_device *pdev)
32317c839f7STomasz Duszynski {
32417c839f7STomasz Duszynski 	struct vfio_device_info dev_info = { .argsz = sizeof(dev_info), };
32517c839f7STomasz Duszynski 	const char *name = pdev->name;
32617c839f7STomasz Duszynski 	int ret;
32717c839f7STomasz Duszynski 
32817c839f7STomasz Duszynski 	ret = rte_vfio_setup_device(PLATFORM_BUS_DEVICES_PATH, name, &pdev->dev_fd, &dev_info);
32917c839f7STomasz Duszynski 	if (ret) {
330*e99981afSDavid Marchand 		PLATFORM_LOG_LINE(ERR, "failed to setup %s", name);
33117c839f7STomasz Duszynski 		return -ENODEV;
33217c839f7STomasz Duszynski 	}
33317c839f7STomasz Duszynski 
33417c839f7STomasz Duszynski 	/* This is an extra check to confirm that platform device was initialized
33517c839f7STomasz Duszynski 	 * by a kernel vfio-platform driver. On kernels that predate vfio-platform
33617c839f7STomasz Duszynski 	 * driver this flag obviously does not exist. In such scenarios this
33717c839f7STomasz Duszynski 	 * check needs to be removed otherwise compilation fails.
33817c839f7STomasz Duszynski 	 *
33917c839f7STomasz Duszynski 	 * Now, on such old kernels code will never reach here because
34017c839f7STomasz Duszynski 	 * there is another check much earlier which verifies whether
34117c839f7STomasz Duszynski 	 * device has been bound to vfio-platform driver.
34217c839f7STomasz Duszynski 	 */
34317c839f7STomasz Duszynski #ifdef VFIO_DEVICE_FLAGS_PLATFORM
34417c839f7STomasz Duszynski 	if (!(dev_info.flags & VFIO_DEVICE_FLAGS_PLATFORM)) {
345*e99981afSDavid Marchand 		PLATFORM_LOG_LINE(ERR, "device not backed by vfio-platform");
34617c839f7STomasz Duszynski 		ret = -ENOTSUP;
34717c839f7STomasz Duszynski 		goto out;
34817c839f7STomasz Duszynski 	}
34917c839f7STomasz Duszynski #endif
35017c839f7STomasz Duszynski 
35117c839f7STomasz Duszynski 	ret = device_map_resources(pdev, dev_info.num_regions);
35217c839f7STomasz Duszynski 	if (ret) {
353*e99981afSDavid Marchand 		PLATFORM_LOG_LINE(ERR, "failed to setup platform resources");
35417c839f7STomasz Duszynski 		goto out;
35517c839f7STomasz Duszynski 	}
35617c839f7STomasz Duszynski 
35717c839f7STomasz Duszynski 	return 0;
35817c839f7STomasz Duszynski out:
35917c839f7STomasz Duszynski 	device_cleanup(pdev);
36017c839f7STomasz Duszynski 
36117c839f7STomasz Duszynski 	return ret;
36217c839f7STomasz Duszynski }
36317c839f7STomasz Duszynski 
36417c839f7STomasz Duszynski static int
36517c839f7STomasz Duszynski driver_call_probe(struct rte_platform_driver *pdrv, struct rte_platform_device *pdev)
36617c839f7STomasz Duszynski {
36717c839f7STomasz Duszynski 	int ret;
36817c839f7STomasz Duszynski 
36917c839f7STomasz Duszynski 	if (rte_dev_is_probed(&pdev->device))
37017c839f7STomasz Duszynski 		return -EBUSY;
37117c839f7STomasz Duszynski 
37217c839f7STomasz Duszynski 	if (pdrv->probe != NULL) {
37317c839f7STomasz Duszynski 		pdev->driver = pdrv;
37417c839f7STomasz Duszynski 		ret = pdrv->probe(pdev);
37517c839f7STomasz Duszynski 		if (ret)
37617c839f7STomasz Duszynski 			return ret;
37717c839f7STomasz Duszynski 	}
37817c839f7STomasz Duszynski 
37917c839f7STomasz Duszynski 	pdev->device.driver = &pdrv->driver;
38017c839f7STomasz Duszynski 
38117c839f7STomasz Duszynski 	return 0;
38217c839f7STomasz Duszynski }
38317c839f7STomasz Duszynski 
38417c839f7STomasz Duszynski static int
38517c839f7STomasz Duszynski driver_probe_device(struct rte_platform_driver *pdrv, struct rte_platform_device *pdev)
38617c839f7STomasz Duszynski {
38717c839f7STomasz Duszynski 	enum rte_iova_mode iova_mode;
38817c839f7STomasz Duszynski 	int ret;
38917c839f7STomasz Duszynski 
39017c839f7STomasz Duszynski 	iova_mode = rte_eal_iova_mode();
39117c839f7STomasz Duszynski 	if (pdrv->drv_flags & RTE_PLATFORM_DRV_NEED_IOVA_AS_VA && iova_mode != RTE_IOVA_VA) {
392*e99981afSDavid Marchand 		PLATFORM_LOG_LINE(ERR, "driver %s expects VA IOVA mode but current mode is PA",
39317c839f7STomasz Duszynski 			     pdrv->driver.name);
39417c839f7STomasz Duszynski 		return -EINVAL;
39517c839f7STomasz Duszynski 	}
39617c839f7STomasz Duszynski 
39717c839f7STomasz Duszynski 	ret = device_setup(pdev);
39817c839f7STomasz Duszynski 	if (ret)
39917c839f7STomasz Duszynski 		return ret;
40017c839f7STomasz Duszynski 
40117c839f7STomasz Duszynski 	ret = driver_call_probe(pdrv, pdev);
40217c839f7STomasz Duszynski 	if (ret)
40317c839f7STomasz Duszynski 		device_cleanup(pdev);
40417c839f7STomasz Duszynski 
40517c839f7STomasz Duszynski 	return ret;
40617c839f7STomasz Duszynski }
40717c839f7STomasz Duszynski 
40817c839f7STomasz Duszynski static bool
40917c839f7STomasz Duszynski driver_match_device(struct rte_platform_driver *pdrv, struct rte_platform_device *pdev)
41017c839f7STomasz Duszynski {
41117c839f7STomasz Duszynski 	bool match = false;
41217c839f7STomasz Duszynski 	char *kdrv;
41317c839f7STomasz Duszynski 
41417c839f7STomasz Duszynski 	kdrv = dev_kernel_driver_name(pdev->name);
41517c839f7STomasz Duszynski 	if (!kdrv)
41617c839f7STomasz Duszynski 		return false;
41717c839f7STomasz Duszynski 
41817c839f7STomasz Duszynski 	/* match by driver name */
41917c839f7STomasz Duszynski 	if (!strcmp(kdrv, pdrv->driver.name)) {
42017c839f7STomasz Duszynski 		match = true;
42117c839f7STomasz Duszynski 		goto out;
42217c839f7STomasz Duszynski 	}
42317c839f7STomasz Duszynski 
42417c839f7STomasz Duszynski 	/* match by driver alias */
42517c839f7STomasz Duszynski 	if (pdrv->driver.alias != NULL && !strcmp(kdrv, pdrv->driver.alias)) {
42617c839f7STomasz Duszynski 		match = true;
42717c839f7STomasz Duszynski 		goto out;
42817c839f7STomasz Duszynski 	}
42917c839f7STomasz Duszynski 
43017c839f7STomasz Duszynski 	/* match by device name */
43117c839f7STomasz Duszynski 	if (!strcmp(pdev->name, pdrv->driver.name))
43217c839f7STomasz Duszynski 		match = true;
43317c839f7STomasz Duszynski 
43417c839f7STomasz Duszynski out:
43517c839f7STomasz Duszynski 	free(kdrv);
43617c839f7STomasz Duszynski 
43717c839f7STomasz Duszynski 	return match;
43817c839f7STomasz Duszynski }
43917c839f7STomasz Duszynski 
44017c839f7STomasz Duszynski static int
44117c839f7STomasz Duszynski device_attach(struct rte_platform_device *pdev)
44217c839f7STomasz Duszynski {
44317c839f7STomasz Duszynski 	struct rte_platform_driver *pdrv;
44417c839f7STomasz Duszynski 
44517c839f7STomasz Duszynski 	FOREACH_DRIVER_ON_PLATFORM_BUS(pdrv) {
44617c839f7STomasz Duszynski 		if (driver_match_device(pdrv, pdev))
44717c839f7STomasz Duszynski 			break;
44817c839f7STomasz Duszynski 	}
44917c839f7STomasz Duszynski 
45017c839f7STomasz Duszynski 	if (pdrv == NULL)
45117c839f7STomasz Duszynski 		return -ENODEV;
45217c839f7STomasz Duszynski 
45317c839f7STomasz Duszynski 	return driver_probe_device(pdrv, pdev);
45417c839f7STomasz Duszynski }
45517c839f7STomasz Duszynski 
45617c839f7STomasz Duszynski static int
45717c839f7STomasz Duszynski platform_bus_probe(void)
45817c839f7STomasz Duszynski {
45917c839f7STomasz Duszynski 	struct rte_platform_device *pdev;
46017c839f7STomasz Duszynski 	int ret;
46117c839f7STomasz Duszynski 
46217c839f7STomasz Duszynski 	FOREACH_DEVICE_ON_PLATFORM_BUS(pdev) {
46317c839f7STomasz Duszynski 		ret = device_attach(pdev);
46417c839f7STomasz Duszynski 		if (ret == -EBUSY) {
465*e99981afSDavid Marchand 			PLATFORM_LOG_LINE(DEBUG, "device %s already probed", pdev->name);
46617c839f7STomasz Duszynski 			continue;
46717c839f7STomasz Duszynski 		}
46817c839f7STomasz Duszynski 		if (ret)
469*e99981afSDavid Marchand 			PLATFORM_LOG_LINE(ERR, "failed to probe %s", pdev->name);
47017c839f7STomasz Duszynski 	}
47117c839f7STomasz Duszynski 
47217c839f7STomasz Duszynski 	return 0;
47317c839f7STomasz Duszynski }
47417c839f7STomasz Duszynski 
47517c839f7STomasz Duszynski static struct rte_device *
47617c839f7STomasz Duszynski platform_bus_find_device(const struct rte_device *start, rte_dev_cmp_t cmp, const void *data)
47717c839f7STomasz Duszynski {
47817c839f7STomasz Duszynski 	struct rte_platform_device *pdev;
47917c839f7STomasz Duszynski 
48017c839f7STomasz Duszynski 	pdev = start ? RTE_TAILQ_NEXT(RTE_DEV_TO_PLATFORM_DEV_CONST(start), next) :
48117c839f7STomasz Duszynski 		       RTE_TAILQ_FIRST(&platform_bus.device_list);
48217c839f7STomasz Duszynski 	while (pdev) {
48317c839f7STomasz Duszynski 		if (cmp(&pdev->device, data) == 0)
48417c839f7STomasz Duszynski 			return &pdev->device;
48517c839f7STomasz Duszynski 
48617c839f7STomasz Duszynski 		pdev = RTE_TAILQ_NEXT(pdev, next);
48717c839f7STomasz Duszynski 	}
48817c839f7STomasz Duszynski 
48917c839f7STomasz Duszynski 	return NULL;
49017c839f7STomasz Duszynski }
49117c839f7STomasz Duszynski 
49217c839f7STomasz Duszynski static int
49317c839f7STomasz Duszynski platform_bus_plug(struct rte_device *dev)
49417c839f7STomasz Duszynski {
49517c839f7STomasz Duszynski 	struct rte_platform_device *pdev;
49617c839f7STomasz Duszynski 
49717c839f7STomasz Duszynski 	if (!dev_allowed(dev->name))
49817c839f7STomasz Duszynski 		return -EPERM;
49917c839f7STomasz Duszynski 
50017c839f7STomasz Duszynski 	if (!dev_is_bound_vfio_platform(dev->name))
50117c839f7STomasz Duszynski 		return -EPERM;
50217c839f7STomasz Duszynski 
50317c839f7STomasz Duszynski 	pdev = RTE_DEV_TO_PLATFORM_DEV(dev);
50417c839f7STomasz Duszynski 	if (pdev == NULL)
50517c839f7STomasz Duszynski 		return -EINVAL;
50617c839f7STomasz Duszynski 
50717c839f7STomasz Duszynski 	return device_attach(pdev);
50817c839f7STomasz Duszynski }
50917c839f7STomasz Duszynski 
51017c839f7STomasz Duszynski static void
51117c839f7STomasz Duszynski device_release_driver(struct rte_platform_device *pdev)
51217c839f7STomasz Duszynski {
51317c839f7STomasz Duszynski 	struct rte_platform_driver *pdrv;
51417c839f7STomasz Duszynski 	int ret;
51517c839f7STomasz Duszynski 
51617c839f7STomasz Duszynski 	pdrv = pdev->driver;
51717c839f7STomasz Duszynski 	if (pdrv != NULL && pdrv->remove != NULL) {
51817c839f7STomasz Duszynski 		ret = pdrv->remove(pdev);
51917c839f7STomasz Duszynski 		if (ret)
520*e99981afSDavid Marchand 			PLATFORM_LOG_LINE(WARNING, "failed to remove %s", pdev->name);
52117c839f7STomasz Duszynski 	}
52217c839f7STomasz Duszynski 
52317c839f7STomasz Duszynski 	pdev->device.driver = NULL;
52417c839f7STomasz Duszynski 	pdev->driver = NULL;
52517c839f7STomasz Duszynski }
52617c839f7STomasz Duszynski 
52717c839f7STomasz Duszynski static int
52817c839f7STomasz Duszynski platform_bus_unplug(struct rte_device *dev)
52917c839f7STomasz Duszynski {
53017c839f7STomasz Duszynski 	struct rte_platform_device *pdev;
53117c839f7STomasz Duszynski 
53217c839f7STomasz Duszynski 	pdev = RTE_DEV_TO_PLATFORM_DEV(dev);
53317c839f7STomasz Duszynski 	if (pdev == NULL)
53417c839f7STomasz Duszynski 		return -EINVAL;
53517c839f7STomasz Duszynski 
53617c839f7STomasz Duszynski 	device_release_driver(pdev);
53717c839f7STomasz Duszynski 	device_cleanup(pdev);
53817c839f7STomasz Duszynski 	rte_devargs_remove(pdev->device.devargs);
53917c839f7STomasz Duszynski 	free(pdev);
54017c839f7STomasz Duszynski 
54117c839f7STomasz Duszynski 	return 0;
54217c839f7STomasz Duszynski }
54317c839f7STomasz Duszynski 
54417c839f7STomasz Duszynski static int
54517c839f7STomasz Duszynski platform_bus_parse(const char *name, void *addr)
54617c839f7STomasz Duszynski {
54717c839f7STomasz Duszynski 	struct rte_platform_device pdev = { };
54817c839f7STomasz Duszynski 	struct rte_platform_driver *pdrv;
54917c839f7STomasz Duszynski 	const char **out = addr;
55017c839f7STomasz Duszynski 
55117c839f7STomasz Duszynski 	rte_strscpy(pdev.name, name, sizeof(pdev.name));
55217c839f7STomasz Duszynski 
55317c839f7STomasz Duszynski 	FOREACH_DRIVER_ON_PLATFORM_BUS(pdrv) {
55417c839f7STomasz Duszynski 		if (driver_match_device(pdrv, &pdev))
55517c839f7STomasz Duszynski 			break;
55617c839f7STomasz Duszynski 	}
55717c839f7STomasz Duszynski 
55817c839f7STomasz Duszynski 	if (pdrv != NULL && addr != NULL)
55917c839f7STomasz Duszynski 		*out = name;
56017c839f7STomasz Duszynski 
56117c839f7STomasz Duszynski 	return pdrv != NULL ? 0 : -ENODEV;
56217c839f7STomasz Duszynski }
56317c839f7STomasz Duszynski 
56417c839f7STomasz Duszynski static int
56517c839f7STomasz Duszynski platform_bus_dma_map(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
56617c839f7STomasz Duszynski {
56717c839f7STomasz Duszynski 	struct rte_platform_device *pdev;
56817c839f7STomasz Duszynski 
56917c839f7STomasz Duszynski 	pdev = RTE_DEV_TO_PLATFORM_DEV(dev);
57017c839f7STomasz Duszynski 	if (pdev == NULL || pdev->driver == NULL) {
57117c839f7STomasz Duszynski 		rte_errno = EINVAL;
57217c839f7STomasz Duszynski 		return -1;
57317c839f7STomasz Duszynski 	}
57417c839f7STomasz Duszynski 
57517c839f7STomasz Duszynski 	if (pdev->driver->dma_map != NULL)
57617c839f7STomasz Duszynski 		return pdev->driver->dma_map(pdev, addr, iova, len);
57717c839f7STomasz Duszynski 
57817c839f7STomasz Duszynski 	return rte_vfio_container_dma_map(RTE_VFIO_DEFAULT_CONTAINER_FD, (uint64_t)addr, iova, len);
57917c839f7STomasz Duszynski }
58017c839f7STomasz Duszynski 
58117c839f7STomasz Duszynski static int
58217c839f7STomasz Duszynski platform_bus_dma_unmap(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
58317c839f7STomasz Duszynski {
58417c839f7STomasz Duszynski 	struct rte_platform_device *pdev;
58517c839f7STomasz Duszynski 
58617c839f7STomasz Duszynski 	pdev = RTE_DEV_TO_PLATFORM_DEV(dev);
58717c839f7STomasz Duszynski 	if (pdev == NULL || pdev->driver == NULL) {
58817c839f7STomasz Duszynski 		rte_errno = EINVAL;
58917c839f7STomasz Duszynski 		return -1;
59017c839f7STomasz Duszynski 	}
59117c839f7STomasz Duszynski 
59217c839f7STomasz Duszynski 	if (pdev->driver->dma_unmap != NULL)
59317c839f7STomasz Duszynski 		return pdev->driver->dma_unmap(pdev, addr, iova, len);
59417c839f7STomasz Duszynski 
59517c839f7STomasz Duszynski 	return rte_vfio_container_dma_unmap(RTE_VFIO_DEFAULT_CONTAINER_FD, (uint64_t)addr, iova,
59617c839f7STomasz Duszynski 					    len);
59717c839f7STomasz Duszynski }
59817c839f7STomasz Duszynski 
59917c839f7STomasz Duszynski static enum rte_iova_mode
60017c839f7STomasz Duszynski platform_bus_get_iommu_class(void)
60117c839f7STomasz Duszynski {
60217c839f7STomasz Duszynski 	struct rte_platform_driver *pdrv;
60317c839f7STomasz Duszynski 	struct rte_platform_device *pdev;
60417c839f7STomasz Duszynski 
60517c839f7STomasz Duszynski 	FOREACH_DEVICE_ON_PLATFORM_BUS(pdev) {
60617c839f7STomasz Duszynski 		pdrv = pdev->driver;
60717c839f7STomasz Duszynski 		if (pdrv != NULL && pdrv->drv_flags & RTE_PLATFORM_DRV_NEED_IOVA_AS_VA)
60817c839f7STomasz Duszynski 			return RTE_IOVA_VA;
60917c839f7STomasz Duszynski 	}
61017c839f7STomasz Duszynski 
61117c839f7STomasz Duszynski 	return RTE_IOVA_DC;
61217c839f7STomasz Duszynski }
61317c839f7STomasz Duszynski 
61417c839f7STomasz Duszynski static int
61517c839f7STomasz Duszynski platform_bus_cleanup(void)
61617c839f7STomasz Duszynski {
61717c839f7STomasz Duszynski 	struct rte_platform_device *pdev, *tmp;
61817c839f7STomasz Duszynski 
61917c839f7STomasz Duszynski 	RTE_TAILQ_FOREACH_SAFE(pdev, &platform_bus.device_list, next, tmp) {
62017c839f7STomasz Duszynski 		TAILQ_REMOVE(&platform_bus.device_list, pdev, next);
621d7800111STomasz Duszynski 		platform_bus_unplug(&pdev->device);
62217c839f7STomasz Duszynski 	}
62317c839f7STomasz Duszynski 
62417c839f7STomasz Duszynski 	return 0;
62517c839f7STomasz Duszynski }
62617c839f7STomasz Duszynski 
62717c839f7STomasz Duszynski struct rte_platform_bus platform_bus = {
62817c839f7STomasz Duszynski 	.bus = {
62917c839f7STomasz Duszynski 		.scan = platform_bus_scan,
63017c839f7STomasz Duszynski 		.probe = platform_bus_probe,
63117c839f7STomasz Duszynski 		.find_device = platform_bus_find_device,
63217c839f7STomasz Duszynski 		.plug = platform_bus_plug,
63317c839f7STomasz Duszynski 		.unplug = platform_bus_unplug,
63417c839f7STomasz Duszynski 		.parse = platform_bus_parse,
63517c839f7STomasz Duszynski 		.dma_map = platform_bus_dma_map,
63617c839f7STomasz Duszynski 		.dma_unmap = platform_bus_dma_unmap,
63717c839f7STomasz Duszynski 		.get_iommu_class = platform_bus_get_iommu_class,
63817c839f7STomasz Duszynski 		.dev_iterate = platform_bus_dev_iterate,
63917c839f7STomasz Duszynski 		.cleanup = platform_bus_cleanup,
64017c839f7STomasz Duszynski 	},
64117c839f7STomasz Duszynski 	.device_list = TAILQ_HEAD_INITIALIZER(platform_bus.device_list),
64217c839f7STomasz Duszynski 	.driver_list = TAILQ_HEAD_INITIALIZER(platform_bus.driver_list),
64317c839f7STomasz Duszynski };
64417c839f7STomasz Duszynski 
64517c839f7STomasz Duszynski RTE_REGISTER_BUS(platform, platform_bus.bus);
64617c839f7STomasz Duszynski RTE_LOG_REGISTER_DEFAULT(platform_bus_logtype, NOTICE);
64717c839f7STomasz Duszynski 
64817c839f7STomasz Duszynski #endif /* VFIO_PRESENT */
649