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, ®_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