1 // SPDX-License-Identifier: GPL-2.0-only 2 #include "lib/include/libvfio/assert.h" 3 #include <fcntl.h> 4 #include <unistd.h> 5 #include <stdlib.h> 6 #include <sys/ioctl.h> 7 #include <linux/limits.h> 8 9 #include <libvfio.h> 10 11 #include "../kselftest_harness.h" 12 13 #define UUID_1 "52ac9bff-3a88-4fbd-901a-0d767c3b6c97" 14 #define UUID_2 "88594674-90a0-47a9-aea8-9d9b352ac08a" 15 16 static const char *pf_bdf; 17 static char *vf_bdf; 18 19 static pid_t main_pid; 20 21 static int container_setup(struct vfio_pci_device *device, const char *bdf, 22 const char *vf_token) 23 { 24 vfio_pci_group_setup(device, bdf); 25 vfio_container_set_iommu(device); 26 __vfio_pci_group_get_device_fd(device, bdf, vf_token); 27 28 /* The device fd will be -1 in case of mismatched tokens */ 29 return (device->fd < 0); 30 } 31 32 static int iommufd_setup(struct vfio_pci_device *device, const char *bdf, 33 const char *vf_token) 34 { 35 vfio_pci_cdev_open(device, bdf); 36 return __vfio_device_bind_iommufd(device->fd, 37 device->iommu->iommufd, vf_token); 38 } 39 40 static int device_init(const char *bdf, struct iommu *iommu, 41 const char *vf_token, struct vfio_pci_device **out_dev) 42 { 43 struct vfio_pci_device *device = vfio_pci_device_alloc(bdf, iommu); 44 int ret; 45 46 if (iommu->mode->container_path) 47 ret = container_setup(device, bdf, vf_token); 48 else 49 ret = iommufd_setup(device, bdf, vf_token); 50 51 *out_dev = device; 52 return ret; 53 } 54 55 static void device_cleanup(struct vfio_pci_device *device) 56 { 57 if (!device) 58 return; 59 60 if (device->fd > 0) 61 VFIO_ASSERT_EQ(close(device->fd), 0); 62 63 if (device->group_fd) 64 VFIO_ASSERT_EQ(close(device->group_fd), 0); 65 66 vfio_pci_device_free(device); 67 } 68 69 FIXTURE(vfio_pci_sriov_uapi_test) { 70 struct vfio_pci_device *pf; 71 struct vfio_pci_device *vf; 72 struct iommu *iommu; 73 char *pf_token; 74 }; 75 76 FIXTURE_VARIANT(vfio_pci_sriov_uapi_test) { 77 const char *iommu_mode; 78 char *vf_token; 79 }; 80 81 #define FIXTURE_VARIANT_ADD_IOMMU_MODE(_iommu_mode, _name, _vf_token) \ 82 FIXTURE_VARIANT_ADD(vfio_pci_sriov_uapi_test, _iommu_mode ## _ ## _name) { \ 83 .iommu_mode = #_iommu_mode, \ 84 .vf_token = (_vf_token), \ 85 } 86 87 FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(same_uuid, UUID_1); 88 FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(diff_uuid, UUID_2); 89 FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(null_uuid, NULL); 90 91 FIXTURE_SETUP(vfio_pci_sriov_uapi_test) 92 { 93 self->iommu = iommu_init(variant->iommu_mode); 94 95 self->pf_token = UUID_1; 96 ASSERT_EQ(device_init(pf_bdf, self->iommu, self->pf_token, &self->pf), 0); 97 } 98 99 FIXTURE_TEARDOWN(vfio_pci_sriov_uapi_test) 100 { 101 device_cleanup(self->vf); 102 device_cleanup(self->pf); 103 iommu_cleanup(self->iommu); 104 } 105 106 /* 107 * This asserts if the VF device is successfully created if its token matches 108 * with the token used to create/override the PF or fails during a mismatch. 109 */ 110 #define ASSERT_COND_VF_CREATION(_ret) do { \ 111 if (!variant->vf_token || strcmp(self->pf_token, variant->vf_token)) { \ 112 ASSERT_NE((_ret), 0); \ 113 } else { \ 114 ASSERT_EQ((_ret), 0); \ 115 } \ 116 } while (0) 117 118 /* 119 * Validate if the UAPI handles correctly and incorrectly set token on the VF. 120 */ 121 TEST_F(vfio_pci_sriov_uapi_test, init_token_match) 122 { 123 int ret; 124 125 ret = device_init(vf_bdf, self->iommu, variant->vf_token, &self->vf); 126 ASSERT_COND_VF_CREATION(ret); 127 } 128 129 /* 130 * After closing the PF, validate if the VF access still needs the right token. 131 */ 132 TEST_F(vfio_pci_sriov_uapi_test, pf_early_close) 133 { 134 int ret; 135 136 device_cleanup(self->pf); 137 138 /* Clean the 'pf' to avoid calling device_cleanup() again. */ 139 self->pf = NULL; 140 141 ret = device_init(vf_bdf, self->iommu, variant->vf_token, &self->vf); 142 ASSERT_COND_VF_CREATION(ret); 143 } 144 145 /* 146 * After PF device init, override the existing token and validate if the newly 147 * set token is the one that's active. 148 */ 149 TEST_F(vfio_pci_sriov_uapi_test, override_token) 150 { 151 int ret; 152 153 self->pf_token = UUID_2; 154 vfio_device_set_vf_token(self->pf->fd, self->pf_token); 155 156 ret = device_init(vf_bdf, self->iommu, variant->vf_token, &self->vf); 157 ASSERT_COND_VF_CREATION(ret); 158 } 159 160 static void vf_teardown(void) 161 { 162 /* 163 * The child processes, created by TEST_F()s, inherits this atexit() 164 * handler. Hence, check and destroy the VF only when the main/parent 165 * process exits. 166 */ 167 if (getpid() != main_pid) 168 return; 169 170 free(vf_bdf); 171 sysfs_sriov_numvfs_set(pf_bdf, 0); 172 } 173 174 static void vf_setup(void) 175 { 176 char *vf_driver; 177 int nr_vfs; 178 179 nr_vfs = sysfs_sriov_totalvfs_get(pf_bdf); 180 if (nr_vfs <= 0) 181 ksft_exit_skip("SR-IOV may not be supported by the PF: %s\n", pf_bdf); 182 183 nr_vfs = sysfs_sriov_numvfs_get(pf_bdf); 184 if (nr_vfs != 0) 185 ksft_exit_skip("SR-IOV already configured for the PF: %s\n", pf_bdf); 186 187 /* Create only one VF for testing */ 188 sysfs_sriov_numvfs_set(pf_bdf, 1); 189 190 /* 191 * Setup an exit handler to destroy the VF in case of failures 192 * during further setup at the end of the test run. 193 */ 194 main_pid = getpid(); 195 VFIO_ASSERT_EQ(atexit(vf_teardown), 0); 196 197 vf_bdf = sysfs_sriov_vf_bdf_get(pf_bdf, 0); 198 199 /* 200 * The VF inherits the driver from the PF. 201 * Ensure this is 'vfio-pci' before proceeding. 202 */ 203 vf_driver = sysfs_driver_get(vf_bdf); 204 VFIO_ASSERT_NE(vf_driver, NULL); 205 VFIO_ASSERT_EQ(strcmp(vf_driver, "vfio-pci"), 0); 206 free(vf_driver); 207 208 printf("Created 1 VF (%s) under the PF: %s\n", vf_bdf, pf_bdf); 209 } 210 211 int main(int argc, char *argv[]) 212 { 213 pf_bdf = vfio_selftests_get_bdf(&argc, argv); 214 vf_setup(); 215 216 return test_harness_run(argc, argv); 217 } 218