1 // SPDX-License-Identifier: GPL-2.0 2 #include <uapi/linux/types.h> 3 #include <linux/limits.h> 4 #include <linux/sizes.h> 5 #include <linux/vfio.h> 6 #include <linux/iommufd.h> 7 8 #include <stdint.h> 9 #include <stdio.h> 10 #include <sys/ioctl.h> 11 #include <unistd.h> 12 13 #include <vfio_util.h> 14 #include "../kselftest_harness.h" 15 16 static const char iommu_dev_path[] = "/dev/iommu"; 17 static const char *cdev_path; 18 19 static int vfio_device_bind_iommufd_ioctl(int cdev_fd, int iommufd) 20 { 21 struct vfio_device_bind_iommufd bind_args = { 22 .argsz = sizeof(bind_args), 23 .iommufd = iommufd, 24 }; 25 26 return ioctl(cdev_fd, VFIO_DEVICE_BIND_IOMMUFD, &bind_args); 27 } 28 29 static int vfio_device_get_info_ioctl(int cdev_fd) 30 { 31 struct vfio_device_info info_args = { .argsz = sizeof(info_args) }; 32 33 return ioctl(cdev_fd, VFIO_DEVICE_GET_INFO, &info_args); 34 } 35 36 static int vfio_device_ioas_alloc_ioctl(int iommufd, struct iommu_ioas_alloc *alloc_args) 37 { 38 *alloc_args = (struct iommu_ioas_alloc){ 39 .size = sizeof(struct iommu_ioas_alloc), 40 }; 41 42 return ioctl(iommufd, IOMMU_IOAS_ALLOC, alloc_args); 43 } 44 45 static int vfio_device_attach_iommufd_pt_ioctl(int cdev_fd, u32 pt_id) 46 { 47 struct vfio_device_attach_iommufd_pt attach_args = { 48 .argsz = sizeof(attach_args), 49 .pt_id = pt_id, 50 }; 51 52 return ioctl(cdev_fd, VFIO_DEVICE_ATTACH_IOMMUFD_PT, &attach_args); 53 } 54 55 static int vfio_device_detach_iommufd_pt_ioctl(int cdev_fd) 56 { 57 struct vfio_device_detach_iommufd_pt detach_args = { 58 .argsz = sizeof(detach_args), 59 }; 60 61 return ioctl(cdev_fd, VFIO_DEVICE_DETACH_IOMMUFD_PT, &detach_args); 62 } 63 64 FIXTURE(vfio_cdev) { 65 int cdev_fd; 66 int iommufd; 67 }; 68 69 FIXTURE_SETUP(vfio_cdev) 70 { 71 ASSERT_LE(0, (self->cdev_fd = open(cdev_path, O_RDWR, 0))); 72 ASSERT_LE(0, (self->iommufd = open(iommu_dev_path, O_RDWR, 0))); 73 } 74 75 FIXTURE_TEARDOWN(vfio_cdev) 76 { 77 ASSERT_EQ(0, close(self->cdev_fd)); 78 ASSERT_EQ(0, close(self->iommufd)); 79 } 80 81 TEST_F(vfio_cdev, bind) 82 { 83 ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, self->iommufd)); 84 ASSERT_EQ(0, vfio_device_get_info_ioctl(self->cdev_fd)); 85 } 86 87 TEST_F(vfio_cdev, get_info_without_bind_fails) 88 { 89 ASSERT_NE(0, vfio_device_get_info_ioctl(self->cdev_fd)); 90 } 91 92 TEST_F(vfio_cdev, bind_bad_iommufd_fails) 93 { 94 ASSERT_NE(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, -2)); 95 } 96 97 TEST_F(vfio_cdev, repeated_bind_fails) 98 { 99 ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, self->iommufd)); 100 ASSERT_NE(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, self->iommufd)); 101 } 102 103 TEST_F(vfio_cdev, attach_detatch_pt) 104 { 105 struct iommu_ioas_alloc alloc_args; 106 107 ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, self->iommufd)); 108 ASSERT_EQ(0, vfio_device_ioas_alloc_ioctl(self->iommufd, &alloc_args)); 109 ASSERT_EQ(0, vfio_device_attach_iommufd_pt_ioctl(self->cdev_fd, alloc_args.out_ioas_id)); 110 ASSERT_EQ(0, vfio_device_detach_iommufd_pt_ioctl(self->cdev_fd)); 111 } 112 113 TEST_F(vfio_cdev, attach_invalid_pt_fails) 114 { 115 ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, self->iommufd)); 116 ASSERT_NE(0, vfio_device_attach_iommufd_pt_ioctl(self->cdev_fd, UINT32_MAX)); 117 } 118 119 int main(int argc, char *argv[]) 120 { 121 const char *device_bdf = vfio_selftests_get_bdf(&argc, argv); 122 123 cdev_path = vfio_pci_get_cdev_path(device_bdf); 124 printf("Using cdev device %s\n", cdev_path); 125 126 return test_harness_run(argc, argv); 127 } 128