1 // SPDX-License-Identifier: GPL-2.0 2 #include <assert.h> 3 #include <dirent.h> 4 #include <fcntl.h> 5 6 #include <uapi/linux/types.h> 7 #include <linux/limits.h> 8 #include <linux/sizes.h> 9 #include <linux/vfio.h> 10 #include <linux/iommufd.h> 11 12 #include <stdint.h> 13 #include <stdio.h> 14 #include <string.h> 15 #include <sys/ioctl.h> 16 #include <unistd.h> 17 18 #include <vfio_util.h> 19 #include "../kselftest_harness.h" 20 21 static const char iommu_dev_path[] = "/dev/iommu"; 22 static char cdev_path[PATH_MAX] = { '\0' }; 23 24 static void set_cdev_path(const char *bdf) 25 { 26 char dir_path[PATH_MAX]; 27 DIR *dir; 28 struct dirent *entry; 29 30 snprintf(dir_path, sizeof(dir_path), "/sys/bus/pci/devices/%s/vfio-dev/", bdf); 31 32 dir = opendir(dir_path); 33 assert(dir); 34 35 /* Find the file named "vfio<number>" */ 36 while ((entry = readdir(dir)) != NULL) { 37 if (!strncmp("vfio", entry->d_name, 4)) { 38 snprintf(cdev_path, sizeof(cdev_path), "/dev/vfio/devices/%s", 39 entry->d_name); 40 break; 41 } 42 } 43 44 assert(strlen(cdev_path) > 0); 45 46 closedir(dir); 47 } 48 49 static int vfio_device_bind_iommufd_ioctl(int cdev_fd, int iommufd) 50 { 51 struct vfio_device_bind_iommufd bind_args = { 52 .argsz = sizeof(bind_args), 53 .iommufd = iommufd, 54 }; 55 56 return ioctl(cdev_fd, VFIO_DEVICE_BIND_IOMMUFD, &bind_args); 57 } 58 59 static int vfio_device_get_info_ioctl(int cdev_fd) 60 { 61 struct vfio_device_info info_args = { .argsz = sizeof(info_args) }; 62 63 return ioctl(cdev_fd, VFIO_DEVICE_GET_INFO, &info_args); 64 } 65 66 static int vfio_device_ioas_alloc_ioctl(int iommufd, struct iommu_ioas_alloc *alloc_args) 67 { 68 *alloc_args = (struct iommu_ioas_alloc){ 69 .size = sizeof(struct iommu_ioas_alloc), 70 }; 71 72 return ioctl(iommufd, IOMMU_IOAS_ALLOC, alloc_args); 73 } 74 75 static int vfio_device_attach_iommufd_pt_ioctl(int cdev_fd, u32 pt_id) 76 { 77 struct vfio_device_attach_iommufd_pt attach_args = { 78 .argsz = sizeof(attach_args), 79 .pt_id = pt_id, 80 }; 81 82 return ioctl(cdev_fd, VFIO_DEVICE_ATTACH_IOMMUFD_PT, &attach_args); 83 } 84 85 static int vfio_device_detach_iommufd_pt_ioctl(int cdev_fd) 86 { 87 struct vfio_device_detach_iommufd_pt detach_args = { 88 .argsz = sizeof(detach_args), 89 }; 90 91 return ioctl(cdev_fd, VFIO_DEVICE_DETACH_IOMMUFD_PT, &detach_args); 92 } 93 94 FIXTURE(vfio_cdev) { 95 int cdev_fd; 96 int iommufd; 97 }; 98 99 FIXTURE_SETUP(vfio_cdev) 100 { 101 ASSERT_LE(0, (self->cdev_fd = open(cdev_path, O_RDWR, 0))); 102 ASSERT_LE(0, (self->iommufd = open(iommu_dev_path, O_RDWR, 0))); 103 } 104 105 FIXTURE_TEARDOWN(vfio_cdev) 106 { 107 ASSERT_EQ(0, close(self->cdev_fd)); 108 ASSERT_EQ(0, close(self->iommufd)); 109 } 110 111 TEST_F(vfio_cdev, bind) 112 { 113 ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, self->iommufd)); 114 ASSERT_EQ(0, vfio_device_get_info_ioctl(self->cdev_fd)); 115 } 116 117 TEST_F(vfio_cdev, get_info_without_bind_fails) 118 { 119 ASSERT_NE(0, vfio_device_get_info_ioctl(self->cdev_fd)); 120 } 121 122 TEST_F(vfio_cdev, bind_bad_iommufd_fails) 123 { 124 ASSERT_NE(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, -2)); 125 } 126 127 TEST_F(vfio_cdev, repeated_bind_fails) 128 { 129 ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, self->iommufd)); 130 ASSERT_NE(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, self->iommufd)); 131 } 132 133 TEST_F(vfio_cdev, attach_detatch_pt) 134 { 135 struct iommu_ioas_alloc alloc_args; 136 137 ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, self->iommufd)); 138 ASSERT_EQ(0, vfio_device_ioas_alloc_ioctl(self->iommufd, &alloc_args)); 139 ASSERT_EQ(0, vfio_device_attach_iommufd_pt_ioctl(self->cdev_fd, alloc_args.out_ioas_id)); 140 ASSERT_EQ(0, vfio_device_detach_iommufd_pt_ioctl(self->cdev_fd)); 141 } 142 143 TEST_F(vfio_cdev, attach_invalid_pt_fails) 144 { 145 ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, self->iommufd)); 146 ASSERT_NE(0, vfio_device_attach_iommufd_pt_ioctl(self->cdev_fd, UINT32_MAX)); 147 } 148 149 int main(int argc, char *argv[]) 150 { 151 const char *device_bdf = vfio_selftests_get_bdf(&argc, argv); 152 153 set_cdev_path(device_bdf); 154 printf("Using cdev device %s\n", cdev_path); 155 156 return test_harness_run(argc, argv); 157 } 158