#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void usage(char *name) { fprintf(stderr, "usage: %s \n", name); fprintf(stderr, "\tssss: PCI segment, ex. 0000\n"); fprintf(stderr, "\tbb: PCI bus, ex. 01\n"); fprintf(stderr, "\tdd: PCI device, ex. 06\n"); fprintf(stderr, "\tf: PCI function, ex. 0\n"); } int main(int argc, char **argv) { int seg, bus, slot, func; int ret, container, group, device, groupid; char path[50], iommu_group_path[50], *group_name; struct stat st; ssize_t len; void *map = MAP_FAILED; int i; unsigned int bar; struct vfio_group_status group_status = { .argsz = sizeof(group_status) }; struct vfio_region_info region_info = { .argsz = sizeof(region_info) }; struct vfio_region_info config_info = { .argsz = sizeof(config_info) }; struct vfio_iommu_type1_dma_map dma_map = { .argsz = sizeof(dma_map), .flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE, }; if (argc != 2) { usage(argv[0]); return -1; } ret = sscanf(argv[1], "%04x:%02x:%02x.%d", &seg, &bus, &slot, &func); if (ret != 4) { fprintf(stderr, "Invalid device\n"); usage(argv[0]); return -1; } /* Boilerplate vfio setup */ container = open("/dev/vfio/vfio", O_RDWR); if (container < 0) { fprintf(stderr, "Failed to open /dev/vfio/vfio, %d (%s)\n", container, strerror(errno)); return container; } snprintf(path, sizeof(path), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/", seg, bus, slot, func); ret = stat(path, &st); if (ret < 0) { fprintf(stderr, "No such device\n"); return ret; } strncat(path, "iommu_group", sizeof(path) - strlen(path) - 1); len = readlink(path, iommu_group_path, sizeof(iommu_group_path)); if (len <= 0) { fprintf(stderr, "No iommu_group for device\n"); return -1; } iommu_group_path[len] = 0; group_name = basename(iommu_group_path); if (sscanf(group_name, "%d", &groupid) != 1) { fprintf(stderr, "Unknown group\n"); return -1; } snprintf(path, sizeof(path), "/dev/vfio/%d", groupid); group = open(path, O_RDWR); if (group < 0) { fprintf(stderr, "Failed to open %s, %d (%s)\n", path, group, strerror(errno)); return group; } ret = ioctl(group, VFIO_GROUP_GET_STATUS, &group_status); if (ret) { fprintf(stderr, "ioctl(VFIO_GROUP_GET_STATUS) failed\n"); return ret; } if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)) { fprintf(stderr, "Group not viable, all devices attached to vfio?\n"); return -1; } ret = ioctl(group, VFIO_GROUP_SET_CONTAINER, &container); if (ret) { fprintf(stderr, "Failed to set group container\n"); return ret; } ret = ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU); if (ret) { fprintf(stderr, "Failed to set IOMMU\n"); return ret; } snprintf(path, sizeof(path), "%04x:%02x:%02x.%d", seg, bus, slot, func); device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, path); if (device < 0) { fprintf(stderr, "Failed to get device\n"); return -ENODEV; } config_info.index = VFIO_PCI_CONFIG_REGION_INDEX; ret = ioctl(device, VFIO_DEVICE_GET_REGION_INFO, &config_info); if (ret) { fprintf(stderr, "Failed to get config space region info\n"); return ret; } for (i = 0; i < 6; i++) { if (pread(device, &bar, sizeof(bar), config_info.offset + PCI_BASE_ADDRESS_0 + (4 * i)) != sizeof(bar)) { fprintf(stderr, "Error reading BAR%d\n", i); return -errno; } if (!(bar & PCI_BASE_ADDRESS_SPACE)) { break; tryagain: if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) i++; } } if (i >= 6) { fprintf(stderr, "No memory BARs found\n"); return -ENODEV; } region_info.index = VFIO_PCI_BAR0_REGION_INDEX + i; ret = ioctl(device, VFIO_DEVICE_GET_REGION_INFO, ®ion_info); if (ret) { fprintf(stderr, "Failed to get BAR%d region info\n", i); return ret; } if (!(region_info.flags & VFIO_REGION_INFO_FLAG_MMAP)) { printf("No mmap support, try next\n"); goto tryagain; } if (region_info.size < getpagesize()) { printf("Too small for mmap, try next\n"); goto tryagain; } map = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, device, region_info.offset); if (map == MAP_FAILED) { fprintf(stderr, "Error mmap'ing BAR: %m\n"); goto tryagain; } dma_map.size = getpagesize(); dma_map.vaddr = (__u64)map; dma_map.iova = 1024 * 1024 * 1024; /* 1GB IOVA, arbitrary */ ret = ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map); if (ret) { fprintf(stderr, "Failed to DMA map: %m\n"); return ret; } printf("Passed\n"); return 0; }