Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'for-linus-iommufd' of git://git.kernel.org/pub/scm/linux/kernel/git/jgg/iommufd

Pull iommufd updates from Jason Gunthorpe:
"This brings the first of three planned user IO page table invalidation
operations:

- IOMMU_HWPT_INVALIDATE allows invalidating the IOTLB integrated into
the iommu itself. The Intel implementation will also generate an
ATC invalidation to flush the device IOTLB as it unambiguously
knows the device, but other HW will not.

It goes along with the prior PR to implement userspace IO page tables
(aka nested translation for VMs) to allow Intel to have full
functionality for simple cases. An Intel implementation of the
operation is provided.

Also fix a small bug in the selftest mock iommu driver probe"

* tag 'for-linus-iommufd' of git://git.kernel.org/pub/scm/linux/kernel/git/jgg/iommufd:
iommufd/selftest: Check the bus type during probe
iommu/vt-d: Add iotlb flush for nested domain
iommufd: Add data structure for Intel VT-d stage-1 cache invalidation
iommufd/selftest: Add coverage for IOMMU_HWPT_INVALIDATE ioctl
iommufd/selftest: Add IOMMU_TEST_OP_MD_CHECK_IOTLB test op
iommufd/selftest: Add mock_domain_cache_invalidate_user support
iommu: Add iommu_copy_struct_from_user_array helper
iommufd: Add IOMMU_HWPT_INVALIDATE
iommu: Add cache_invalidate_user op

