Path: blob/master/tools/testing/selftests/iommu/iommufd_utils.h
26302 views
/* SPDX-License-Identifier: GPL-2.0-only */1/* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES */2#ifndef __SELFTEST_IOMMUFD_UTILS3#define __SELFTEST_IOMMUFD_UTILS45#include <unistd.h>6#include <stddef.h>7#include <sys/fcntl.h>8#include <sys/ioctl.h>9#include <stdint.h>10#include <assert.h>11#include <poll.h>1213#include "../kselftest_harness.h"14#include "../../../../drivers/iommu/iommufd/iommufd_test.h"1516/* Hack to make assertions more readable */17#define _IOMMU_TEST_CMD(x) IOMMU_TEST_CMD1819/* Imported from include/asm-generic/bitops/generic-non-atomic.h */20#define BITS_PER_BYTE 821#define BITS_PER_LONG __BITS_PER_LONG22#define BIT_MASK(nr) (1UL << ((nr) % __BITS_PER_LONG))23#define BIT_WORD(nr) ((nr) / __BITS_PER_LONG)2425enum {26IOPT_PAGES_ACCOUNT_NONE = 0,27IOPT_PAGES_ACCOUNT_USER = 1,28IOPT_PAGES_ACCOUNT_MM = 2,29};3031#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))3233static inline void set_bit(unsigned int nr, unsigned long *addr)34{35unsigned long mask = BIT_MASK(nr);36unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);3738*p |= mask;39}4041static inline bool test_bit(unsigned int nr, unsigned long *addr)42{43return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG - 1)));44}4546static void *buffer;47static unsigned long BUFFER_SIZE;4849static void *mfd_buffer;50static int mfd;5152static unsigned long PAGE_SIZE;5354#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER))55#define offsetofend(TYPE, MEMBER) \56(offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER))5758#define test_err_mmap(_errno, length, offset) \59EXPECT_ERRNO(_errno, (long)mmap(NULL, length, PROT_READ | PROT_WRITE, \60MAP_SHARED, self->fd, offset))6162static inline void *memfd_mmap(size_t length, int prot, int flags, int *mfd_p)63{64int mfd_flags = (flags & MAP_HUGETLB) ? MFD_HUGETLB : 0;65int mfd = memfd_create("buffer", mfd_flags);66void *buf = MAP_FAILED;6768if (mfd <= 0)69return MAP_FAILED;70if (ftruncate(mfd, length))71goto out;72*mfd_p = mfd;73buf = mmap(0, length, prot, flags, mfd, 0);74out:75if (buf == MAP_FAILED)76close(mfd);77return buf;78}7980/*81* Have the kernel check the refcount on pages. I don't know why a freshly82* mmap'd anon non-compound page starts out with a ref of 383*/84#define check_refs(_ptr, _length, _refs) \85({ \86struct iommu_test_cmd test_cmd = { \87.size = sizeof(test_cmd), \88.op = IOMMU_TEST_OP_MD_CHECK_REFS, \89.check_refs = { .length = _length, \90.uptr = (uintptr_t)(_ptr), \91.refs = _refs }, \92}; \93ASSERT_EQ(0, \94ioctl(self->fd, \95_IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_REFS), \96&test_cmd)); \97})9899static int _test_cmd_mock_domain(int fd, unsigned int ioas_id, __u32 *stdev_id,100__u32 *hwpt_id, __u32 *idev_id)101{102struct iommu_test_cmd cmd = {103.size = sizeof(cmd),104.op = IOMMU_TEST_OP_MOCK_DOMAIN,105.id = ioas_id,106.mock_domain = {},107};108int ret;109110ret = ioctl(fd, IOMMU_TEST_CMD, &cmd);111if (ret)112return ret;113if (stdev_id)114*stdev_id = cmd.mock_domain.out_stdev_id;115assert(cmd.id != 0);116if (hwpt_id)117*hwpt_id = cmd.mock_domain.out_hwpt_id;118if (idev_id)119*idev_id = cmd.mock_domain.out_idev_id;120return 0;121}122#define test_cmd_mock_domain(ioas_id, stdev_id, hwpt_id, idev_id) \123ASSERT_EQ(0, _test_cmd_mock_domain(self->fd, ioas_id, stdev_id, \124hwpt_id, idev_id))125#define test_err_mock_domain(_errno, ioas_id, stdev_id, hwpt_id) \126EXPECT_ERRNO(_errno, _test_cmd_mock_domain(self->fd, ioas_id, \127stdev_id, hwpt_id, NULL))128129static int _test_cmd_mock_domain_flags(int fd, unsigned int ioas_id,130__u32 stdev_flags, __u32 *stdev_id,131__u32 *hwpt_id, __u32 *idev_id)132{133struct iommu_test_cmd cmd = {134.size = sizeof(cmd),135.op = IOMMU_TEST_OP_MOCK_DOMAIN_FLAGS,136.id = ioas_id,137.mock_domain_flags = { .dev_flags = stdev_flags },138};139int ret;140141ret = ioctl(fd, IOMMU_TEST_CMD, &cmd);142if (ret)143return ret;144if (stdev_id)145*stdev_id = cmd.mock_domain_flags.out_stdev_id;146assert(cmd.id != 0);147if (hwpt_id)148*hwpt_id = cmd.mock_domain_flags.out_hwpt_id;149if (idev_id)150*idev_id = cmd.mock_domain_flags.out_idev_id;151return 0;152}153#define test_cmd_mock_domain_flags(ioas_id, flags, stdev_id, hwpt_id, idev_id) \154ASSERT_EQ(0, _test_cmd_mock_domain_flags(self->fd, ioas_id, flags, \155stdev_id, hwpt_id, idev_id))156#define test_err_mock_domain_flags(_errno, ioas_id, flags, stdev_id, hwpt_id) \157EXPECT_ERRNO(_errno, \158_test_cmd_mock_domain_flags(self->fd, ioas_id, flags, \159stdev_id, hwpt_id, NULL))160161static int _test_cmd_mock_domain_replace(int fd, __u32 stdev_id, __u32 pt_id,162__u32 *hwpt_id)163{164struct iommu_test_cmd cmd = {165.size = sizeof(cmd),166.op = IOMMU_TEST_OP_MOCK_DOMAIN_REPLACE,167.id = stdev_id,168.mock_domain_replace = {169.pt_id = pt_id,170},171};172int ret;173174ret = ioctl(fd, IOMMU_TEST_CMD, &cmd);175if (ret)176return ret;177if (hwpt_id)178*hwpt_id = cmd.mock_domain_replace.pt_id;179return 0;180}181182#define test_cmd_mock_domain_replace(stdev_id, pt_id) \183ASSERT_EQ(0, _test_cmd_mock_domain_replace(self->fd, stdev_id, pt_id, \184NULL))185#define test_err_mock_domain_replace(_errno, stdev_id, pt_id) \186EXPECT_ERRNO(_errno, _test_cmd_mock_domain_replace(self->fd, stdev_id, \187pt_id, NULL))188189static int _test_cmd_hwpt_alloc(int fd, __u32 device_id, __u32 pt_id, __u32 ft_id,190__u32 flags, __u32 *hwpt_id, __u32 data_type,191void *data, size_t data_len)192{193struct iommu_hwpt_alloc cmd = {194.size = sizeof(cmd),195.flags = flags,196.dev_id = device_id,197.pt_id = pt_id,198.data_type = data_type,199.data_len = data_len,200.data_uptr = (uint64_t)data,201.fault_id = ft_id,202};203int ret;204205ret = ioctl(fd, IOMMU_HWPT_ALLOC, &cmd);206if (ret)207return ret;208if (hwpt_id)209*hwpt_id = cmd.out_hwpt_id;210return 0;211}212213#define test_cmd_hwpt_alloc(device_id, pt_id, flags, hwpt_id) \214ASSERT_EQ(0, _test_cmd_hwpt_alloc(self->fd, device_id, pt_id, 0, flags, \215hwpt_id, IOMMU_HWPT_DATA_NONE, NULL, \2160))217#define test_err_hwpt_alloc(_errno, device_id, pt_id, flags, hwpt_id) \218EXPECT_ERRNO(_errno, _test_cmd_hwpt_alloc( \219self->fd, device_id, pt_id, 0, flags, \220hwpt_id, IOMMU_HWPT_DATA_NONE, NULL, 0))221222#define test_cmd_hwpt_alloc_nested(device_id, pt_id, flags, hwpt_id, \223data_type, data, data_len) \224ASSERT_EQ(0, _test_cmd_hwpt_alloc(self->fd, device_id, pt_id, 0, flags, \225hwpt_id, data_type, data, data_len))226#define test_err_hwpt_alloc_nested(_errno, device_id, pt_id, flags, hwpt_id, \227data_type, data, data_len) \228EXPECT_ERRNO(_errno, \229_test_cmd_hwpt_alloc(self->fd, device_id, pt_id, 0, flags, \230hwpt_id, data_type, data, data_len))231232#define test_cmd_hwpt_alloc_iopf(device_id, pt_id, fault_id, flags, hwpt_id, \233data_type, data, data_len) \234ASSERT_EQ(0, _test_cmd_hwpt_alloc(self->fd, device_id, pt_id, fault_id, \235flags, hwpt_id, data_type, data, \236data_len))237#define test_err_hwpt_alloc_iopf(_errno, device_id, pt_id, fault_id, flags, \238hwpt_id, data_type, data, data_len) \239EXPECT_ERRNO(_errno, \240_test_cmd_hwpt_alloc(self->fd, device_id, pt_id, fault_id, \241flags, hwpt_id, data_type, data, \242data_len))243244#define test_cmd_hwpt_check_iotlb(hwpt_id, iotlb_id, expected) \245({ \246struct iommu_test_cmd test_cmd = { \247.size = sizeof(test_cmd), \248.op = IOMMU_TEST_OP_MD_CHECK_IOTLB, \249.id = hwpt_id, \250.check_iotlb = { \251.id = iotlb_id, \252.iotlb = expected, \253}, \254}; \255ASSERT_EQ(0, \256ioctl(self->fd, \257_IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_IOTLB), \258&test_cmd)); \259})260261#define test_cmd_hwpt_check_iotlb_all(hwpt_id, expected) \262({ \263int i; \264for (i = 0; i < MOCK_NESTED_DOMAIN_IOTLB_NUM; i++) \265test_cmd_hwpt_check_iotlb(hwpt_id, i, expected); \266})267268#define test_cmd_dev_check_cache(device_id, cache_id, expected) \269({ \270struct iommu_test_cmd test_cmd = { \271.size = sizeof(test_cmd), \272.op = IOMMU_TEST_OP_DEV_CHECK_CACHE, \273.id = device_id, \274.check_dev_cache = { \275.id = cache_id, \276.cache = expected, \277}, \278}; \279ASSERT_EQ(0, ioctl(self->fd, \280_IOMMU_TEST_CMD( \281IOMMU_TEST_OP_DEV_CHECK_CACHE), \282&test_cmd)); \283})284285#define test_cmd_dev_check_cache_all(device_id, expected) \286({ \287int c; \288for (c = 0; c < MOCK_DEV_CACHE_NUM; c++) \289test_cmd_dev_check_cache(device_id, c, expected); \290})291292static int _test_cmd_hwpt_invalidate(int fd, __u32 hwpt_id, void *reqs,293uint32_t data_type, uint32_t lreq,294uint32_t *nreqs)295{296struct iommu_hwpt_invalidate cmd = {297.size = sizeof(cmd),298.hwpt_id = hwpt_id,299.data_type = data_type,300.data_uptr = (uint64_t)reqs,301.entry_len = lreq,302.entry_num = *nreqs,303};304int rc = ioctl(fd, IOMMU_HWPT_INVALIDATE, &cmd);305*nreqs = cmd.entry_num;306return rc;307}308309#define test_cmd_hwpt_invalidate(hwpt_id, reqs, data_type, lreq, nreqs) \310({ \311ASSERT_EQ(0, \312_test_cmd_hwpt_invalidate(self->fd, hwpt_id, reqs, \313data_type, lreq, nreqs)); \314})315#define test_err_hwpt_invalidate(_errno, hwpt_id, reqs, data_type, lreq, \316nreqs) \317({ \318EXPECT_ERRNO(_errno, _test_cmd_hwpt_invalidate( \319self->fd, hwpt_id, reqs, \320data_type, lreq, nreqs)); \321})322323static int _test_cmd_viommu_invalidate(int fd, __u32 viommu_id, void *reqs,324uint32_t data_type, uint32_t lreq,325uint32_t *nreqs)326{327struct iommu_hwpt_invalidate cmd = {328.size = sizeof(cmd),329.hwpt_id = viommu_id,330.data_type = data_type,331.data_uptr = (uint64_t)reqs,332.entry_len = lreq,333.entry_num = *nreqs,334};335int rc = ioctl(fd, IOMMU_HWPT_INVALIDATE, &cmd);336*nreqs = cmd.entry_num;337return rc;338}339340#define test_cmd_viommu_invalidate(viommu, reqs, lreq, nreqs) \341({ \342ASSERT_EQ(0, \343_test_cmd_viommu_invalidate(self->fd, viommu, reqs, \344IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, \345lreq, nreqs)); \346})347#define test_err_viommu_invalidate(_errno, viommu_id, reqs, data_type, lreq, \348nreqs) \349({ \350EXPECT_ERRNO(_errno, _test_cmd_viommu_invalidate( \351self->fd, viommu_id, reqs, \352data_type, lreq, nreqs)); \353})354355static int _test_cmd_access_replace_ioas(int fd, __u32 access_id,356unsigned int ioas_id)357{358struct iommu_test_cmd cmd = {359.size = sizeof(cmd),360.op = IOMMU_TEST_OP_ACCESS_REPLACE_IOAS,361.id = access_id,362.access_replace_ioas = { .ioas_id = ioas_id },363};364int ret;365366ret = ioctl(fd, IOMMU_TEST_CMD, &cmd);367if (ret)368return ret;369return 0;370}371#define test_cmd_access_replace_ioas(access_id, ioas_id) \372ASSERT_EQ(0, _test_cmd_access_replace_ioas(self->fd, access_id, ioas_id))373374static int _test_cmd_set_dirty_tracking(int fd, __u32 hwpt_id, bool enabled)375{376struct iommu_hwpt_set_dirty_tracking cmd = {377.size = sizeof(cmd),378.flags = enabled ? IOMMU_HWPT_DIRTY_TRACKING_ENABLE : 0,379.hwpt_id = hwpt_id,380};381int ret;382383ret = ioctl(fd, IOMMU_HWPT_SET_DIRTY_TRACKING, &cmd);384if (ret)385return -errno;386return 0;387}388#define test_cmd_set_dirty_tracking(hwpt_id, enabled) \389ASSERT_EQ(0, _test_cmd_set_dirty_tracking(self->fd, hwpt_id, enabled))390391static int _test_cmd_get_dirty_bitmap(int fd, __u32 hwpt_id, size_t length,392__u64 iova, size_t page_size,393__u64 *bitmap, __u32 flags)394{395struct iommu_hwpt_get_dirty_bitmap cmd = {396.size = sizeof(cmd),397.hwpt_id = hwpt_id,398.flags = flags,399.iova = iova,400.length = length,401.page_size = page_size,402.data = (uintptr_t)bitmap,403};404int ret;405406ret = ioctl(fd, IOMMU_HWPT_GET_DIRTY_BITMAP, &cmd);407if (ret)408return ret;409return 0;410}411412#define test_cmd_get_dirty_bitmap(fd, hwpt_id, length, iova, page_size, \413bitmap, flags) \414ASSERT_EQ(0, _test_cmd_get_dirty_bitmap(fd, hwpt_id, length, iova, \415page_size, bitmap, flags))416417static int _test_cmd_mock_domain_set_dirty(int fd, __u32 hwpt_id, size_t length,418__u64 iova, size_t page_size,419__u64 *bitmap, __u64 *dirty)420{421struct iommu_test_cmd cmd = {422.size = sizeof(cmd),423.op = IOMMU_TEST_OP_DIRTY,424.id = hwpt_id,425.dirty = {426.iova = iova,427.length = length,428.page_size = page_size,429.uptr = (uintptr_t)bitmap,430}431};432int ret;433434ret = ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_DIRTY), &cmd);435if (ret)436return -ret;437if (dirty)438*dirty = cmd.dirty.out_nr_dirty;439return 0;440}441442#define test_cmd_mock_domain_set_dirty(fd, hwpt_id, length, iova, page_size, \443bitmap, nr) \444ASSERT_EQ(0, \445_test_cmd_mock_domain_set_dirty(fd, hwpt_id, length, iova, \446page_size, bitmap, nr))447448static int _test_mock_dirty_bitmaps(int fd, __u32 hwpt_id, size_t length,449__u64 iova, size_t page_size,450size_t pte_page_size, __u64 *bitmap,451__u64 nbits, __u32 flags,452struct __test_metadata *_metadata)453{454unsigned long npte = pte_page_size / page_size, pteset = 2 * npte;455unsigned long j, i, nr = nbits / pteset ?: 1;456unsigned long bitmap_size = DIV_ROUND_UP(nbits, BITS_PER_BYTE);457__u64 out_dirty = 0;458459/* Mark all even bits as dirty in the mock domain */460memset(bitmap, 0, bitmap_size);461for (i = 0; i < nbits; i += pteset)462set_bit(i, (unsigned long *)bitmap);463464test_cmd_mock_domain_set_dirty(fd, hwpt_id, length, iova, page_size,465bitmap, &out_dirty);466ASSERT_EQ(nr, out_dirty);467468/* Expect all even bits as dirty in the user bitmap */469memset(bitmap, 0, bitmap_size);470test_cmd_get_dirty_bitmap(fd, hwpt_id, length, iova, page_size, bitmap,471flags);472/* Beware ASSERT_EQ() is two statements -- braces are not redundant! */473for (i = 0; i < nbits; i += pteset) {474for (j = 0; j < pteset; j++) {475ASSERT_EQ(j < npte,476test_bit(i + j, (unsigned long *)bitmap));477}478ASSERT_EQ(!(i % pteset), test_bit(i, (unsigned long *)bitmap));479}480481memset(bitmap, 0, bitmap_size);482test_cmd_get_dirty_bitmap(fd, hwpt_id, length, iova, page_size, bitmap,483flags);484485/* It as read already -- expect all zeroes */486for (i = 0; i < nbits; i += pteset) {487for (j = 0; j < pteset; j++) {488ASSERT_EQ(489(j < npte) &&490(flags &491IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR),492test_bit(i + j, (unsigned long *)bitmap));493}494}495496return 0;497}498#define test_mock_dirty_bitmaps(hwpt_id, length, iova, page_size, pte_size,\499bitmap, bitmap_size, flags, _metadata) \500ASSERT_EQ(0, _test_mock_dirty_bitmaps(self->fd, hwpt_id, length, iova, \501page_size, pte_size, bitmap, \502bitmap_size, flags, _metadata))503504static int _test_cmd_create_access(int fd, unsigned int ioas_id,505__u32 *access_id, unsigned int flags)506{507struct iommu_test_cmd cmd = {508.size = sizeof(cmd),509.op = IOMMU_TEST_OP_CREATE_ACCESS,510.id = ioas_id,511.create_access = { .flags = flags },512};513int ret;514515ret = ioctl(fd, IOMMU_TEST_CMD, &cmd);516if (ret)517return ret;518*access_id = cmd.create_access.out_access_fd;519return 0;520}521#define test_cmd_create_access(ioas_id, access_id, flags) \522ASSERT_EQ(0, _test_cmd_create_access(self->fd, ioas_id, access_id, \523flags))524525static int _test_cmd_destroy_access(unsigned int access_id)526{527return close(access_id);528}529#define test_cmd_destroy_access(access_id) \530ASSERT_EQ(0, _test_cmd_destroy_access(access_id))531532static int _test_cmd_destroy_access_pages(int fd, unsigned int access_id,533unsigned int access_pages_id)534{535struct iommu_test_cmd cmd = {536.size = sizeof(cmd),537.op = IOMMU_TEST_OP_DESTROY_ACCESS_PAGES,538.id = access_id,539.destroy_access_pages = { .access_pages_id = access_pages_id },540};541return ioctl(fd, IOMMU_TEST_CMD, &cmd);542}543#define test_cmd_destroy_access_pages(access_id, access_pages_id) \544ASSERT_EQ(0, _test_cmd_destroy_access_pages(self->fd, access_id, \545access_pages_id))546#define test_err_destroy_access_pages(_errno, access_id, access_pages_id) \547EXPECT_ERRNO(_errno, _test_cmd_destroy_access_pages( \548self->fd, access_id, access_pages_id))549550static int _test_ioctl_destroy(int fd, unsigned int id)551{552struct iommu_destroy cmd = {553.size = sizeof(cmd),554.id = id,555};556return ioctl(fd, IOMMU_DESTROY, &cmd);557}558#define test_ioctl_destroy(id) ASSERT_EQ(0, _test_ioctl_destroy(self->fd, id))559560static int _test_ioctl_ioas_alloc(int fd, __u32 *id)561{562struct iommu_ioas_alloc cmd = {563.size = sizeof(cmd),564};565int ret;566567ret = ioctl(fd, IOMMU_IOAS_ALLOC, &cmd);568if (ret)569return ret;570*id = cmd.out_ioas_id;571return 0;572}573#define test_ioctl_ioas_alloc(id) \574({ \575ASSERT_EQ(0, _test_ioctl_ioas_alloc(self->fd, id)); \576ASSERT_NE(0, *(id)); \577})578579static int _test_ioctl_ioas_map(int fd, unsigned int ioas_id, void *buffer,580size_t length, __u64 *iova, unsigned int flags)581{582struct iommu_ioas_map cmd = {583.size = sizeof(cmd),584.flags = flags,585.ioas_id = ioas_id,586.user_va = (uintptr_t)buffer,587.length = length,588};589int ret;590591if (flags & IOMMU_IOAS_MAP_FIXED_IOVA)592cmd.iova = *iova;593594ret = ioctl(fd, IOMMU_IOAS_MAP, &cmd);595*iova = cmd.iova;596return ret;597}598#define test_ioctl_ioas_map(buffer, length, iova_p) \599ASSERT_EQ(0, _test_ioctl_ioas_map(self->fd, self->ioas_id, buffer, \600length, iova_p, \601IOMMU_IOAS_MAP_WRITEABLE | \602IOMMU_IOAS_MAP_READABLE))603604#define test_err_ioctl_ioas_map(_errno, buffer, length, iova_p) \605EXPECT_ERRNO(_errno, \606_test_ioctl_ioas_map(self->fd, self->ioas_id, buffer, \607length, iova_p, \608IOMMU_IOAS_MAP_WRITEABLE | \609IOMMU_IOAS_MAP_READABLE))610611#define test_ioctl_ioas_map_id(ioas_id, buffer, length, iova_p) \612ASSERT_EQ(0, _test_ioctl_ioas_map(self->fd, ioas_id, buffer, length, \613iova_p, \614IOMMU_IOAS_MAP_WRITEABLE | \615IOMMU_IOAS_MAP_READABLE))616617#define test_ioctl_ioas_map_fixed(buffer, length, iova) \618({ \619__u64 __iova = iova; \620ASSERT_EQ(0, _test_ioctl_ioas_map( \621self->fd, self->ioas_id, buffer, length, \622&__iova, \623IOMMU_IOAS_MAP_FIXED_IOVA | \624IOMMU_IOAS_MAP_WRITEABLE | \625IOMMU_IOAS_MAP_READABLE)); \626})627628#define test_ioctl_ioas_map_fixed_id(ioas_id, buffer, length, iova) \629({ \630__u64 __iova = iova; \631ASSERT_EQ(0, \632_test_ioctl_ioas_map( \633self->fd, ioas_id, buffer, length, &__iova, \634IOMMU_IOAS_MAP_FIXED_IOVA | \635IOMMU_IOAS_MAP_WRITEABLE | \636IOMMU_IOAS_MAP_READABLE)); \637})638639#define test_err_ioctl_ioas_map_fixed(_errno, buffer, length, iova) \640({ \641__u64 __iova = iova; \642EXPECT_ERRNO(_errno, \643_test_ioctl_ioas_map( \644self->fd, self->ioas_id, buffer, length, \645&__iova, \646IOMMU_IOAS_MAP_FIXED_IOVA | \647IOMMU_IOAS_MAP_WRITEABLE | \648IOMMU_IOAS_MAP_READABLE)); \649})650651static int _test_ioctl_ioas_unmap(int fd, unsigned int ioas_id, uint64_t iova,652size_t length, uint64_t *out_len)653{654struct iommu_ioas_unmap cmd = {655.size = sizeof(cmd),656.ioas_id = ioas_id,657.iova = iova,658.length = length,659};660int ret;661662ret = ioctl(fd, IOMMU_IOAS_UNMAP, &cmd);663if (out_len)664*out_len = cmd.length;665return ret;666}667#define test_ioctl_ioas_unmap(iova, length) \668ASSERT_EQ(0, _test_ioctl_ioas_unmap(self->fd, self->ioas_id, iova, \669length, NULL))670671#define test_ioctl_ioas_unmap_id(ioas_id, iova, length) \672ASSERT_EQ(0, _test_ioctl_ioas_unmap(self->fd, ioas_id, iova, length, \673NULL))674675#define test_err_ioctl_ioas_unmap(_errno, iova, length) \676EXPECT_ERRNO(_errno, _test_ioctl_ioas_unmap(self->fd, self->ioas_id, \677iova, length, NULL))678679static int _test_ioctl_ioas_map_file(int fd, unsigned int ioas_id, int mfd,680size_t start, size_t length, __u64 *iova,681unsigned int flags)682{683struct iommu_ioas_map_file cmd = {684.size = sizeof(cmd),685.flags = flags,686.ioas_id = ioas_id,687.fd = mfd,688.start = start,689.length = length,690};691int ret;692693if (flags & IOMMU_IOAS_MAP_FIXED_IOVA)694cmd.iova = *iova;695696ret = ioctl(fd, IOMMU_IOAS_MAP_FILE, &cmd);697*iova = cmd.iova;698return ret;699}700701#define test_ioctl_ioas_map_file(mfd, start, length, iova_p) \702ASSERT_EQ(0, \703_test_ioctl_ioas_map_file( \704self->fd, self->ioas_id, mfd, start, length, iova_p, \705IOMMU_IOAS_MAP_WRITEABLE | IOMMU_IOAS_MAP_READABLE))706707#define test_err_ioctl_ioas_map_file(_errno, mfd, start, length, iova_p) \708EXPECT_ERRNO( \709_errno, \710_test_ioctl_ioas_map_file( \711self->fd, self->ioas_id, mfd, start, length, iova_p, \712IOMMU_IOAS_MAP_WRITEABLE | IOMMU_IOAS_MAP_READABLE))713714#define test_ioctl_ioas_map_id_file(ioas_id, mfd, start, length, iova_p) \715ASSERT_EQ(0, \716_test_ioctl_ioas_map_file( \717self->fd, ioas_id, mfd, start, length, iova_p, \718IOMMU_IOAS_MAP_WRITEABLE | IOMMU_IOAS_MAP_READABLE))719720static int _test_ioctl_set_temp_memory_limit(int fd, unsigned int limit)721{722struct iommu_test_cmd memlimit_cmd = {723.size = sizeof(memlimit_cmd),724.op = IOMMU_TEST_OP_SET_TEMP_MEMORY_LIMIT,725.memory_limit = { .limit = limit },726};727728return ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_SET_TEMP_MEMORY_LIMIT),729&memlimit_cmd);730}731732#define test_ioctl_set_temp_memory_limit(limit) \733ASSERT_EQ(0, _test_ioctl_set_temp_memory_limit(self->fd, limit))734735#define test_ioctl_set_default_memory_limit() \736test_ioctl_set_temp_memory_limit(65536)737738static void teardown_iommufd(int fd, struct __test_metadata *_metadata)739{740struct iommu_test_cmd test_cmd = {741.size = sizeof(test_cmd),742.op = IOMMU_TEST_OP_MD_CHECK_REFS,743.check_refs = { .length = BUFFER_SIZE,744.uptr = (uintptr_t)buffer },745};746747if (fd == -1)748return;749750EXPECT_EQ(0, close(fd));751752fd = open("/dev/iommu", O_RDWR);753EXPECT_NE(-1, fd);754EXPECT_EQ(0, ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_REFS),755&test_cmd));756EXPECT_EQ(0, close(fd));757}758759#define EXPECT_ERRNO(expected_errno, cmd) \760({ \761ASSERT_EQ(-1, cmd); \762EXPECT_EQ(expected_errno, errno); \763})764765#endif766767/* @data can be NULL */768static int _test_cmd_get_hw_info(int fd, __u32 device_id, __u32 data_type,769void *data, size_t data_len,770uint32_t *capabilities, uint8_t *max_pasid)771{772struct iommu_test_hw_info *info = (struct iommu_test_hw_info *)data;773struct iommu_hw_info cmd = {774.size = sizeof(cmd),775.dev_id = device_id,776.data_len = data_len,777.in_data_type = data_type,778.data_uptr = (uint64_t)data,779.out_capabilities = 0,780};781int ret;782783if (data_type != IOMMU_HW_INFO_TYPE_DEFAULT)784cmd.flags |= IOMMU_HW_INFO_FLAG_INPUT_TYPE;785786ret = ioctl(fd, IOMMU_GET_HW_INFO, &cmd);787if (ret)788return ret;789790assert(cmd.out_data_type == IOMMU_HW_INFO_TYPE_SELFTEST);791792/*793* The struct iommu_test_hw_info should be the one defined794* by the current kernel.795*/796assert(cmd.data_len == sizeof(struct iommu_test_hw_info));797798/*799* Trailing bytes should be 0 if user buffer is larger than800* the data that kernel reports.801*/802if (data_len > cmd.data_len) {803char *ptr = (char *)(data + cmd.data_len);804int idx = 0;805806while (idx < data_len - cmd.data_len) {807assert(!*(ptr + idx));808idx++;809}810}811812if (info) {813if (data_len >= offsetofend(struct iommu_test_hw_info, test_reg))814assert(info->test_reg == IOMMU_HW_INFO_SELFTEST_REGVAL);815if (data_len >= offsetofend(struct iommu_test_hw_info, flags))816assert(!info->flags);817}818819if (max_pasid)820*max_pasid = cmd.out_max_pasid_log2;821822if (capabilities)823*capabilities = cmd.out_capabilities;824825return 0;826}827828#define test_cmd_get_hw_info(device_id, data_type, data, data_len) \829ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, data_type, \830data, data_len, NULL, NULL))831832#define test_err_get_hw_info(_errno, device_id, data_type, data, data_len) \833EXPECT_ERRNO(_errno, \834_test_cmd_get_hw_info(self->fd, device_id, data_type, \835data, data_len, NULL, NULL))836837#define test_cmd_get_hw_capabilities(device_id, caps) \838ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, \839IOMMU_HW_INFO_TYPE_DEFAULT, NULL, \8400, &caps, NULL))841842#define test_cmd_get_hw_info_pasid(device_id, max_pasid) \843ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, \844IOMMU_HW_INFO_TYPE_DEFAULT, NULL, \8450, NULL, max_pasid))846847static int _test_ioctl_fault_alloc(int fd, __u32 *fault_id, __u32 *fault_fd)848{849struct iommu_fault_alloc cmd = {850.size = sizeof(cmd),851};852int ret;853854ret = ioctl(fd, IOMMU_FAULT_QUEUE_ALLOC, &cmd);855if (ret)856return ret;857*fault_id = cmd.out_fault_id;858*fault_fd = cmd.out_fault_fd;859return 0;860}861862#define test_ioctl_fault_alloc(fault_id, fault_fd) \863({ \864ASSERT_EQ(0, _test_ioctl_fault_alloc(self->fd, fault_id, \865fault_fd)); \866ASSERT_NE(0, *(fault_id)); \867ASSERT_NE(0, *(fault_fd)); \868})869870static int _test_cmd_trigger_iopf(int fd, __u32 device_id, __u32 pasid,871__u32 fault_fd)872{873struct iommu_test_cmd trigger_iopf_cmd = {874.size = sizeof(trigger_iopf_cmd),875.op = IOMMU_TEST_OP_TRIGGER_IOPF,876.trigger_iopf = {877.dev_id = device_id,878.pasid = pasid,879.grpid = 0x2,880.perm = IOMMU_PGFAULT_PERM_READ | IOMMU_PGFAULT_PERM_WRITE,881.addr = 0xdeadbeaf,882},883};884struct iommu_hwpt_page_response response = {885.code = IOMMUFD_PAGE_RESP_SUCCESS,886};887struct iommu_hwpt_pgfault fault = {};888ssize_t bytes;889int ret;890891ret = ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_TRIGGER_IOPF), &trigger_iopf_cmd);892if (ret)893return ret;894895bytes = read(fault_fd, &fault, sizeof(fault));896if (bytes <= 0)897return -EIO;898899response.cookie = fault.cookie;900901bytes = write(fault_fd, &response, sizeof(response));902if (bytes <= 0)903return -EIO;904905return 0;906}907908#define test_cmd_trigger_iopf(device_id, fault_fd) \909ASSERT_EQ(0, _test_cmd_trigger_iopf(self->fd, device_id, 0x1, fault_fd))910#define test_cmd_trigger_iopf_pasid(device_id, pasid, fault_fd) \911ASSERT_EQ(0, _test_cmd_trigger_iopf(self->fd, device_id, \912pasid, fault_fd))913914static int _test_cmd_viommu_alloc(int fd, __u32 device_id, __u32 hwpt_id,915__u32 flags, __u32 type, void *data,916__u32 data_len, __u32 *viommu_id)917{918struct iommu_viommu_alloc cmd = {919.size = sizeof(cmd),920.flags = flags,921.type = type,922.dev_id = device_id,923.hwpt_id = hwpt_id,924.data_uptr = (uint64_t)data,925.data_len = data_len,926};927int ret;928929ret = ioctl(fd, IOMMU_VIOMMU_ALLOC, &cmd);930if (ret)931return ret;932if (viommu_id)933*viommu_id = cmd.out_viommu_id;934return 0;935}936937#define test_cmd_viommu_alloc(device_id, hwpt_id, type, data, data_len, \938viommu_id) \939ASSERT_EQ(0, _test_cmd_viommu_alloc(self->fd, device_id, hwpt_id, 0, \940type, data, data_len, viommu_id))941#define test_err_viommu_alloc(_errno, device_id, hwpt_id, type, data, \942data_len, viommu_id) \943EXPECT_ERRNO(_errno, \944_test_cmd_viommu_alloc(self->fd, device_id, hwpt_id, 0, \945type, data, data_len, viommu_id))946947static int _test_cmd_vdevice_alloc(int fd, __u32 viommu_id, __u32 idev_id,948__u64 virt_id, __u32 *vdev_id)949{950struct iommu_vdevice_alloc cmd = {951.size = sizeof(cmd),952.dev_id = idev_id,953.viommu_id = viommu_id,954.virt_id = virt_id,955};956int ret;957958ret = ioctl(fd, IOMMU_VDEVICE_ALLOC, &cmd);959if (ret)960return ret;961if (vdev_id)962*vdev_id = cmd.out_vdevice_id;963return 0;964}965966#define test_cmd_vdevice_alloc(viommu_id, idev_id, virt_id, vdev_id) \967ASSERT_EQ(0, _test_cmd_vdevice_alloc(self->fd, viommu_id, idev_id, \968virt_id, vdev_id))969#define test_err_vdevice_alloc(_errno, viommu_id, idev_id, virt_id, vdev_id) \970EXPECT_ERRNO(_errno, \971_test_cmd_vdevice_alloc(self->fd, viommu_id, idev_id, \972virt_id, vdev_id))973974static int _test_cmd_hw_queue_alloc(int fd, __u32 viommu_id, __u32 type,975__u32 idx, __u64 base_addr, __u64 length,976__u32 *hw_queue_id)977{978struct iommu_hw_queue_alloc cmd = {979.size = sizeof(cmd),980.viommu_id = viommu_id,981.type = type,982.index = idx,983.nesting_parent_iova = base_addr,984.length = length,985};986int ret;987988ret = ioctl(fd, IOMMU_HW_QUEUE_ALLOC, &cmd);989if (ret)990return ret;991if (hw_queue_id)992*hw_queue_id = cmd.out_hw_queue_id;993return 0;994}995996#define test_cmd_hw_queue_alloc(viommu_id, type, idx, base_addr, len, out_qid) \997ASSERT_EQ(0, _test_cmd_hw_queue_alloc(self->fd, viommu_id, type, idx, \998base_addr, len, out_qid))999#define test_err_hw_queue_alloc(_errno, viommu_id, type, idx, base_addr, len, \1000out_qid) \1001EXPECT_ERRNO(_errno, \1002_test_cmd_hw_queue_alloc(self->fd, viommu_id, type, idx, \1003base_addr, len, out_qid))10041005static int _test_cmd_veventq_alloc(int fd, __u32 viommu_id, __u32 type,1006__u32 *veventq_id, __u32 *veventq_fd)1007{1008struct iommu_veventq_alloc cmd = {1009.size = sizeof(cmd),1010.type = type,1011.veventq_depth = 2,1012.viommu_id = viommu_id,1013};1014int ret;10151016ret = ioctl(fd, IOMMU_VEVENTQ_ALLOC, &cmd);1017if (ret)1018return ret;1019if (veventq_id)1020*veventq_id = cmd.out_veventq_id;1021if (veventq_fd)1022*veventq_fd = cmd.out_veventq_fd;1023return 0;1024}10251026#define test_cmd_veventq_alloc(viommu_id, type, veventq_id, veventq_fd) \1027ASSERT_EQ(0, _test_cmd_veventq_alloc(self->fd, viommu_id, type, \1028veventq_id, veventq_fd))1029#define test_err_veventq_alloc(_errno, viommu_id, type, veventq_id, \1030veventq_fd) \1031EXPECT_ERRNO(_errno, \1032_test_cmd_veventq_alloc(self->fd, viommu_id, type, \1033veventq_id, veventq_fd))10341035static int _test_cmd_trigger_vevents(int fd, __u32 dev_id, __u32 nvevents)1036{1037struct iommu_test_cmd trigger_vevent_cmd = {1038.size = sizeof(trigger_vevent_cmd),1039.op = IOMMU_TEST_OP_TRIGGER_VEVENT,1040.trigger_vevent = {1041.dev_id = dev_id,1042},1043};1044int ret;10451046while (nvevents--) {1047ret = ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_TRIGGER_VEVENT),1048&trigger_vevent_cmd);1049if (ret < 0)1050return -1;1051}1052return ret;1053}10541055#define test_cmd_trigger_vevents(dev_id, nvevents) \1056ASSERT_EQ(0, _test_cmd_trigger_vevents(self->fd, dev_id, nvevents))10571058static int _test_cmd_read_vevents(int fd, __u32 event_fd, __u32 nvevents,1059__u32 virt_id, int *prev_seq)1060{1061struct pollfd pollfd = { .fd = event_fd, .events = POLLIN };1062struct iommu_viommu_event_selftest *event;1063struct iommufd_vevent_header *hdr;1064ssize_t bytes;1065void *data;1066int ret, i;10671068ret = poll(&pollfd, 1, 1000);1069if (ret < 0)1070return -1;10711072data = calloc(nvevents, sizeof(*hdr) + sizeof(*event));1073if (!data) {1074errno = ENOMEM;1075return -1;1076}10771078bytes = read(event_fd, data,1079nvevents * (sizeof(*hdr) + sizeof(*event)));1080if (bytes <= 0) {1081errno = EFAULT;1082ret = -1;1083goto out_free;1084}10851086for (i = 0; i < nvevents; i++) {1087hdr = data + i * (sizeof(*hdr) + sizeof(*event));10881089if (hdr->flags & IOMMU_VEVENTQ_FLAG_LOST_EVENTS ||1090hdr->sequence - *prev_seq > 1) {1091*prev_seq = hdr->sequence;1092errno = EOVERFLOW;1093ret = -1;1094goto out_free;1095}1096*prev_seq = hdr->sequence;1097event = data + sizeof(*hdr);1098if (event->virt_id != virt_id) {1099errno = EINVAL;1100ret = -1;1101goto out_free;1102}1103}11041105ret = 0;1106out_free:1107free(data);1108return ret;1109}11101111#define test_cmd_read_vevents(event_fd, nvevents, virt_id, prev_seq) \1112ASSERT_EQ(0, _test_cmd_read_vevents(self->fd, event_fd, nvevents, \1113virt_id, prev_seq))1114#define test_err_read_vevents(_errno, event_fd, nvevents, virt_id, prev_seq) \1115EXPECT_ERRNO(_errno, \1116_test_cmd_read_vevents(self->fd, event_fd, nvevents, \1117virt_id, prev_seq))11181119static int _test_cmd_pasid_attach(int fd, __u32 stdev_id, __u32 pasid,1120__u32 pt_id)1121{1122struct iommu_test_cmd test_attach = {1123.size = sizeof(test_attach),1124.op = IOMMU_TEST_OP_PASID_ATTACH,1125.id = stdev_id,1126.pasid_attach = {1127.pasid = pasid,1128.pt_id = pt_id,1129},1130};11311132return ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_PASID_ATTACH),1133&test_attach);1134}11351136#define test_cmd_pasid_attach(pasid, hwpt_id) \1137ASSERT_EQ(0, _test_cmd_pasid_attach(self->fd, self->stdev_id, \1138pasid, hwpt_id))11391140#define test_err_pasid_attach(_errno, pasid, hwpt_id) \1141EXPECT_ERRNO(_errno, \1142_test_cmd_pasid_attach(self->fd, self->stdev_id, \1143pasid, hwpt_id))11441145static int _test_cmd_pasid_replace(int fd, __u32 stdev_id, __u32 pasid,1146__u32 pt_id)1147{1148struct iommu_test_cmd test_replace = {1149.size = sizeof(test_replace),1150.op = IOMMU_TEST_OP_PASID_REPLACE,1151.id = stdev_id,1152.pasid_replace = {1153.pasid = pasid,1154.pt_id = pt_id,1155},1156};11571158return ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_PASID_REPLACE),1159&test_replace);1160}11611162#define test_cmd_pasid_replace(pasid, hwpt_id) \1163ASSERT_EQ(0, _test_cmd_pasid_replace(self->fd, self->stdev_id, \1164pasid, hwpt_id))11651166#define test_err_pasid_replace(_errno, pasid, hwpt_id) \1167EXPECT_ERRNO(_errno, \1168_test_cmd_pasid_replace(self->fd, self->stdev_id, \1169pasid, hwpt_id))11701171static int _test_cmd_pasid_detach(int fd, __u32 stdev_id, __u32 pasid)1172{1173struct iommu_test_cmd test_detach = {1174.size = sizeof(test_detach),1175.op = IOMMU_TEST_OP_PASID_DETACH,1176.id = stdev_id,1177.pasid_detach = {1178.pasid = pasid,1179},1180};11811182return ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_PASID_DETACH),1183&test_detach);1184}11851186#define test_cmd_pasid_detach(pasid) \1187ASSERT_EQ(0, _test_cmd_pasid_detach(self->fd, self->stdev_id, pasid))11881189static int test_cmd_pasid_check_hwpt(int fd, __u32 stdev_id, __u32 pasid,1190__u32 hwpt_id)1191{1192struct iommu_test_cmd test_pasid_check = {1193.size = sizeof(test_pasid_check),1194.op = IOMMU_TEST_OP_PASID_CHECK_HWPT,1195.id = stdev_id,1196.pasid_check = {1197.pasid = pasid,1198.hwpt_id = hwpt_id,1199},1200};12011202return ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_PASID_CHECK_HWPT),1203&test_pasid_check);1204}120512061207