From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-ej1-f44.google.com (mail-ej1-f44.google.com [209.85.218.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4ECF94AA004 for ; Tue, 6 Oct 2026 18:32:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.44 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791311577; cv=none; b=DTGelFAhZpKWk4+bUDCnffFxRCeMDa4PRTQvA9zPh6NITAb3mpsCav2nvUuPQm5h2zMz6lV94jnHSz/PMq5p5Glkw7bpccYTee8dOjgQGKSAgYaXesDpztKAgFsBQuQarDJjGVh4AhLLgkOeHiHnGZvMXL7uaV3HebQvjGTT4ew= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791311577; c=relaxed/simple; bh=HR8R4ngbcsA3oGQGVLPcFktLLA3qriwWNlLE9nHXOc4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TzeCn24G9feUR7hkAWnRBKJiaM+eafr/pLnxN7eqppuHDSnOIknV35Z+vnJA2GlsGFYFKJkk4LTt6lD8kiwsIyWmrPJT7YY/SlUlrAT7hvenJiW3ikGlZOkRiasjx4XJTGLHqQ2LltQmLr7Tc8ZQVh4hgjF5RLKNsRM8ak+xm9c= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=spoOBds4; arc=none smtp.client-ip=209.85.218.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="spoOBds4" Received: by mail-ej1-f44.google.com with SMTP id a640c23a62f3a-c2e8c710847so127272766b.0 for ; Tue, 06 Oct 2026 11:32:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1791311568; x=1791916368; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=teKgoAnZutiypdvXjRT5Q8SK6RKAQZGpxaEXQB1IAug=; b=spoOBds424KZXnIlATe9XjcRLiINybq3koZapAG8RkVuCARMqX+CCeai+UL/Biz/xF IAd8jh8wPoa/kT1FJojsdNPLEQFoHpY433uF/XBUCfcAkAVHbRlVv3LQfenaJVmgK45I Zm9oOWS73sGKvqe9+zu16rhNokGfZqK2OmCHfGS53Ta/gbULOktrmH0M6nRzteBjfBMj 5WkcqZ968DL0VzpWHDGw5HNJByhV6DoVa5psljX+QI9XkZ5m1/JVLi5aLgqNfPO+XNGR ZTjYq4ao1KrzXmixDCGfUdbSsfJNxZqPqYLFmtBOdCpqGR9WiRUFLf4QpQVZdbiC4WaR r96w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1791311568; x=1791916368; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=teKgoAnZutiypdvXjRT5Q8SK6RKAQZGpxaEXQB1IAug=; b=25GGI0VjFs12nsjX6zzyTE7FBlwqqRTSBzjVh/D5c4wjDwHA7klcuVeq12hbWE9g+b Xim+hUHjnkkqXdTqakbz+qVBQNv1bR+aQkyjKAmyIq05LU1T3sdGrxMPJsCjNr6zZP35 +/ZjW3YOuMRYlpYxfZgaPVDiwfl4Bf0wSsnZu4QYb626cge7J8UnUYoQlOlY7+KgVhmZ HuZsljoWUnwZ2GXc8oe0zvQ49HPBvnLzMSpQfYlUhEP2iLtWeV4W4qwK4aWgsw29DI9n /c8Pr4F0tPOt8vKeTiVCnD2T7Z40gG7KrydsVS0oscPOU3mQi8h3/UynBxuUJNhPRwgn Wenw== X-Forwarded-Encrypted: i=1; AKwUvByOJS1L5d1l2NbA++jMyArlihPPNVyL3tvvzovp+zazwl9OxoxUqpg2aXz7LQGug0P6LZ7E5QXylzfj/Ns=@vger.kernel.org X-Gm-Message-State: AFq9FYIPHiaaPAQ87T197hZA0p5eLWQqV/6Q+TupXRxD9eOrO9GRgqsH kyl/i9nldhCotb/9WdEcA1XW3HMUJb3J5XqR0AtlrJxD3W3fiOpJYwKf X-Gm-Gg: AYBFou2jaQ90iqjxHAZupQfUKgmwdiIVCPVqKizRRoOI8HR33rukAIwGQrGJwpH1x2K hinsGeosyFyImp3aTDhQHEX1G/MUy1M1X4tjOvDqYVY8Ghc7dOwr9BJVUFkyWBtg/YBr6bOkb9e sdM54ULsKZYv17dlklbeLk7QZsvyMCe1u0vpLO5p5D5bF6wjWJr+shmcOoQnkubL/s4Af6Jodom vMuRrU4mZ9+DaIDY+91g2jQ83rTAQkCDmBCLvfege7zN+KcrLzyKbxv8y//Ye9lztqFBVzV+tNX VWza05wYmKyFTRJRjBLbbssItA6pnQDOdBkDJIyMx9EQeBbrNYMgRbC6aGvmmIa+F98Wq9srpUF IsTAISTB1eQX4LmFGq0Z5+C0xLtJC5DDKGMeVnXjJWFam/BuaYs0efiPY7kUNtKlD0vldaUvVyj CXmV2X/q6jrF6TzLXenp7m9fLjTMUb3tehK9xYfT2Nh1d4DEFk4NxZCsBkCQqTgU0uXYbqQU/Zd JKQK6dNwriAckwESbdhWkFRlA14WWI/5DzMw2W3lQbSowA7n5UGQ0dw5nsoYQCbvaw2LA45HcrV PIV0IAGBghoABWpGTLF04Klt68hQvCb+RfAgwY3fjeWMqQ== X-Received: by 2002:a17:906:c107:b0:c29:f0a4:a4f8 with SMTP id a640c23a62f3a-c317b4bc0b7mr4286266b.0.1791311568135; Tue, 06 Oct 2026 11:32:48 -0700 (PDT) Received: from dev-dsk-fgriffo-1c-93421965.eu-west-1.amazon.com (54-240-197-234.amazon.com. [54.240.197.234]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c3158260f7bsm221934866b.6.2026.10.06.11.32.46 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Tue, 06 Oct 2026 11:32:47 -0700 (PDT) From: Fred Griffoul To: Paolo Bonzini , Sean Christopherson , Marc Zyngier , Oliver Upton , Andrew Morton , David Hildenbrand , Alexander Viro , Christian Brauner , Jan Kara , Jason Gunthorpe , Kevin Tian , Joerg Roedel , Will Deacon , Robin Murphy , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H . Peter Anvin" , Jonathan Corbet , Shuah Khan Cc: David Woodhouse , Ackerley Tng , Lorenzo Stoakes , "Liam R . Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Joey Gouly , Suzuki K Poulose , Zenghui Yu , Steffen Eiden , linux-kernel@vger.kernel.org, kvm@vger.kernel.org, kvmarm@lists.linux.dev, iommu@lists.linux.dev, linux-fsdevel@vger.kernel.org, linux-mm@kvack.org, linux-kselftest@vger.kernel.org Subject: [PATCH 7/9] iommufd/selftest: Add a mock memory provider Date: Tue, 6 Oct 2026 18:32:33 +0000 Message-ID: <20261006183235.16576-8-griffoul@gmail.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261006183235.16576-1-griffoul@gmail.com> References: <20260720111259.122911-1-dwmw2@infradead.org> <20261006183235.16576-1-griffoul@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Fred Griffoul The provider path of IOMMU_IOAS_MAP_FILE needs a provider to test against. Add a mock provider whose pages can be made holes, read only, MMIO or another shared frame, with a revoke after each change. Test that holes stay unmapped, that a revoke changes only its range, that new domains see the current state, and that provider pages and dirty tracking domains cannot share an IOAS. Signed-off-by: Fred Griffoul --- drivers/iommu/iommufd/iommufd_test.h | 20 ++ drivers/iommu/iommufd/selftest.c | 177 ++++++++++++++++++ tools/testing/selftests/iommu/iommufd.c | 119 ++++++++++++ tools/testing/selftests/iommu/iommufd_utils.h | 64 +++++++ 4 files changed, 380 insertions(+) diff --git a/drivers/iommu/iommufd/iommufd_test.h b/drivers/iommu/iommufd/iommufd_test.h index 28fd9c43edc4..5e5023dc8070 100644 --- a/drivers/iommu/iommufd/iommufd_test.h +++ b/drivers/iommu/iommufd/iommufd_test.h @@ -33,6 +33,17 @@ enum { IOMMU_TEST_OP_DMABUF_REVOKE, IOMMU_TEST_OP_MD_CHECK_MAPPED, IOMMU_TEST_OP_MD_IOVA_TO_PHYS, + IOMMU_TEST_OP_PROVIDER_GET, + IOMMU_TEST_OP_PROVIDER_SET, +}; + +/* Page states of the mock memory provider, IOMMU_TEST_OP_PROVIDER_SET */ +enum { + MOCK_PROVIDER_BACKED = 0, + MOCK_PROVIDER_HOLE, + MOCK_PROVIDER_READONLY, + MOCK_PROVIDER_OTHER, + MOCK_PROVIDER_MMIO, }; enum { @@ -209,6 +220,15 @@ struct iommu_test_cmd { __aligned_u64 iova; __aligned_u64 out_phys; /* 0 if unmapped */ } iova_to_phys; + struct { + __u32 length; + } provider_get; + struct { + __s32 fd; + __u32 state; + __aligned_u64 offset; + __aligned_u64 length; + } provider_set; }; __u32 last; }; diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c index 1f5cd2d00fda..270c41e6dd77 100644 --- a/drivers/iommu/iommufd/selftest.c +++ b/drivers/iommu/iommufd/selftest.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -2132,6 +2133,174 @@ static int iommufd_test_md_check_mapped(struct iommufd_ucmd *ucmd, return rc; } +/* + * A mock memory provider. Each page of the file is backed by its own page of + * RAM, or is a hole, read only, the one page that every page in the OTHER + * state shares, or reported as MMIO. + * IOMMU_TEST_OP_PROVIDER_SET changes the state of a range and revokes it. + */ +struct iommufd_test_provider { + struct mem_provider_file mpf; + size_t npages; + struct page **pages; + struct page *other; + u8 *state; +}; + +static void iommufd_test_provider_free(struct iommufd_test_provider *tp) +{ + size_t i; + + for (i = 0; i != tp->npages; i++) + if (tp->pages[i]) + __free_page(tp->pages[i]); + if (tp->other) + __free_page(tp->other); + kfree(tp->state); + kfree(tp->pages); + kfree(tp); +} + +static int iommufd_test_provider_release(struct inode *inode, + struct file *file) +{ + iommufd_test_provider_free(file->private_data); + return 0; +} + +static struct mem_provider_file * +iommufd_test_provider_attach(struct file *file, loff_t size) +{ + struct iommufd_test_provider *tp = file->private_data; + + if (size > (loff_t)tp->npages * PAGE_SIZE) + return ERR_PTR(-EINVAL); + return &tp->mpf; +} + +static void iommufd_test_provider_detach(struct mem_provider_file *mpf) +{ +} + +static int iommufd_test_provider_get_page(struct mem_provider_file *mpf, + pgoff_t index, unsigned long *pfn, + int *max_order, u32 *attrs) +{ + struct iommufd_test_provider *tp = + container_of(mpf, struct iommufd_test_provider, mpf); + + *max_order = 0; + switch (READ_ONCE(tp->state[index])) { + case MOCK_PROVIDER_HOLE: + return -EFAULT; + case MOCK_PROVIDER_READONLY: + *attrs = MEM_PROVIDER_ATTR_READONLY; + break; + case MOCK_PROVIDER_OTHER: + *pfn = page_to_pfn(tp->other); + return 0; + case MOCK_PROVIDER_MMIO: + *attrs = MEM_PROVIDER_TYPE_MMIO; + break; + } + *pfn = page_to_pfn(tp->pages[index]); + return 0; +} + +static const struct mem_provider_ops iommufd_test_provider_ops = { + .attach = iommufd_test_provider_attach, + .detach = iommufd_test_provider_detach, + .get_page = iommufd_test_provider_get_page, +}; + +static const struct mem_provider_fops iommufd_test_provider_fops = { + .fops = { + .owner = THIS_MODULE, + .fop_flags = FOP_MEM_PROVIDER, + .release = iommufd_test_provider_release, + }, + .ops = &iommufd_test_provider_ops, +}; + +static int iommufd_test_provider_get(struct iommufd_ucmd *ucmd, size_t len) +{ + struct iommufd_test_provider *tp; + struct file *file; + size_t i; + int fd, rc; + + len = ALIGN(len, PAGE_SIZE); + if (len == 0 || len > PAGE_SIZE * 512) + return -EINVAL; + + tp = kzalloc_obj(*tp); + if (!tp) + return -ENOMEM; + mem_provider_file_init(&tp->mpf); + tp->npages = len / PAGE_SIZE; + tp->pages = kcalloc(tp->npages, sizeof(*tp->pages), GFP_KERNEL); + tp->state = kcalloc(tp->npages, sizeof(*tp->state), GFP_KERNEL); + tp->other = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!tp->pages || !tp->state || !tp->other) { + rc = -ENOMEM; + goto err_free; + } + for (i = 0; i != tp->npages; i++) { + tp->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!tp->pages[i]) { + rc = -ENOMEM; + goto err_free; + } + } + + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) { + rc = fd; + goto err_free; + } + file = anon_inode_getfile("[iommufd-test-provider]", + &iommufd_test_provider_fops.fops, tp, O_RDWR); + if (IS_ERR(file)) { + put_unused_fd(fd); + rc = PTR_ERR(file); + goto err_free; + } + fd_install(fd, file); + return fd; + +err_free: + iommufd_test_provider_free(tp); + return rc; +} + +static int iommufd_test_provider_set(struct iommufd_ucmd *ucmd, int fd, + unsigned int state, u64 offset, u64 length) +{ + struct iommufd_test_provider *tp; + u64 end; + size_t i; + + if (state > MOCK_PROVIDER_MMIO) + return -EINVAL; + + CLASS(fd, f)(fd); + if (fd_empty(f)) + return -EBADF; + if (fd_file(f)->f_op != &iommufd_test_provider_fops.fops) + return -EINVAL; + tp = fd_file(f)->private_data; + + if (!length || check_add_overflow(offset, length, &end) || + !PAGE_ALIGNED(offset) || !PAGE_ALIGNED(length) || + end > (u64)tp->npages * PAGE_SIZE) + return -EINVAL; + + for (i = offset / PAGE_SIZE; i != end / PAGE_SIZE; i++) + WRITE_ONCE(tp->state[i], state); + mem_provider_revoke(&tp->mpf, offset, length); + return 0; +} + static int iommufd_test_dmabuf_revoke(struct iommufd_ucmd *ucmd, int fd, bool revoked) { @@ -2252,6 +2421,14 @@ int iommufd_test(struct iommufd_ucmd *ucmd) case IOMMU_TEST_OP_MD_IOVA_TO_PHYS: return iommufd_test_md_iova_to_phys(ucmd, cmd->id, cmd->iova_to_phys.iova); + case IOMMU_TEST_OP_PROVIDER_GET: + return iommufd_test_provider_get(ucmd, + cmd->provider_get.length); + case IOMMU_TEST_OP_PROVIDER_SET: + return iommufd_test_provider_set(ucmd, cmd->provider_set.fd, + cmd->provider_set.state, + cmd->provider_set.offset, + cmd->provider_set.length); default: return -EOPNOTSUPP; } diff --git a/tools/testing/selftests/iommu/iommufd.c b/tools/testing/selftests/iommu/iommufd.c index d44b34b05757..ebf81ca3af18 100644 --- a/tools/testing/selftests/iommu/iommufd.c +++ b/tools/testing/selftests/iommu/iommufd.c @@ -1627,6 +1627,125 @@ TEST_F(iommufd_ioas, dmabuf_revoke) close(dfd); } +TEST_F(iommufd_ioas, provider_simple) +{ + size_t buf_size = PAGE_SIZE * 4; + __u64 iova; + int pfd; + + test_cmd_get_provider(buf_size, &pfd); + test_err_ioctl_ioas_map_file(EINVAL, pfd, 0, 0, &iova); + test_err_ioctl_ioas_map_file(EINVAL, pfd, 0, buf_size + PAGE_SIZE, + &iova); + test_err_ioctl_ioas_map_file(EINVAL, pfd, 1, PAGE_SIZE, &iova); + test_ioctl_ioas_map_file(pfd, 0, buf_size, &iova); + if (variant->mock_domains) + test_cmd_md_check_mapped(self->hwpt_id, iova, buf_size, true); + + /* The mapping keeps the provider file alive */ + close(pfd); + if (variant->mock_domains) + test_cmd_md_check_mapped(self->hwpt_id, iova, buf_size, true); + test_ioctl_ioas_unmap(iova, buf_size); +} + +TEST_F(iommufd_ioas, provider_revoke) +{ + size_t buf_size = PAGE_SIZE * 8; + __u64 phys0, phys1; + __u32 hwpt_id; + __u64 iova; + int pfd; + + if (!variant->mock_domains) + SKIP(return, "Needs a mock domain"); + + test_cmd_get_provider(buf_size, &pfd); + + /* A page that is a hole when the file is mapped stays unmapped */ + test_cmd_provider_set(pfd, MOCK_PROVIDER_HOLE, PAGE_SIZE, PAGE_SIZE); + test_ioctl_ioas_map_file(pfd, 0, buf_size, &iova); + test_cmd_md_check_mapped(self->hwpt_id, iova, PAGE_SIZE, true); + test_cmd_md_check_mapped(self->hwpt_id, iova + PAGE_SIZE, PAGE_SIZE, + false); + test_cmd_md_check_mapped(self->hwpt_id, iova + 2 * PAGE_SIZE, + buf_size - 2 * PAGE_SIZE, true); + + /* Give the page back */ + test_cmd_provider_set(pfd, MOCK_PROVIDER_BACKED, PAGE_SIZE, PAGE_SIZE); + test_cmd_md_check_mapped(self->hwpt_id, iova, buf_size, true); + + /* Take back the middle, and only the middle goes */ + test_cmd_provider_set(pfd, MOCK_PROVIDER_HOLE, 2 * PAGE_SIZE, + 4 * PAGE_SIZE); + test_cmd_md_check_mapped(self->hwpt_id, iova, 2 * PAGE_SIZE, true); + test_cmd_md_check_mapped(self->hwpt_id, iova + 2 * PAGE_SIZE, + 4 * PAGE_SIZE, false); + test_cmd_md_check_mapped(self->hwpt_id, iova + 6 * PAGE_SIZE, + 2 * PAGE_SIZE, true); + + /* A domain added now sees the same holes */ + test_cmd_hwpt_alloc(self->device_id, self->ioas_id, 0, &hwpt_id); + test_cmd_md_check_mapped(hwpt_id, iova, 2 * PAGE_SIZE, true); + test_cmd_md_check_mapped(hwpt_id, iova + 2 * PAGE_SIZE, 4 * PAGE_SIZE, + false); + test_cmd_md_check_mapped(hwpt_id, iova + 6 * PAGE_SIZE, 2 * PAGE_SIZE, + true); + test_ioctl_destroy(hwpt_id); + + /* Put the same other frame behind two pages */ + phys0 = test_cmd_md_iova_to_phys(self->hwpt_id, iova); + test_cmd_provider_set(pfd, MOCK_PROVIDER_OTHER, 0, 2 * PAGE_SIZE); + phys1 = test_cmd_md_iova_to_phys(self->hwpt_id, iova); + ASSERT_NE(0, phys1); + ASSERT_NE(phys0, phys1); + ASSERT_EQ(phys1, + test_cmd_md_iova_to_phys(self->hwpt_id, iova + PAGE_SIZE)); + + /* Read-only and MMIO pages stay mapped */ + test_cmd_provider_set(pfd, MOCK_PROVIDER_READONLY, 0, buf_size / 2); + test_cmd_provider_set(pfd, MOCK_PROVIDER_MMIO, buf_size / 2, + buf_size / 2); + test_cmd_md_check_mapped(self->hwpt_id, iova, buf_size, true); + + /* A change past the end of the file is refused */ + EXPECT_ERRNO(EINVAL, _test_cmd_provider_set(self->fd, pfd, + MOCK_PROVIDER_HOLE, + buf_size, PAGE_SIZE)); + + test_ioctl_ioas_unmap(iova, buf_size); + close(pfd); +} + +TEST_F(iommufd_ioas, provider_dirty_tracking) +{ + size_t buf_size = PAGE_SIZE * 4; + __u32 hwpt_id; + __u64 iova; + int pfd; + + if (!variant->mock_domains) + SKIP(return, "Needs a mock domain"); + + test_cmd_get_provider(buf_size, &pfd); + + /* No dirty tracking domain over provider pages ... */ + test_ioctl_ioas_map_file(pfd, 0, buf_size, &iova); + test_err_hwpt_alloc(EOPNOTSUPP, self->device_id, self->ioas_id, + IOMMU_HWPT_ALLOC_DIRTY_TRACKING, &hwpt_id); + test_ioctl_ioas_unmap(iova, buf_size); + + /* ... and no provider pages under a dirty tracking domain. */ + test_cmd_hwpt_alloc(self->device_id, self->ioas_id, + IOMMU_HWPT_ALLOC_DIRTY_TRACKING, &hwpt_id); + test_err_ioctl_ioas_map_file(EOPNOTSUPP, pfd, 0, buf_size, &iova); + test_ioctl_destroy(hwpt_id); + + test_ioctl_ioas_map_file(pfd, 0, buf_size, &iova); + test_ioctl_ioas_unmap(iova, buf_size); + close(pfd); +} + FIXTURE(iommufd_mock_domain) { int fd; diff --git a/tools/testing/selftests/iommu/iommufd_utils.h b/tools/testing/selftests/iommu/iommufd_utils.h index b4928cbd4d9c..d1bd0a283db2 100644 --- a/tools/testing/selftests/iommu/iommufd_utils.h +++ b/tools/testing/selftests/iommu/iommufd_utils.h @@ -593,6 +593,70 @@ static int _test_cmd_revoke_dmabuf(int fd, int dmabuf_fd, bool revoked) #define test_cmd_revoke_dmabuf(dmabuf_fd, revoke) \ ASSERT_EQ(0, _test_cmd_revoke_dmabuf(self->fd, dmabuf_fd, revoke)) +static int _test_cmd_get_provider(int fd, size_t len, int *out_fd) +{ + struct iommu_test_cmd cmd = { + .size = sizeof(cmd), + .op = IOMMU_TEST_OP_PROVIDER_GET, + .provider_get = { .length = len }, + }; + + *out_fd = ioctl(fd, IOMMU_TEST_CMD, &cmd); + if (*out_fd < 0) + return -1; + return 0; +} +#define test_cmd_get_provider(len, out_fd) \ + ASSERT_EQ(0, _test_cmd_get_provider(self->fd, len, out_fd)) + +static int _test_cmd_provider_set(int fd, int pfd, unsigned int state, + __u64 offset, __u64 length) +{ + struct iommu_test_cmd cmd = { + .size = sizeof(cmd), + .op = IOMMU_TEST_OP_PROVIDER_SET, + .provider_set = { .fd = pfd, .state = state, + .offset = offset, .length = length }, + }; + + return ioctl(fd, IOMMU_TEST_CMD, &cmd); +} +#define test_cmd_provider_set(pfd, state, offset, length) \ + ASSERT_EQ(0, _test_cmd_provider_set(self->fd, pfd, state, offset, length)) + +static int _test_cmd_md_check_mapped(int fd, __u32 hwpt_id, __u64 iova, + __u64 length, bool mapped) +{ + struct iommu_test_cmd cmd = { + .size = sizeof(cmd), + .op = IOMMU_TEST_OP_MD_CHECK_MAPPED, + .id = hwpt_id, + .check_mapped = { .mapped = mapped, .iova = iova, + .length = length }, + }; + + return ioctl(fd, IOMMU_TEST_CMD, &cmd); +} +#define test_cmd_md_check_mapped(hwpt_id, iova, length, mapped) \ + ASSERT_EQ(0, _test_cmd_md_check_mapped(self->fd, hwpt_id, iova, \ + length, mapped)) + +static __u64 _test_cmd_md_iova_to_phys(int fd, __u32 hwpt_id, __u64 iova) +{ + struct iommu_test_cmd cmd = { + .size = sizeof(cmd), + .op = IOMMU_TEST_OP_MD_IOVA_TO_PHYS, + .id = hwpt_id, + .iova_to_phys = { .iova = iova }, + }; + + if (ioctl(fd, IOMMU_TEST_CMD, &cmd)) + return -1; + return cmd.iova_to_phys.out_phys; +} +#define test_cmd_md_iova_to_phys(hwpt_id, iova) \ + _test_cmd_md_iova_to_phys(self->fd, hwpt_id, iova) + static int _test_ioctl_destroy(int fd, unsigned int id) { struct iommu_destroy cmd = {