+619 -13
+88
drivers/iommu/intel/nested.c
··· 73 73 kfree(to_dmar_domain(domain)); 74 74 } 75 75 76 + static void nested_flush_dev_iotlb(struct dmar_domain *domain, u64 addr, 77 + unsigned int mask) 78 + { 79 + struct device_domain_info *info; 80 + unsigned long flags; 81 + u16 sid, qdep; 82 + 83 + spin_lock_irqsave(&domain->lock, flags); 84 + list_for_each_entry(info, &domain->devices, link) { 85 + if (!info->ats_enabled) 86 + continue; 87 + sid = info->bus << 8 | info->devfn; 88 + qdep = info->ats_qdep; 89 + qi_flush_dev_iotlb(info->iommu, sid, info->pfsid, 90 + qdep, addr, mask); 91 + quirk_extra_dev_tlb_flush(info, addr, mask, 92 + IOMMU_NO_PASID, qdep); 93 + } 94 + spin_unlock_irqrestore(&domain->lock, flags); 95 + } 96 + 97 + static void intel_nested_flush_cache(struct dmar_domain *domain, u64 addr, 98 + unsigned long npages, bool ih) 99 + { 100 + struct iommu_domain_info *info; 101 + unsigned int mask; 102 + unsigned long i; 103 + 104 + xa_for_each(&domain->iommu_array, i, info) 105 + qi_flush_piotlb(info->iommu, 106 + domain_id_iommu(domain, info->iommu), 107 + IOMMU_NO_PASID, addr, npages, ih); 108 + 109 + if (!domain->has_iotlb_device) 110 + return; 111 + 112 + if (npages == U64_MAX) 113 + mask = 64 - VTD_PAGE_SHIFT; 114 + else 115 + mask = ilog2(__roundup_pow_of_two(npages)); 116 + 117 + nested_flush_dev_iotlb(domain, addr, mask); 118 + } 119 + 120 + static int intel_nested_cache_invalidate_user(struct iommu_domain *domain, 121 + struct iommu_user_data_array *array) 122 + { 123 + struct dmar_domain *dmar_domain = to_dmar_domain(domain); 124 + struct iommu_hwpt_vtd_s1_invalidate inv_entry; 125 + u32 index, processed = 0; 126 + int ret = 0; 127 + 128 + if (array->type != IOMMU_HWPT_INVALIDATE_DATA_VTD_S1) { 129 + ret = -EINVAL; 130 + goto out; 131 + } 132 + 133 + for (index = 0; index < array->entry_num; index++) { 134 + ret = iommu_copy_struct_from_user_array(&inv_entry, array, 135 + IOMMU_HWPT_INVALIDATE_DATA_VTD_S1, 136 + index, __reserved); 137 + if (ret) 138 + break; 139 + 140 + if ((inv_entry.flags & ~IOMMU_VTD_INV_FLAGS_LEAF) || 141 + inv_entry.__reserved) { 142 + ret = -EOPNOTSUPP; 143 + break; 144 + } 145 + 146 + if (!IS_ALIGNED(inv_entry.addr, VTD_PAGE_SIZE) || 147 + ((inv_entry.npages == U64_MAX) && inv_entry.addr)) { 148 + ret = -EINVAL; 149 + break; 150 + } 151 + 152 + intel_nested_flush_cache(dmar_domain, inv_entry.addr, 153 + inv_entry.npages, 154 + inv_entry.flags & IOMMU_VTD_INV_FLAGS_LEAF); 155 + processed++; 156 + } 157 + 158 + out: 159 + array->entry_num = processed; 160 + return ret; 161 + } 162 + 76 163 static const struct iommu_domain_ops intel_nested_domain_ops = { 77 164 .attach_dev = intel_nested_attach_dev, 78 165 .free = intel_nested_domain_free, 166 + .cache_invalidate_user = intel_nested_cache_invalidate_user, 79 167 }; 80 168 81 169 struct iommu_domain *intel_nested_domain_alloc(struct iommu_domain *parent,
+41
drivers/iommu/iommufd/hw_pagetable.c
··· 373 373 iommufd_put_object(ucmd->ictx, &hwpt_paging->common.obj); 374 374 return rc; 375 375 } 376 + 377 + int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd) 378 + { 379 + struct iommu_hwpt_invalidate *cmd = ucmd->cmd; 380 + struct iommu_user_data_array data_array = { 381 + .type = cmd->data_type, 382 + .uptr = u64_to_user_ptr(cmd->data_uptr), 383 + .entry_len = cmd->entry_len, 384 + .entry_num = cmd->entry_num, 385 + }; 386 + struct iommufd_hw_pagetable *hwpt; 387 + u32 done_num = 0; 388 + int rc; 389 + 390 + if (cmd->__reserved) { 391 + rc = -EOPNOTSUPP; 392 + goto out; 393 + } 394 + 395 + if (cmd->entry_num && (!cmd->data_uptr || !cmd->entry_len)) { 396 + rc = -EINVAL; 397 + goto out; 398 + } 399 + 400 + hwpt = iommufd_get_hwpt_nested(ucmd, cmd->hwpt_id); 401 + if (IS_ERR(hwpt)) { 402 + rc = PTR_ERR(hwpt); 403 + goto out; 404 + } 405 + 406 + rc = hwpt->domain->ops->cache_invalidate_user(hwpt->domain, 407 + &data_array); 408 + done_num = data_array.entry_num; 409 + 410 + iommufd_put_object(ucmd->ictx, &hwpt->obj); 411 + out: 412 + cmd->entry_num = done_num; 413 + if (iommufd_ucmd_respond(ucmd, sizeof(*cmd))) 414 + return -EFAULT; 415 + return rc; 416 + }
+10
drivers/iommu/iommufd/iommufd_private.h
··· 328 328 IOMMUFD_OBJ_HWPT_PAGING), 329 329 struct iommufd_hwpt_paging, common.obj); 330 330 } 331 + 332 + static inline struct iommufd_hw_pagetable * 333 + iommufd_get_hwpt_nested(struct iommufd_ucmd *ucmd, u32 id) 334 + { 335 + return container_of(iommufd_get_object(ucmd->ictx, id, 336 + IOMMUFD_OBJ_HWPT_NESTED), 337 + struct iommufd_hw_pagetable, obj); 338 + } 339 + 331 340 int iommufd_hwpt_set_dirty_tracking(struct iommufd_ucmd *ucmd); 332 341 int iommufd_hwpt_get_dirty_bitmap(struct iommufd_ucmd *ucmd); 333 342 ··· 354 345 void iommufd_hwpt_nested_destroy(struct iommufd_object *obj); 355 346 void iommufd_hwpt_nested_abort(struct iommufd_object *obj); 356 347 int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd); 348 + int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd); 357 349 358 350 static inline void iommufd_hw_pagetable_put(struct iommufd_ctx *ictx, 359 351 struct iommufd_hw_pagetable *hwpt)
+23
drivers/iommu/iommufd/iommufd_test.h
··· 21 21 IOMMU_TEST_OP_ACCESS_REPLACE_IOAS, 22 22 IOMMU_TEST_OP_MOCK_DOMAIN_FLAGS, 23 23 IOMMU_TEST_OP_DIRTY, 24 + IOMMU_TEST_OP_MD_CHECK_IOTLB, 24 25 }; 25 26 26 27 enum { ··· 122 121 __aligned_u64 uptr; 123 122 __aligned_u64 out_nr_dirty; 124 123 } dirty; 124 + struct { 125 + __u32 id; 126 + __u32 iotlb; 127 + } check_iotlb; 125 128 }; 126 129 __u32 last; 127 130 }; ··· 151 146 */ 152 147 struct iommu_hwpt_selftest { 153 148 __u32 iotlb; 149 + }; 150 + 151 + /* Should not be equal to any defined value in enum iommu_hwpt_invalidate_data_type */ 152 + #define IOMMU_HWPT_INVALIDATE_DATA_SELFTEST 0xdeadbeef 153 + #define IOMMU_HWPT_INVALIDATE_DATA_SELFTEST_INVALID 0xdadbeef 154 + 155 + /** 156 + * struct iommu_hwpt_invalidate_selftest - Invalidation data for Mock driver 157 + * (IOMMU_HWPT_INVALIDATE_DATA_SELFTEST) 158 + * @flags: Invalidate flags 159 + * @iotlb_id: Invalidate iotlb entry index 160 + * 161 + * If IOMMU_TEST_INVALIDATE_ALL is set in @flags, @iotlb_id will be ignored 162 + */ 163 + struct iommu_hwpt_invalidate_selftest { 164 + #define IOMMU_TEST_INVALIDATE_FLAG_ALL (1 << 0) 165 + __u32 flags; 166 + __u32 iotlb_id; 154 167 }; 155 168 156 169 #endif
+3
drivers/iommu/iommufd/main.c
··· 322 322 struct iommu_hw_info info; 323 323 struct iommu_hwpt_alloc hwpt; 324 324 struct iommu_hwpt_get_dirty_bitmap get_dirty_bitmap; 325 + struct iommu_hwpt_invalidate cache; 325 326 struct iommu_hwpt_set_dirty_tracking set_dirty_tracking; 326 327 struct iommu_ioas_alloc alloc; 327 328 struct iommu_ioas_allow_iovas allow_iovas; ··· 361 360 __reserved), 362 361 IOCTL_OP(IOMMU_HWPT_GET_DIRTY_BITMAP, iommufd_hwpt_get_dirty_bitmap, 363 362 struct iommu_hwpt_get_dirty_bitmap, data), 363 + IOCTL_OP(IOMMU_HWPT_INVALIDATE, iommufd_hwpt_invalidate, 364 + struct iommu_hwpt_invalidate, __reserved), 364 365 IOCTL_OP(IOMMU_HWPT_SET_DIRTY_TRACKING, iommufd_hwpt_set_dirty_tracking, 365 366 struct iommu_hwpt_set_dirty_tracking, __reserved), 366 367 IOCTL_OP(IOMMU_IOAS_ALLOC, iommufd_ioas_alloc_ioctl,
+91 -13
drivers/iommu/iommufd/selftest.c
··· 25 25 26 26 size_t iommufd_test_memory_limit = 65536; 27 27 28 + struct mock_bus_type { 29 + struct bus_type bus; 30 + struct notifier_block nb; 31 + }; 32 + 33 + static struct mock_bus_type iommufd_mock_bus_type = { 34 + .bus = { 35 + .name = "iommufd_mock", 36 + }, 37 + }; 38 + 39 + static atomic_t mock_dev_num; 40 + 28 41 enum { 29 42 MOCK_DIRTY_TRACK = 1, 30 43 MOCK_IO_PAGE_SIZE = PAGE_SIZE / 2, ··· 450 437 451 438 static struct iommu_device *mock_probe_device(struct device *dev) 452 439 { 440 + if (dev->bus != &iommufd_mock_bus_type.bus) 441 + return ERR_PTR(-ENODEV); 453 442 return &mock_iommu_device; 454 443 } 455 444 ··· 488 473 kfree(mock_nested); 489 474 } 490 475 476 + static int 477 + mock_domain_cache_invalidate_user(struct iommu_domain *domain, 478 + struct iommu_user_data_array *array) 479 + { 480 + struct mock_iommu_domain_nested *mock_nested = 481 + container_of(domain, struct mock_iommu_domain_nested, domain); 482 + struct iommu_hwpt_invalidate_selftest inv; 483 + u32 processed = 0; 484 + int i = 0, j; 485 + int rc = 0; 486 + 487 + if (array->type != IOMMU_HWPT_INVALIDATE_DATA_SELFTEST) { 488 + rc = -EINVAL; 489 + goto out; 490 + } 491 + 492 + for ( ; i < array->entry_num; i++) { 493 + rc = iommu_copy_struct_from_user_array(&inv, array, 494 + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST, 495 + i, iotlb_id); 496 + if (rc) 497 + break; 498 + 499 + if (inv.flags & ~IOMMU_TEST_INVALIDATE_FLAG_ALL) { 500 + rc = -EOPNOTSUPP; 501 + break; 502 + } 503 + 504 + if (inv.iotlb_id > MOCK_NESTED_DOMAIN_IOTLB_ID_MAX) { 505 + rc = -EINVAL; 506 + break; 507 + } 508 + 509 + if (inv.flags & IOMMU_TEST_INVALIDATE_FLAG_ALL) { 510 + /* Invalidate all mock iotlb entries and ignore iotlb_id */ 511 + for (j = 0; j < MOCK_NESTED_DOMAIN_IOTLB_NUM; j++) 512 + mock_nested->iotlb[j] = 0; 513 + } else { 514 + mock_nested->iotlb[inv.iotlb_id] = 0; 515 + } 516 + 517 + processed++; 518 + } 519 + 520 + out: 521 + array->entry_num = processed; 522 + return rc; 523 + } 524 + 491 525 static struct iommu_domain_ops domain_nested_ops = { 492 526 .free = mock_domain_free_nested, 493 527 .attach_dev = mock_domain_nop_attach, 528 + .cache_invalidate_user = mock_domain_cache_invalidate_user, 494 529 }; 495 530 496 531 static inline struct iommufd_hw_pagetable * ··· 590 525 struct mock_iommu_domain_nested, domain); 591 526 return hwpt; 592 527 } 593 - 594 - struct mock_bus_type { 595 - struct bus_type bus; 596 - struct notifier_block nb; 597 - }; 598 - 599 - static struct mock_bus_type iommufd_mock_bus_type = { 600 - .bus = { 601 - .name = "iommufd_mock", 602 - }, 603 - }; 604 - 605 - static atomic_t mock_dev_num; 606 528 607 529 static void mock_dev_release(struct device *dev) 608 530 { ··· 843 791 uptr += PAGE_SIZE; 844 792 } 845 793 return 0; 794 + } 795 + 796 + static int iommufd_test_md_check_iotlb(struct iommufd_ucmd *ucmd, 797 + u32 mockpt_id, unsigned int iotlb_id, 798 + u32 iotlb) 799 + { 800 + struct mock_iommu_domain_nested *mock_nested; 801 + struct iommufd_hw_pagetable *hwpt; 802 + int rc = 0; 803 + 804 + hwpt = get_md_pagetable_nested(ucmd, mockpt_id, &mock_nested); 805 + if (IS_ERR(hwpt)) 806 + return PTR_ERR(hwpt); 807 + 808 + mock_nested = container_of(hwpt->domain, 809 + struct mock_iommu_domain_nested, domain); 810 + 811 + if (iotlb_id > MOCK_NESTED_DOMAIN_IOTLB_ID_MAX || 812 + mock_nested->iotlb[iotlb_id] != iotlb) 813 + rc = -EINVAL; 814 + iommufd_put_object(ucmd->ictx, &hwpt->obj); 815 + return rc; 846 816 } 847 817 848 818 struct selftest_access { ··· 1348 1274 return iommufd_test_md_check_refs( 1349 1275 ucmd, u64_to_user_ptr(cmd->check_refs.uptr), 1350 1276 cmd->check_refs.length, cmd->check_refs.refs); 1277 + case IOMMU_TEST_OP_MD_CHECK_IOTLB: 1278 + return iommufd_test_md_check_iotlb(ucmd, cmd->id, 1279 + cmd->check_iotlb.id, 1280 + cmd->check_iotlb.iotlb); 1351 1281 case IOMMU_TEST_OP_CREATE_ACCESS: 1352 1282 return iommufd_test_create_access(ucmd, cmd->id, 1353 1283 cmd->create_access.flags);
+77
include/linux/iommu.h
··· 290 290 }; 291 291 292 292 /** 293 + * struct iommu_user_data_array - iommu driver specific user space data array 294 + * @type: The data type of all the entries in the user buffer array 295 + * @uptr: Pointer to the user buffer array 296 + * @entry_len: The fixed-width length of an entry in the array, in bytes 297 + * @entry_num: The number of total entries in the array 298 + * 299 + * The user buffer includes an array of requests with format defined in 300 + * include/uapi/linux/iommufd.h 301 + */ 302 + struct iommu_user_data_array { 303 + unsigned int type; 304 + void __user *uptr; 305 + size_t entry_len; 306 + u32 entry_num; 307 + }; 308 + 309 + /** 293 310 * __iommu_copy_struct_from_user - Copy iommu driver specific user space data 294 311 * @dst_data: Pointer to an iommu driver specific user data that is defined in 295 312 * include/uapi/linux/iommufd.h ··· 345 328 __iommu_copy_struct_from_user(kdst, user_data, data_type, \ 346 329 sizeof(*kdst), \ 347 330 offsetofend(typeof(*kdst), min_last)) 331 + 332 + /** 333 + * __iommu_copy_struct_from_user_array - Copy iommu driver specific user space 334 + * data from an iommu_user_data_array 335 + * @dst_data: Pointer to an iommu driver specific user data that is defined in 336 + * include/uapi/linux/iommufd.h 337 + * @src_array: Pointer to a struct iommu_user_data_array for a user space array 338 + * @data_type: The data type of the @dst_data. Must match with @src_array.type 339 + * @index: Index to the location in the array to copy user data from 340 + * @data_len: Length of current user data structure, i.e. sizeof(struct _dst) 341 + * @min_len: Initial length of user data structure for backward compatibility. 342 + * This should be offsetofend using the last member in the user data 343 + * struct that was initially added to include/uapi/linux/iommufd.h 344 + */ 345 + static inline int __iommu_copy_struct_from_user_array( 346 + void *dst_data, const struct iommu_user_data_array *src_array, 347 + unsigned int data_type, unsigned int index, size_t data_len, 348 + size_t min_len) 349 + { 350 + struct iommu_user_data src_data; 351 + 352 + if (WARN_ON(!src_array || index >= src_array->entry_num)) 353 + return -EINVAL; 354 + if (!src_array->entry_num) 355 + return -EINVAL; 356 + src_data.uptr = src_array->uptr + src_array->entry_len * index; 357 + src_data.len = src_array->entry_len; 358 + src_data.type = src_array->type; 359 + 360 + return __iommu_copy_struct_from_user(dst_data, &src_data, data_type, 361 + data_len, min_len); 362 + } 363 + 364 + /** 365 + * iommu_copy_struct_from_user_array - Copy iommu driver specific user space 366 + * data from an iommu_user_data_array 367 + * @kdst: Pointer to an iommu driver specific user data that is defined in 368 + * include/uapi/linux/iommufd.h 369 + * @user_array: Pointer to a struct iommu_user_data_array for a user space 370 + * array 371 + * @data_type: The data type of the @kdst. Must match with @user_array->type 372 + * @index: Index to the location in the array to copy user data from 373 + * @min_last: The last member of the data structure @kdst points in the 374 + * initial version. 375 + * Return 0 for success, otherwise -error. 376 + */ 377 + #define iommu_copy_struct_from_user_array(kdst, user_array, data_type, index, \ 378 + min_last) \ 379 + __iommu_copy_struct_from_user_array( \ 380 + kdst, user_array, data_type, index, sizeof(*(kdst)), \ 381 + offsetofend(typeof(*(kdst)), min_last)) 348 382 349 383 /** 350 384 * struct iommu_ops - iommu ops and capabilities ··· 513 445 * @iotlb_sync_map: Sync mappings created recently using @map to the hardware 514 446 * @iotlb_sync: Flush all queued ranges from the hardware TLBs and empty flush 515 447 * queue 448 + * @cache_invalidate_user: Flush hardware cache for user space IO page table. 449 + * The @domain must be IOMMU_DOMAIN_NESTED. The @array 450 + * passes in the cache invalidation requests, in form 451 + * of a driver data structure. The driver must update 452 + * array->entry_num to report the number of handled 453 + * invalidation requests. The driver data structure 454 + * must be defined in include/uapi/linux/iommufd.h 516 455 * @iova_to_phys: translate iova to physical address 517 456 * @enforce_cache_coherency: Prevent any kind of DMA from bypassing IOMMU_CACHE, 518 457 * including no-snoop TLPs on PCIe or other platform ··· 545 470 size_t size); 546 471 void (*iotlb_sync)(struct iommu_domain *domain, 547 472 struct iommu_iotlb_gather *iotlb_gather); 473 + int (*cache_invalidate_user)(struct iommu_domain *domain, 474 + struct iommu_user_data_array *array); 548 475 549 476 phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, 550 477 dma_addr_t iova);
+79
include/uapi/linux/iommufd.h
··· 49 49 IOMMUFD_CMD_GET_HW_INFO, 50 50 IOMMUFD_CMD_HWPT_SET_DIRTY_TRACKING, 51 51 IOMMUFD_CMD_HWPT_GET_DIRTY_BITMAP, 52 + IOMMUFD_CMD_HWPT_INVALIDATE, 52 53 }; 53 54 54 55 /** ··· 614 613 #define IOMMU_HWPT_GET_DIRTY_BITMAP _IO(IOMMUFD_TYPE, \ 615 614 IOMMUFD_CMD_HWPT_GET_DIRTY_BITMAP) 616 615 616 + /** 617 + * enum iommu_hwpt_invalidate_data_type - IOMMU HWPT Cache Invalidation 618 + * Data Type 619 + * @IOMMU_HWPT_INVALIDATE_DATA_VTD_S1: Invalidation data for VTD_S1 620 + */ 621 + enum iommu_hwpt_invalidate_data_type { 622 + IOMMU_HWPT_INVALIDATE_DATA_VTD_S1, 623 + }; 624 + 625 + /** 626 + * enum iommu_hwpt_vtd_s1_invalidate_flags - Flags for Intel VT-d 627 + * stage-1 cache invalidation 628 + * @IOMMU_VTD_INV_FLAGS_LEAF: Indicates whether the invalidation applies 629 + * to all-levels page structure cache or just 630 + * the leaf PTE cache. 631 + */ 632 + enum iommu_hwpt_vtd_s1_invalidate_flags { 633 + IOMMU_VTD_INV_FLAGS_LEAF = 1 << 0, 634 + }; 635 + 636 + /** 637 + * struct iommu_hwpt_vtd_s1_invalidate - Intel VT-d cache invalidation 638 + * (IOMMU_HWPT_INVALIDATE_DATA_VTD_S1) 639 + * @addr: The start address of the range to be invalidated. It needs to 640 + * be 4KB aligned. 641 + * @npages: Number of contiguous 4K pages to be invalidated. 642 + * @flags: Combination of enum iommu_hwpt_vtd_s1_invalidate_flags 643 + * @__reserved: Must be 0 644 + * 645 + * The Intel VT-d specific invalidation data for user-managed stage-1 cache 646 + * invalidation in nested translation. Userspace uses this structure to 647 + * tell the impacted cache scope after modifying the stage-1 page table. 648 + * 649 + * Invalidating all the caches related to the page table by setting @addr 650 + * to be 0 and @npages to be U64_MAX. 651 + * 652 + * The device TLB will be invalidated automatically if ATS is enabled. 653 + */ 654 + struct iommu_hwpt_vtd_s1_invalidate { 655 + __aligned_u64 addr; 656 + __aligned_u64 npages; 657 + __u32 flags; 658 + __u32 __reserved; 659 + }; 660 + 661 + /** 662 + * struct iommu_hwpt_invalidate - ioctl(IOMMU_HWPT_INVALIDATE) 663 + * @size: sizeof(struct iommu_hwpt_invalidate) 664 + * @hwpt_id: ID of a nested HWPT for cache invalidation 665 + * @data_uptr: User pointer to an array of driver-specific cache invalidation 666 + * data. 667 + * @data_type: One of enum iommu_hwpt_invalidate_data_type, defining the data 668 + * type of all the entries in the invalidation request array. It 669 + * should be a type supported by the hwpt pointed by @hwpt_id. 670 + * @entry_len: Length (in bytes) of a request entry in the request array 671 + * @entry_num: Input the number of cache invalidation requests in the array. 672 + * Output the number of requests successfully handled by kernel. 673 + * @__reserved: Must be 0. 674 + * 675 + * Invalidate the iommu cache for user-managed page table. Modifications on a 676 + * user-managed page table should be followed by this operation to sync cache. 677 + * Each ioctl can support one or more cache invalidation requests in the array 678 + * that has a total size of @entry_len * @entry_num. 679 + * 680 + * An empty invalidation request array by setting @entry_num==0 is allowed, and 681 + * @entry_len and @data_uptr would be ignored in this case. This can be used to 682 + * check if the given @data_type is supported or not by kernel. 683 + */ 684 + struct iommu_hwpt_invalidate { 685 + __u32 size; 686 + __u32 hwpt_id; 687 + __aligned_u64 data_uptr; 688 + __u32 data_type; 689 + __u32 entry_len; 690 + __u32 entry_num; 691 + __u32 __reserved; 692 + }; 693 + #define IOMMU_HWPT_INVALIDATE _IO(IOMMUFD_TYPE, IOMMUFD_CMD_HWPT_INVALIDATE) 617 694 #endif
+152
tools/testing/selftests/iommu/iommufd.c
··· 116 116 TEST_LENGTH(iommu_destroy, IOMMU_DESTROY, id); 117 117 TEST_LENGTH(iommu_hw_info, IOMMU_GET_HW_INFO, __reserved); 118 118 TEST_LENGTH(iommu_hwpt_alloc, IOMMU_HWPT_ALLOC, __reserved); 119 + TEST_LENGTH(iommu_hwpt_invalidate, IOMMU_HWPT_INVALIDATE, __reserved); 119 120 TEST_LENGTH(iommu_ioas_alloc, IOMMU_IOAS_ALLOC, out_ioas_id); 120 121 TEST_LENGTH(iommu_ioas_iova_ranges, IOMMU_IOAS_IOVA_RANGES, 121 122 out_iova_alignment); ··· 272 271 struct iommu_hwpt_selftest data = { 273 272 .iotlb = IOMMU_TEST_IOTLB_DEFAULT, 274 273 }; 274 + struct iommu_hwpt_invalidate_selftest inv_reqs[2] = {}; 275 275 uint32_t nested_hwpt_id[2] = {}; 276 + uint32_t num_inv; 276 277 uint32_t parent_hwpt_id = 0; 277 278 uint32_t parent_hwpt_id_not_work = 0; 278 279 uint32_t test_hwpt_id = 0; ··· 333 330 &nested_hwpt_id[1], 334 331 IOMMU_HWPT_DATA_SELFTEST, &data, 335 332 sizeof(data)); 333 + test_cmd_hwpt_check_iotlb_all(nested_hwpt_id[0], 334 + IOMMU_TEST_IOTLB_DEFAULT); 335 + test_cmd_hwpt_check_iotlb_all(nested_hwpt_id[1], 336 + IOMMU_TEST_IOTLB_DEFAULT); 336 337 337 338 /* Negative test: a nested hwpt on top of a nested hwpt */ 338 339 test_err_hwpt_alloc_nested(EINVAL, self->device_id, ··· 346 339 /* Negative test: parent hwpt now cannot be freed */ 347 340 EXPECT_ERRNO(EBUSY, 348 341 _test_ioctl_destroy(self->fd, parent_hwpt_id)); 342 + 343 + /* hwpt_invalidate only supports a user-managed hwpt (nested) */ 344 + num_inv = 1; 345 + test_err_hwpt_invalidate(ENOENT, parent_hwpt_id, inv_reqs, 346 + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST, 347 + sizeof(*inv_reqs), &num_inv); 348 + assert(!num_inv); 349 + 350 + /* Check data_type by passing zero-length array */ 351 + num_inv = 0; 352 + test_cmd_hwpt_invalidate(nested_hwpt_id[0], inv_reqs, 353 + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST, 354 + sizeof(*inv_reqs), &num_inv); 355 + assert(!num_inv); 356 + 357 + /* Negative test: Invalid data_type */ 358 + num_inv = 1; 359 + test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs, 360 + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST_INVALID, 361 + sizeof(*inv_reqs), &num_inv); 362 + assert(!num_inv); 363 + 364 + /* Negative test: structure size sanity */ 365 + num_inv = 1; 366 + test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs, 367 + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST, 368 + sizeof(*inv_reqs) + 1, &num_inv); 369 + assert(!num_inv); 370 + 371 + num_inv = 1; 372 + test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs, 373 + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST, 374 + 1, &num_inv); 375 + assert(!num_inv); 376 + 377 + /* Negative test: invalid flag is passed */ 378 + num_inv = 1; 379 + inv_reqs[0].flags = 0xffffffff; 380 + test_err_hwpt_invalidate(EOPNOTSUPP, nested_hwpt_id[0], inv_reqs, 381 + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST, 382 + sizeof(*inv_reqs), &num_inv); 383 + assert(!num_inv); 384 + 385 + /* Negative test: invalid data_uptr when array is not empty */ 386 + num_inv = 1; 387 + inv_reqs[0].flags = 0; 388 + test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], NULL, 389 + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST, 390 + sizeof(*inv_reqs), &num_inv); 391 + assert(!num_inv); 392 + 393 + /* Negative test: invalid entry_len when array is not empty */ 394 + num_inv = 1; 395 + inv_reqs[0].flags = 0; 396 + test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs, 397 + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST, 398 + 0, &num_inv); 399 + assert(!num_inv); 400 + 401 + /* Negative test: invalid iotlb_id */ 402 + num_inv = 1; 403 + inv_reqs[0].flags = 0; 404 + inv_reqs[0].iotlb_id = MOCK_NESTED_DOMAIN_IOTLB_ID_MAX + 1; 405 + test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs, 406 + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST, 407 + sizeof(*inv_reqs), &num_inv); 408 + assert(!num_inv); 409 + 410 + /* 411 + * Invalidate the 1st iotlb entry but fail the 2nd request 412 + * due to invalid flags configuration in the 2nd request. 413 + */ 414 + num_inv = 2; 415 + inv_reqs[0].flags = 0; 416 + inv_reqs[0].iotlb_id = 0; 417 + inv_reqs[1].flags = 0xffffffff; 418 + inv_reqs[1].iotlb_id = 1; 419 + test_err_hwpt_invalidate(EOPNOTSUPP, nested_hwpt_id[0], inv_reqs, 420 + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST, 421 + sizeof(*inv_reqs), &num_inv); 422 + assert(num_inv == 1); 423 + test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 0, 0); 424 + test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 1, 425 + IOMMU_TEST_IOTLB_DEFAULT); 426 + test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 2, 427 + IOMMU_TEST_IOTLB_DEFAULT); 428 + test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 3, 429 + IOMMU_TEST_IOTLB_DEFAULT); 430 + 431 + /* 432 + * Invalidate the 1st iotlb entry but fail the 2nd request 433 + * due to invalid iotlb_id configuration in the 2nd request. 434 + */ 435 + num_inv = 2; 436 + inv_reqs[0].flags = 0; 437 + inv_reqs[0].iotlb_id = 0; 438 + inv_reqs[1].flags = 0; 439 + inv_reqs[1].iotlb_id = MOCK_NESTED_DOMAIN_IOTLB_ID_MAX + 1; 440 + test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs, 441 + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST, 442 + sizeof(*inv_reqs), &num_inv); 443 + assert(num_inv == 1); 444 + test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 0, 0); 445 + test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 1, 446 + IOMMU_TEST_IOTLB_DEFAULT); 447 + test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 2, 448 + IOMMU_TEST_IOTLB_DEFAULT); 449 + test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 3, 450 + IOMMU_TEST_IOTLB_DEFAULT); 451 + 452 + /* Invalidate the 2nd iotlb entry and verify */ 453 + num_inv = 1; 454 + inv_reqs[0].flags = 0; 455 + inv_reqs[0].iotlb_id = 1; 456 + test_cmd_hwpt_invalidate(nested_hwpt_id[0], inv_reqs, 457 + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST, 458 + sizeof(*inv_reqs), &num_inv); 459 + assert(num_inv == 1); 460 + test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 0, 0); 461 + test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 1, 0); 462 + test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 2, 463 + IOMMU_TEST_IOTLB_DEFAULT); 464 + test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 3, 465 + IOMMU_TEST_IOTLB_DEFAULT); 466 + 467 + /* Invalidate the 3rd and 4th iotlb entries and verify */ 468 + num_inv = 2; 469 + inv_reqs[0].flags = 0; 470 + inv_reqs[0].iotlb_id = 2; 471 + inv_reqs[1].flags = 0; 472 + inv_reqs[1].iotlb_id = 3; 473 + test_cmd_hwpt_invalidate(nested_hwpt_id[0], inv_reqs, 474 + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST, 475 + sizeof(*inv_reqs), &num_inv); 476 + assert(num_inv == 2); 477 + test_cmd_hwpt_check_iotlb_all(nested_hwpt_id[0], 0); 478 + 479 + /* Invalidate all iotlb entries for nested_hwpt_id[1] and verify */ 480 + num_inv = 1; 481 + inv_reqs[0].flags = IOMMU_TEST_INVALIDATE_FLAG_ALL; 482 + test_cmd_hwpt_invalidate(nested_hwpt_id[1], inv_reqs, 483 + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST, 484 + sizeof(*inv_reqs), &num_inv); 485 + assert(num_inv == 1); 486 + test_cmd_hwpt_check_iotlb_all(nested_hwpt_id[1], 0); 349 487 350 488 /* Attach device to nested_hwpt_id[0] that then will be busy */ 351 489 test_cmd_mock_domain_replace(self->stdev_id, nested_hwpt_id[0]);
+55
tools/testing/selftests/iommu/iommufd_utils.h
··· 195 195 _test_cmd_hwpt_alloc(self->fd, device_id, pt_id, flags, \ 196 196 hwpt_id, data_type, data, data_len)) 197 197 198 + #define test_cmd_hwpt_check_iotlb(hwpt_id, iotlb_id, expected) \ 199 + ({ \ 200 + struct iommu_test_cmd test_cmd = { \ 201 + .size = sizeof(test_cmd), \ 202 + .op = IOMMU_TEST_OP_MD_CHECK_IOTLB, \ 203 + .id = hwpt_id, \ 204 + .check_iotlb = { \ 205 + .id = iotlb_id, \ 206 + .iotlb = expected, \ 207 + }, \ 208 + }; \ 209 + ASSERT_EQ(0, \ 210 + ioctl(self->fd, \ 211 + _IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_IOTLB), \ 212 + &test_cmd)); \ 213 + }) 214 + 215 + #define test_cmd_hwpt_check_iotlb_all(hwpt_id, expected) \ 216 + ({ \ 217 + int i; \ 218 + for (i = 0; i < MOCK_NESTED_DOMAIN_IOTLB_NUM; i++) \ 219 + test_cmd_hwpt_check_iotlb(hwpt_id, i, expected); \ 220 + }) 221 + 222 + static int _test_cmd_hwpt_invalidate(int fd, __u32 hwpt_id, void *reqs, 223 + uint32_t data_type, uint32_t lreq, 224 + uint32_t *nreqs) 225 + { 226 + struct iommu_hwpt_invalidate cmd = { 227 + .size = sizeof(cmd), 228 + .hwpt_id = hwpt_id, 229 + .data_type = data_type, 230 + .data_uptr = (uint64_t)reqs, 231 + .entry_len = lreq, 232 + .entry_num = *nreqs, 233 + }; 234 + int rc = ioctl(fd, IOMMU_HWPT_INVALIDATE, &cmd); 235 + *nreqs = cmd.entry_num; 236 + return rc; 237 + } 238 + 239 + #define test_cmd_hwpt_invalidate(hwpt_id, reqs, data_type, lreq, nreqs) \ 240 + ({ \ 241 + ASSERT_EQ(0, \ 242 + _test_cmd_hwpt_invalidate(self->fd, hwpt_id, reqs, \ 243 + data_type, lreq, nreqs)); \ 244 + }) 245 + #define test_err_hwpt_invalidate(_errno, hwpt_id, reqs, data_type, lreq, \ 246 + nreqs) \ 247 + ({ \ 248 + EXPECT_ERRNO(_errno, _test_cmd_hwpt_invalidate( \ 249 + self->fd, hwpt_id, reqs, \ 250 + data_type, lreq, nreqs)); \ 251 + }) 252 + 198 253 static int _test_cmd_access_replace_ioas(int fd, __u32 access_id, 199 254 unsigned int ioas_id) 200 255 {