162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 262306a36Sopenharmony_ci/* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES */ 362306a36Sopenharmony_ci#ifndef __SELFTEST_IOMMUFD_UTILS 462306a36Sopenharmony_ci#define __SELFTEST_IOMMUFD_UTILS 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <unistd.h> 762306a36Sopenharmony_ci#include <stddef.h> 862306a36Sopenharmony_ci#include <sys/fcntl.h> 962306a36Sopenharmony_ci#include <sys/ioctl.h> 1062306a36Sopenharmony_ci#include <stdint.h> 1162306a36Sopenharmony_ci#include <assert.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "../kselftest_harness.h" 1462306a36Sopenharmony_ci#include "../../../../drivers/iommu/iommufd/iommufd_test.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* Hack to make assertions more readable */ 1762306a36Sopenharmony_ci#define _IOMMU_TEST_CMD(x) IOMMU_TEST_CMD 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic void *buffer; 2062306a36Sopenharmony_cistatic unsigned long BUFFER_SIZE; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic unsigned long PAGE_SIZE; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) 2562306a36Sopenharmony_ci#define offsetofend(TYPE, MEMBER) \ 2662306a36Sopenharmony_ci (offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER)) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Have the kernel check the refcount on pages. I don't know why a freshly 3062306a36Sopenharmony_ci * mmap'd anon non-compound page starts out with a ref of 3 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci#define check_refs(_ptr, _length, _refs) \ 3362306a36Sopenharmony_ci ({ \ 3462306a36Sopenharmony_ci struct iommu_test_cmd test_cmd = { \ 3562306a36Sopenharmony_ci .size = sizeof(test_cmd), \ 3662306a36Sopenharmony_ci .op = IOMMU_TEST_OP_MD_CHECK_REFS, \ 3762306a36Sopenharmony_ci .check_refs = { .length = _length, \ 3862306a36Sopenharmony_ci .uptr = (uintptr_t)(_ptr), \ 3962306a36Sopenharmony_ci .refs = _refs }, \ 4062306a36Sopenharmony_ci }; \ 4162306a36Sopenharmony_ci ASSERT_EQ(0, \ 4262306a36Sopenharmony_ci ioctl(self->fd, \ 4362306a36Sopenharmony_ci _IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_REFS), \ 4462306a36Sopenharmony_ci &test_cmd)); \ 4562306a36Sopenharmony_ci }) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int _test_cmd_mock_domain(int fd, unsigned int ioas_id, __u32 *stdev_id, 4862306a36Sopenharmony_ci __u32 *hwpt_id, __u32 *idev_id) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct iommu_test_cmd cmd = { 5162306a36Sopenharmony_ci .size = sizeof(cmd), 5262306a36Sopenharmony_ci .op = IOMMU_TEST_OP_MOCK_DOMAIN, 5362306a36Sopenharmony_ci .id = ioas_id, 5462306a36Sopenharmony_ci .mock_domain = {}, 5562306a36Sopenharmony_ci }; 5662306a36Sopenharmony_ci int ret; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); 5962306a36Sopenharmony_ci if (ret) 6062306a36Sopenharmony_ci return ret; 6162306a36Sopenharmony_ci if (stdev_id) 6262306a36Sopenharmony_ci *stdev_id = cmd.mock_domain.out_stdev_id; 6362306a36Sopenharmony_ci assert(cmd.id != 0); 6462306a36Sopenharmony_ci if (hwpt_id) 6562306a36Sopenharmony_ci *hwpt_id = cmd.mock_domain.out_hwpt_id; 6662306a36Sopenharmony_ci if (idev_id) 6762306a36Sopenharmony_ci *idev_id = cmd.mock_domain.out_idev_id; 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci#define test_cmd_mock_domain(ioas_id, stdev_id, hwpt_id, idev_id) \ 7162306a36Sopenharmony_ci ASSERT_EQ(0, _test_cmd_mock_domain(self->fd, ioas_id, stdev_id, \ 7262306a36Sopenharmony_ci hwpt_id, idev_id)) 7362306a36Sopenharmony_ci#define test_err_mock_domain(_errno, ioas_id, stdev_id, hwpt_id) \ 7462306a36Sopenharmony_ci EXPECT_ERRNO(_errno, _test_cmd_mock_domain(self->fd, ioas_id, \ 7562306a36Sopenharmony_ci stdev_id, hwpt_id, NULL)) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int _test_cmd_mock_domain_replace(int fd, __u32 stdev_id, __u32 pt_id, 7862306a36Sopenharmony_ci __u32 *hwpt_id) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct iommu_test_cmd cmd = { 8162306a36Sopenharmony_ci .size = sizeof(cmd), 8262306a36Sopenharmony_ci .op = IOMMU_TEST_OP_MOCK_DOMAIN_REPLACE, 8362306a36Sopenharmony_ci .id = stdev_id, 8462306a36Sopenharmony_ci .mock_domain_replace = { 8562306a36Sopenharmony_ci .pt_id = pt_id, 8662306a36Sopenharmony_ci }, 8762306a36Sopenharmony_ci }; 8862306a36Sopenharmony_ci int ret; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); 9162306a36Sopenharmony_ci if (ret) 9262306a36Sopenharmony_ci return ret; 9362306a36Sopenharmony_ci if (hwpt_id) 9462306a36Sopenharmony_ci *hwpt_id = cmd.mock_domain_replace.pt_id; 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define test_cmd_mock_domain_replace(stdev_id, pt_id) \ 9962306a36Sopenharmony_ci ASSERT_EQ(0, _test_cmd_mock_domain_replace(self->fd, stdev_id, pt_id, \ 10062306a36Sopenharmony_ci NULL)) 10162306a36Sopenharmony_ci#define test_err_mock_domain_replace(_errno, stdev_id, pt_id) \ 10262306a36Sopenharmony_ci EXPECT_ERRNO(_errno, _test_cmd_mock_domain_replace(self->fd, stdev_id, \ 10362306a36Sopenharmony_ci pt_id, NULL)) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int _test_cmd_hwpt_alloc(int fd, __u32 device_id, __u32 pt_id, 10662306a36Sopenharmony_ci __u32 *hwpt_id) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct iommu_hwpt_alloc cmd = { 10962306a36Sopenharmony_ci .size = sizeof(cmd), 11062306a36Sopenharmony_ci .dev_id = device_id, 11162306a36Sopenharmony_ci .pt_id = pt_id, 11262306a36Sopenharmony_ci }; 11362306a36Sopenharmony_ci int ret; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci ret = ioctl(fd, IOMMU_HWPT_ALLOC, &cmd); 11662306a36Sopenharmony_ci if (ret) 11762306a36Sopenharmony_ci return ret; 11862306a36Sopenharmony_ci if (hwpt_id) 11962306a36Sopenharmony_ci *hwpt_id = cmd.out_hwpt_id; 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#define test_cmd_hwpt_alloc(device_id, pt_id, hwpt_id) \ 12462306a36Sopenharmony_ci ASSERT_EQ(0, _test_cmd_hwpt_alloc(self->fd, device_id, pt_id, hwpt_id)) 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int _test_cmd_access_replace_ioas(int fd, __u32 access_id, 12762306a36Sopenharmony_ci unsigned int ioas_id) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct iommu_test_cmd cmd = { 13062306a36Sopenharmony_ci .size = sizeof(cmd), 13162306a36Sopenharmony_ci .op = IOMMU_TEST_OP_ACCESS_REPLACE_IOAS, 13262306a36Sopenharmony_ci .id = access_id, 13362306a36Sopenharmony_ci .access_replace_ioas = { .ioas_id = ioas_id }, 13462306a36Sopenharmony_ci }; 13562306a36Sopenharmony_ci int ret; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); 13862306a36Sopenharmony_ci if (ret) 13962306a36Sopenharmony_ci return ret; 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci#define test_cmd_access_replace_ioas(access_id, ioas_id) \ 14362306a36Sopenharmony_ci ASSERT_EQ(0, _test_cmd_access_replace_ioas(self->fd, access_id, ioas_id)) 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int _test_cmd_create_access(int fd, unsigned int ioas_id, 14662306a36Sopenharmony_ci __u32 *access_id, unsigned int flags) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct iommu_test_cmd cmd = { 14962306a36Sopenharmony_ci .size = sizeof(cmd), 15062306a36Sopenharmony_ci .op = IOMMU_TEST_OP_CREATE_ACCESS, 15162306a36Sopenharmony_ci .id = ioas_id, 15262306a36Sopenharmony_ci .create_access = { .flags = flags }, 15362306a36Sopenharmony_ci }; 15462306a36Sopenharmony_ci int ret; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); 15762306a36Sopenharmony_ci if (ret) 15862306a36Sopenharmony_ci return ret; 15962306a36Sopenharmony_ci *access_id = cmd.create_access.out_access_fd; 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci#define test_cmd_create_access(ioas_id, access_id, flags) \ 16362306a36Sopenharmony_ci ASSERT_EQ(0, _test_cmd_create_access(self->fd, ioas_id, access_id, \ 16462306a36Sopenharmony_ci flags)) 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int _test_cmd_destroy_access(unsigned int access_id) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci return close(access_id); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci#define test_cmd_destroy_access(access_id) \ 17162306a36Sopenharmony_ci ASSERT_EQ(0, _test_cmd_destroy_access(access_id)) 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int _test_cmd_destroy_access_pages(int fd, unsigned int access_id, 17462306a36Sopenharmony_ci unsigned int access_pages_id) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct iommu_test_cmd cmd = { 17762306a36Sopenharmony_ci .size = sizeof(cmd), 17862306a36Sopenharmony_ci .op = IOMMU_TEST_OP_DESTROY_ACCESS_PAGES, 17962306a36Sopenharmony_ci .id = access_id, 18062306a36Sopenharmony_ci .destroy_access_pages = { .access_pages_id = access_pages_id }, 18162306a36Sopenharmony_ci }; 18262306a36Sopenharmony_ci return ioctl(fd, IOMMU_TEST_CMD, &cmd); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci#define test_cmd_destroy_access_pages(access_id, access_pages_id) \ 18562306a36Sopenharmony_ci ASSERT_EQ(0, _test_cmd_destroy_access_pages(self->fd, access_id, \ 18662306a36Sopenharmony_ci access_pages_id)) 18762306a36Sopenharmony_ci#define test_err_destroy_access_pages(_errno, access_id, access_pages_id) \ 18862306a36Sopenharmony_ci EXPECT_ERRNO(_errno, _test_cmd_destroy_access_pages( \ 18962306a36Sopenharmony_ci self->fd, access_id, access_pages_id)) 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int _test_ioctl_destroy(int fd, unsigned int id) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct iommu_destroy cmd = { 19462306a36Sopenharmony_ci .size = sizeof(cmd), 19562306a36Sopenharmony_ci .id = id, 19662306a36Sopenharmony_ci }; 19762306a36Sopenharmony_ci return ioctl(fd, IOMMU_DESTROY, &cmd); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci#define test_ioctl_destroy(id) ASSERT_EQ(0, _test_ioctl_destroy(self->fd, id)) 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int _test_ioctl_ioas_alloc(int fd, __u32 *id) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct iommu_ioas_alloc cmd = { 20462306a36Sopenharmony_ci .size = sizeof(cmd), 20562306a36Sopenharmony_ci }; 20662306a36Sopenharmony_ci int ret; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci ret = ioctl(fd, IOMMU_IOAS_ALLOC, &cmd); 20962306a36Sopenharmony_ci if (ret) 21062306a36Sopenharmony_ci return ret; 21162306a36Sopenharmony_ci *id = cmd.out_ioas_id; 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci#define test_ioctl_ioas_alloc(id) \ 21562306a36Sopenharmony_ci ({ \ 21662306a36Sopenharmony_ci ASSERT_EQ(0, _test_ioctl_ioas_alloc(self->fd, id)); \ 21762306a36Sopenharmony_ci ASSERT_NE(0, *(id)); \ 21862306a36Sopenharmony_ci }) 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int _test_ioctl_ioas_map(int fd, unsigned int ioas_id, void *buffer, 22162306a36Sopenharmony_ci size_t length, __u64 *iova, unsigned int flags) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct iommu_ioas_map cmd = { 22462306a36Sopenharmony_ci .size = sizeof(cmd), 22562306a36Sopenharmony_ci .flags = flags, 22662306a36Sopenharmony_ci .ioas_id = ioas_id, 22762306a36Sopenharmony_ci .user_va = (uintptr_t)buffer, 22862306a36Sopenharmony_ci .length = length, 22962306a36Sopenharmony_ci }; 23062306a36Sopenharmony_ci int ret; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (flags & IOMMU_IOAS_MAP_FIXED_IOVA) 23362306a36Sopenharmony_ci cmd.iova = *iova; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci ret = ioctl(fd, IOMMU_IOAS_MAP, &cmd); 23662306a36Sopenharmony_ci *iova = cmd.iova; 23762306a36Sopenharmony_ci return ret; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci#define test_ioctl_ioas_map(buffer, length, iova_p) \ 24062306a36Sopenharmony_ci ASSERT_EQ(0, _test_ioctl_ioas_map(self->fd, self->ioas_id, buffer, \ 24162306a36Sopenharmony_ci length, iova_p, \ 24262306a36Sopenharmony_ci IOMMU_IOAS_MAP_WRITEABLE | \ 24362306a36Sopenharmony_ci IOMMU_IOAS_MAP_READABLE)) 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci#define test_err_ioctl_ioas_map(_errno, buffer, length, iova_p) \ 24662306a36Sopenharmony_ci EXPECT_ERRNO(_errno, \ 24762306a36Sopenharmony_ci _test_ioctl_ioas_map(self->fd, self->ioas_id, buffer, \ 24862306a36Sopenharmony_ci length, iova_p, \ 24962306a36Sopenharmony_ci IOMMU_IOAS_MAP_WRITEABLE | \ 25062306a36Sopenharmony_ci IOMMU_IOAS_MAP_READABLE)) 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci#define test_ioctl_ioas_map_id(ioas_id, buffer, length, iova_p) \ 25362306a36Sopenharmony_ci ASSERT_EQ(0, _test_ioctl_ioas_map(self->fd, ioas_id, buffer, length, \ 25462306a36Sopenharmony_ci iova_p, \ 25562306a36Sopenharmony_ci IOMMU_IOAS_MAP_WRITEABLE | \ 25662306a36Sopenharmony_ci IOMMU_IOAS_MAP_READABLE)) 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci#define test_ioctl_ioas_map_fixed(buffer, length, iova) \ 25962306a36Sopenharmony_ci ({ \ 26062306a36Sopenharmony_ci __u64 __iova = iova; \ 26162306a36Sopenharmony_ci ASSERT_EQ(0, _test_ioctl_ioas_map( \ 26262306a36Sopenharmony_ci self->fd, self->ioas_id, buffer, length, \ 26362306a36Sopenharmony_ci &__iova, \ 26462306a36Sopenharmony_ci IOMMU_IOAS_MAP_FIXED_IOVA | \ 26562306a36Sopenharmony_ci IOMMU_IOAS_MAP_WRITEABLE | \ 26662306a36Sopenharmony_ci IOMMU_IOAS_MAP_READABLE)); \ 26762306a36Sopenharmony_ci }) 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci#define test_err_ioctl_ioas_map_fixed(_errno, buffer, length, iova) \ 27062306a36Sopenharmony_ci ({ \ 27162306a36Sopenharmony_ci __u64 __iova = iova; \ 27262306a36Sopenharmony_ci EXPECT_ERRNO(_errno, \ 27362306a36Sopenharmony_ci _test_ioctl_ioas_map( \ 27462306a36Sopenharmony_ci self->fd, self->ioas_id, buffer, length, \ 27562306a36Sopenharmony_ci &__iova, \ 27662306a36Sopenharmony_ci IOMMU_IOAS_MAP_FIXED_IOVA | \ 27762306a36Sopenharmony_ci IOMMU_IOAS_MAP_WRITEABLE | \ 27862306a36Sopenharmony_ci IOMMU_IOAS_MAP_READABLE)); \ 27962306a36Sopenharmony_ci }) 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int _test_ioctl_ioas_unmap(int fd, unsigned int ioas_id, uint64_t iova, 28262306a36Sopenharmony_ci size_t length, uint64_t *out_len) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct iommu_ioas_unmap cmd = { 28562306a36Sopenharmony_ci .size = sizeof(cmd), 28662306a36Sopenharmony_ci .ioas_id = ioas_id, 28762306a36Sopenharmony_ci .iova = iova, 28862306a36Sopenharmony_ci .length = length, 28962306a36Sopenharmony_ci }; 29062306a36Sopenharmony_ci int ret; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ret = ioctl(fd, IOMMU_IOAS_UNMAP, &cmd); 29362306a36Sopenharmony_ci if (out_len) 29462306a36Sopenharmony_ci *out_len = cmd.length; 29562306a36Sopenharmony_ci return ret; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci#define test_ioctl_ioas_unmap(iova, length) \ 29862306a36Sopenharmony_ci ASSERT_EQ(0, _test_ioctl_ioas_unmap(self->fd, self->ioas_id, iova, \ 29962306a36Sopenharmony_ci length, NULL)) 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci#define test_ioctl_ioas_unmap_id(ioas_id, iova, length) \ 30262306a36Sopenharmony_ci ASSERT_EQ(0, _test_ioctl_ioas_unmap(self->fd, ioas_id, iova, length, \ 30362306a36Sopenharmony_ci NULL)) 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci#define test_err_ioctl_ioas_unmap(_errno, iova, length) \ 30662306a36Sopenharmony_ci EXPECT_ERRNO(_errno, _test_ioctl_ioas_unmap(self->fd, self->ioas_id, \ 30762306a36Sopenharmony_ci iova, length, NULL)) 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int _test_ioctl_set_temp_memory_limit(int fd, unsigned int limit) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct iommu_test_cmd memlimit_cmd = { 31262306a36Sopenharmony_ci .size = sizeof(memlimit_cmd), 31362306a36Sopenharmony_ci .op = IOMMU_TEST_OP_SET_TEMP_MEMORY_LIMIT, 31462306a36Sopenharmony_ci .memory_limit = { .limit = limit }, 31562306a36Sopenharmony_ci }; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_SET_TEMP_MEMORY_LIMIT), 31862306a36Sopenharmony_ci &memlimit_cmd); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci#define test_ioctl_set_temp_memory_limit(limit) \ 32262306a36Sopenharmony_ci ASSERT_EQ(0, _test_ioctl_set_temp_memory_limit(self->fd, limit)) 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci#define test_ioctl_set_default_memory_limit() \ 32562306a36Sopenharmony_ci test_ioctl_set_temp_memory_limit(65536) 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic void teardown_iommufd(int fd, struct __test_metadata *_metadata) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct iommu_test_cmd test_cmd = { 33062306a36Sopenharmony_ci .size = sizeof(test_cmd), 33162306a36Sopenharmony_ci .op = IOMMU_TEST_OP_MD_CHECK_REFS, 33262306a36Sopenharmony_ci .check_refs = { .length = BUFFER_SIZE, 33362306a36Sopenharmony_ci .uptr = (uintptr_t)buffer }, 33462306a36Sopenharmony_ci }; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (fd == -1) 33762306a36Sopenharmony_ci return; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci EXPECT_EQ(0, close(fd)); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci fd = open("/dev/iommu", O_RDWR); 34262306a36Sopenharmony_ci EXPECT_NE(-1, fd); 34362306a36Sopenharmony_ci EXPECT_EQ(0, ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_REFS), 34462306a36Sopenharmony_ci &test_cmd)); 34562306a36Sopenharmony_ci EXPECT_EQ(0, close(fd)); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci#define EXPECT_ERRNO(expected_errno, cmd) \ 34962306a36Sopenharmony_ci ({ \ 35062306a36Sopenharmony_ci ASSERT_EQ(-1, cmd); \ 35162306a36Sopenharmony_ci EXPECT_EQ(expected_errno, errno); \ 35262306a36Sopenharmony_ci }) 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci#endif 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci/* @data can be NULL */ 35762306a36Sopenharmony_cistatic int _test_cmd_get_hw_info(int fd, __u32 device_id, 35862306a36Sopenharmony_ci void *data, size_t data_len) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct iommu_test_hw_info *info = (struct iommu_test_hw_info *)data; 36162306a36Sopenharmony_ci struct iommu_hw_info cmd = { 36262306a36Sopenharmony_ci .size = sizeof(cmd), 36362306a36Sopenharmony_ci .dev_id = device_id, 36462306a36Sopenharmony_ci .data_len = data_len, 36562306a36Sopenharmony_ci .data_uptr = (uint64_t)data, 36662306a36Sopenharmony_ci }; 36762306a36Sopenharmony_ci int ret; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci ret = ioctl(fd, IOMMU_GET_HW_INFO, &cmd); 37062306a36Sopenharmony_ci if (ret) 37162306a36Sopenharmony_ci return ret; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci assert(cmd.out_data_type == IOMMU_HW_INFO_TYPE_SELFTEST); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* 37662306a36Sopenharmony_ci * The struct iommu_test_hw_info should be the one defined 37762306a36Sopenharmony_ci * by the current kernel. 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_ci assert(cmd.data_len == sizeof(struct iommu_test_hw_info)); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* 38262306a36Sopenharmony_ci * Trailing bytes should be 0 if user buffer is larger than 38362306a36Sopenharmony_ci * the data that kernel reports. 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_ci if (data_len > cmd.data_len) { 38662306a36Sopenharmony_ci char *ptr = (char *)(data + cmd.data_len); 38762306a36Sopenharmony_ci int idx = 0; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci while (idx < data_len - cmd.data_len) { 39062306a36Sopenharmony_ci assert(!*(ptr + idx)); 39162306a36Sopenharmony_ci idx++; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (info) { 39662306a36Sopenharmony_ci if (data_len >= offsetofend(struct iommu_test_hw_info, test_reg)) 39762306a36Sopenharmony_ci assert(info->test_reg == IOMMU_HW_INFO_SELFTEST_REGVAL); 39862306a36Sopenharmony_ci if (data_len >= offsetofend(struct iommu_test_hw_info, flags)) 39962306a36Sopenharmony_ci assert(!info->flags); 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci#define test_cmd_get_hw_info(device_id, data, data_len) \ 40662306a36Sopenharmony_ci ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, \ 40762306a36Sopenharmony_ci data, data_len)) 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci#define test_err_get_hw_info(_errno, device_id, data, data_len) \ 41062306a36Sopenharmony_ci EXPECT_ERRNO(_errno, \ 41162306a36Sopenharmony_ci _test_cmd_get_hw_info(self->fd, device_id, \ 41262306a36Sopenharmony_ci data, data_len)) 413