Path: blob/master/tools/testing/selftests/iommu/iommufd.c
26302 views
// SPDX-License-Identifier: GPL-2.0-only1/* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES */2#include <asm/unistd.h>3#include <stdlib.h>4#include <sys/capability.h>5#include <sys/mman.h>6#include <sys/eventfd.h>78#define __EXPORTED_HEADERS__9#include <linux/vfio.h>1011#include "iommufd_utils.h"1213static unsigned long HUGEPAGE_SIZE;1415#define MOCK_PAGE_SIZE (PAGE_SIZE / 2)16#define MOCK_HUGE_PAGE_SIZE (512 * MOCK_PAGE_SIZE)1718static unsigned long get_huge_page_size(void)19{20char buf[80];21int ret;22int fd;2324fd = open("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size",25O_RDONLY);26if (fd < 0)27return 2 * 1024 * 1024;2829ret = read(fd, buf, sizeof(buf));30close(fd);31if (ret <= 0 || ret == sizeof(buf))32return 2 * 1024 * 1024;33buf[ret] = 0;34return strtoul(buf, NULL, 10);35}3637static __attribute__((constructor)) void setup_sizes(void)38{39void *vrc;40int rc;4142PAGE_SIZE = sysconf(_SC_PAGE_SIZE);43HUGEPAGE_SIZE = get_huge_page_size();4445BUFFER_SIZE = PAGE_SIZE * 16;46rc = posix_memalign(&buffer, HUGEPAGE_SIZE, BUFFER_SIZE);47assert(!rc);48assert(buffer);49assert((uintptr_t)buffer % HUGEPAGE_SIZE == 0);50vrc = mmap(buffer, BUFFER_SIZE, PROT_READ | PROT_WRITE,51MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0);52assert(vrc == buffer);5354mfd_buffer = memfd_mmap(BUFFER_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,55&mfd);56assert(mfd_buffer != MAP_FAILED);57assert(mfd > 0);58}5960FIXTURE(iommufd)61{62int fd;63};6465FIXTURE_SETUP(iommufd)66{67self->fd = open("/dev/iommu", O_RDWR);68ASSERT_NE(-1, self->fd);69}7071FIXTURE_TEARDOWN(iommufd)72{73teardown_iommufd(self->fd, _metadata);74}7576TEST_F(iommufd, simple_close)77{78}7980TEST_F(iommufd, cmd_fail)81{82struct iommu_destroy cmd = { .size = sizeof(cmd), .id = 0 };8384/* object id is invalid */85EXPECT_ERRNO(ENOENT, _test_ioctl_destroy(self->fd, 0));86/* Bad pointer */87EXPECT_ERRNO(EFAULT, ioctl(self->fd, IOMMU_DESTROY, NULL));88/* Unknown ioctl */89EXPECT_ERRNO(ENOTTY,90ioctl(self->fd, _IO(IOMMUFD_TYPE, IOMMUFD_CMD_BASE - 1),91&cmd));92}9394TEST_F(iommufd, cmd_length)95{96#define TEST_LENGTH(_struct, _ioctl, _last) \97{ \98size_t min_size = offsetofend(struct _struct, _last); \99struct { \100struct _struct cmd; \101uint8_t extra; \102} cmd = { .cmd = { .size = min_size - 1 }, \103.extra = UINT8_MAX }; \104int old_errno; \105int rc; \106\107EXPECT_ERRNO(EINVAL, ioctl(self->fd, _ioctl, &cmd)); \108cmd.cmd.size = sizeof(struct _struct) + 1; \109EXPECT_ERRNO(E2BIG, ioctl(self->fd, _ioctl, &cmd)); \110cmd.cmd.size = sizeof(struct _struct); \111rc = ioctl(self->fd, _ioctl, &cmd); \112old_errno = errno; \113cmd.cmd.size = sizeof(struct _struct) + 1; \114cmd.extra = 0; \115if (rc) { \116EXPECT_ERRNO(old_errno, \117ioctl(self->fd, _ioctl, &cmd)); \118} else { \119ASSERT_EQ(0, ioctl(self->fd, _ioctl, &cmd)); \120} \121}122123TEST_LENGTH(iommu_destroy, IOMMU_DESTROY, id);124TEST_LENGTH(iommu_hw_info, IOMMU_GET_HW_INFO, __reserved);125TEST_LENGTH(iommu_hwpt_alloc, IOMMU_HWPT_ALLOC, __reserved);126TEST_LENGTH(iommu_hwpt_invalidate, IOMMU_HWPT_INVALIDATE, __reserved);127TEST_LENGTH(iommu_ioas_alloc, IOMMU_IOAS_ALLOC, out_ioas_id);128TEST_LENGTH(iommu_ioas_iova_ranges, IOMMU_IOAS_IOVA_RANGES,129out_iova_alignment);130TEST_LENGTH(iommu_ioas_allow_iovas, IOMMU_IOAS_ALLOW_IOVAS,131allowed_iovas);132TEST_LENGTH(iommu_ioas_map, IOMMU_IOAS_MAP, iova);133TEST_LENGTH(iommu_ioas_copy, IOMMU_IOAS_COPY, src_iova);134TEST_LENGTH(iommu_ioas_unmap, IOMMU_IOAS_UNMAP, length);135TEST_LENGTH(iommu_option, IOMMU_OPTION, val64);136TEST_LENGTH(iommu_vfio_ioas, IOMMU_VFIO_IOAS, __reserved);137TEST_LENGTH(iommu_ioas_map_file, IOMMU_IOAS_MAP_FILE, iova);138TEST_LENGTH(iommu_viommu_alloc, IOMMU_VIOMMU_ALLOC, out_viommu_id);139TEST_LENGTH(iommu_vdevice_alloc, IOMMU_VDEVICE_ALLOC, virt_id);140TEST_LENGTH(iommu_ioas_change_process, IOMMU_IOAS_CHANGE_PROCESS,141__reserved);142#undef TEST_LENGTH143}144145TEST_F(iommufd, cmd_ex_fail)146{147struct {148struct iommu_destroy cmd;149__u64 future;150} cmd = { .cmd = { .size = sizeof(cmd), .id = 0 } };151152/* object id is invalid and command is longer */153EXPECT_ERRNO(ENOENT, ioctl(self->fd, IOMMU_DESTROY, &cmd));154/* future area is non-zero */155cmd.future = 1;156EXPECT_ERRNO(E2BIG, ioctl(self->fd, IOMMU_DESTROY, &cmd));157/* Original command "works" */158cmd.cmd.size = sizeof(cmd.cmd);159EXPECT_ERRNO(ENOENT, ioctl(self->fd, IOMMU_DESTROY, &cmd));160/* Short command fails */161cmd.cmd.size = sizeof(cmd.cmd) - 1;162EXPECT_ERRNO(EINVAL, ioctl(self->fd, IOMMU_DESTROY, &cmd));163}164165TEST_F(iommufd, global_options)166{167struct iommu_option cmd = {168.size = sizeof(cmd),169.option_id = IOMMU_OPTION_RLIMIT_MODE,170.op = IOMMU_OPTION_OP_GET,171.val64 = 1,172};173174cmd.option_id = IOMMU_OPTION_RLIMIT_MODE;175ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));176ASSERT_EQ(0, cmd.val64);177178/* This requires root */179cmd.op = IOMMU_OPTION_OP_SET;180cmd.val64 = 1;181ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));182cmd.val64 = 2;183EXPECT_ERRNO(EINVAL, ioctl(self->fd, IOMMU_OPTION, &cmd));184185cmd.op = IOMMU_OPTION_OP_GET;186ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));187ASSERT_EQ(1, cmd.val64);188189cmd.op = IOMMU_OPTION_OP_SET;190cmd.val64 = 0;191ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));192193cmd.op = IOMMU_OPTION_OP_GET;194cmd.option_id = IOMMU_OPTION_HUGE_PAGES;195EXPECT_ERRNO(ENOENT, ioctl(self->fd, IOMMU_OPTION, &cmd));196cmd.op = IOMMU_OPTION_OP_SET;197EXPECT_ERRNO(ENOENT, ioctl(self->fd, IOMMU_OPTION, &cmd));198}199200static void drop_cap_ipc_lock(struct __test_metadata *_metadata)201{202cap_t caps;203cap_value_t cap_list[1] = { CAP_IPC_LOCK };204205caps = cap_get_proc();206ASSERT_NE(caps, NULL);207ASSERT_NE(-1,208cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_CLEAR));209ASSERT_NE(-1, cap_set_proc(caps));210cap_free(caps);211}212213static long get_proc_status_value(pid_t pid, const char *var)214{215FILE *fp;216char buf[80], tag[80];217long val = -1;218219snprintf(buf, sizeof(buf), "/proc/%d/status", pid);220fp = fopen(buf, "r");221if (!fp)222return val;223224while (fgets(buf, sizeof(buf), fp))225if (fscanf(fp, "%s %ld\n", tag, &val) == 2 && !strcmp(tag, var))226break;227228fclose(fp);229return val;230}231232static long get_vm_pinned(pid_t pid)233{234return get_proc_status_value(pid, "VmPin:");235}236237static long get_vm_locked(pid_t pid)238{239return get_proc_status_value(pid, "VmLck:");240}241242FIXTURE(change_process)243{244int fd;245uint32_t ioas_id;246};247248FIXTURE_VARIANT(change_process)249{250int accounting;251};252253FIXTURE_SETUP(change_process)254{255self->fd = open("/dev/iommu", O_RDWR);256ASSERT_NE(-1, self->fd);257258drop_cap_ipc_lock(_metadata);259if (variant->accounting != IOPT_PAGES_ACCOUNT_NONE) {260struct iommu_option set_limit_cmd = {261.size = sizeof(set_limit_cmd),262.option_id = IOMMU_OPTION_RLIMIT_MODE,263.op = IOMMU_OPTION_OP_SET,264.val64 = (variant->accounting == IOPT_PAGES_ACCOUNT_MM),265};266ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &set_limit_cmd));267}268269test_ioctl_ioas_alloc(&self->ioas_id);270test_cmd_mock_domain(self->ioas_id, NULL, NULL, NULL);271}272273FIXTURE_TEARDOWN(change_process)274{275teardown_iommufd(self->fd, _metadata);276}277278FIXTURE_VARIANT_ADD(change_process, account_none)279{280.accounting = IOPT_PAGES_ACCOUNT_NONE,281};282283FIXTURE_VARIANT_ADD(change_process, account_user)284{285.accounting = IOPT_PAGES_ACCOUNT_USER,286};287288FIXTURE_VARIANT_ADD(change_process, account_mm)289{290.accounting = IOPT_PAGES_ACCOUNT_MM,291};292293TEST_F(change_process, basic)294{295pid_t parent = getpid();296pid_t child;297__u64 iova;298struct iommu_ioas_change_process cmd = {299.size = sizeof(cmd),300};301302/* Expect failure if non-file maps exist */303test_ioctl_ioas_map(buffer, PAGE_SIZE, &iova);304EXPECT_ERRNO(EINVAL, ioctl(self->fd, IOMMU_IOAS_CHANGE_PROCESS, &cmd));305test_ioctl_ioas_unmap(iova, PAGE_SIZE);306307/* Change process works in current process. */308test_ioctl_ioas_map_file(mfd, 0, PAGE_SIZE, &iova);309ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_CHANGE_PROCESS, &cmd));310311/* Change process works in another process */312child = fork();313if (!child) {314int nlock = PAGE_SIZE / 1024;315316/* Parent accounts for locked memory before */317ASSERT_EQ(nlock, get_vm_pinned(parent));318if (variant->accounting == IOPT_PAGES_ACCOUNT_MM)319ASSERT_EQ(nlock, get_vm_locked(parent));320ASSERT_EQ(0, get_vm_pinned(getpid()));321ASSERT_EQ(0, get_vm_locked(getpid()));322323ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_CHANGE_PROCESS, &cmd));324325/* Child accounts for locked memory after */326ASSERT_EQ(0, get_vm_pinned(parent));327ASSERT_EQ(0, get_vm_locked(parent));328ASSERT_EQ(nlock, get_vm_pinned(getpid()));329if (variant->accounting == IOPT_PAGES_ACCOUNT_MM)330ASSERT_EQ(nlock, get_vm_locked(getpid()));331332exit(0);333}334ASSERT_NE(-1, child);335ASSERT_EQ(child, waitpid(child, NULL, 0));336}337338FIXTURE(iommufd_ioas)339{340int fd;341uint32_t ioas_id;342uint32_t stdev_id;343uint32_t hwpt_id;344uint32_t device_id;345uint64_t base_iova;346uint32_t device_pasid_id;347};348349FIXTURE_VARIANT(iommufd_ioas)350{351unsigned int mock_domains;352unsigned int memory_limit;353bool pasid_capable;354};355356FIXTURE_SETUP(iommufd_ioas)357{358unsigned int i;359360361self->fd = open("/dev/iommu", O_RDWR);362ASSERT_NE(-1, self->fd);363test_ioctl_ioas_alloc(&self->ioas_id);364365if (!variant->memory_limit) {366test_ioctl_set_default_memory_limit();367} else {368test_ioctl_set_temp_memory_limit(variant->memory_limit);369}370371for (i = 0; i != variant->mock_domains; i++) {372test_cmd_mock_domain(self->ioas_id, &self->stdev_id,373&self->hwpt_id, &self->device_id);374test_cmd_dev_check_cache_all(self->device_id,375IOMMU_TEST_DEV_CACHE_DEFAULT);376self->base_iova = MOCK_APERTURE_START;377}378379if (variant->pasid_capable)380test_cmd_mock_domain_flags(self->ioas_id,381MOCK_FLAGS_DEVICE_PASID,382NULL, NULL,383&self->device_pasid_id);384}385386FIXTURE_TEARDOWN(iommufd_ioas)387{388test_ioctl_set_default_memory_limit();389teardown_iommufd(self->fd, _metadata);390}391392FIXTURE_VARIANT_ADD(iommufd_ioas, no_domain)393{394};395396FIXTURE_VARIANT_ADD(iommufd_ioas, mock_domain)397{398.mock_domains = 1,399.pasid_capable = true,400};401402FIXTURE_VARIANT_ADD(iommufd_ioas, two_mock_domain)403{404.mock_domains = 2,405};406407FIXTURE_VARIANT_ADD(iommufd_ioas, mock_domain_limit)408{409.mock_domains = 1,410.memory_limit = 16,411};412413TEST_F(iommufd_ioas, ioas_auto_destroy)414{415}416417TEST_F(iommufd_ioas, ioas_destroy)418{419if (self->stdev_id) {420/* IOAS cannot be freed while a device has a HWPT using it */421EXPECT_ERRNO(EBUSY,422_test_ioctl_destroy(self->fd, self->ioas_id));423} else {424/* Can allocate and manually free an IOAS table */425test_ioctl_destroy(self->ioas_id);426}427}428429TEST_F(iommufd_ioas, alloc_hwpt_nested)430{431const uint32_t min_data_len =432offsetofend(struct iommu_hwpt_selftest, iotlb);433struct iommu_hwpt_selftest data = {434.iotlb = IOMMU_TEST_IOTLB_DEFAULT,435};436struct iommu_hwpt_invalidate_selftest inv_reqs[2] = {};437uint32_t nested_hwpt_id[2] = {};438uint32_t num_inv;439uint32_t parent_hwpt_id = 0;440uint32_t parent_hwpt_id_not_work = 0;441uint32_t test_hwpt_id = 0;442uint32_t iopf_hwpt_id;443uint32_t fault_id;444uint32_t fault_fd;445446if (self->device_id) {447/* Negative tests */448test_err_hwpt_alloc(ENOENT, self->ioas_id, self->device_id, 0,449&test_hwpt_id);450test_err_hwpt_alloc(EINVAL, self->device_id, self->device_id, 0,451&test_hwpt_id);452test_err_hwpt_alloc(EOPNOTSUPP, self->device_id, self->ioas_id,453IOMMU_HWPT_ALLOC_NEST_PARENT |454IOMMU_HWPT_FAULT_ID_VALID,455&test_hwpt_id);456457test_cmd_hwpt_alloc(self->device_id, self->ioas_id,458IOMMU_HWPT_ALLOC_NEST_PARENT,459&parent_hwpt_id);460461test_cmd_hwpt_alloc(self->device_id, self->ioas_id, 0,462&parent_hwpt_id_not_work);463464/* Negative nested tests */465test_err_hwpt_alloc_nested(EINVAL, self->device_id,466parent_hwpt_id, 0,467&nested_hwpt_id[0],468IOMMU_HWPT_DATA_NONE, &data,469sizeof(data));470test_err_hwpt_alloc_nested(EOPNOTSUPP, self->device_id,471parent_hwpt_id, 0,472&nested_hwpt_id[0],473IOMMU_HWPT_DATA_SELFTEST + 1, &data,474sizeof(data));475test_err_hwpt_alloc_nested(EINVAL, self->device_id,476parent_hwpt_id, 0,477&nested_hwpt_id[0],478IOMMU_HWPT_DATA_SELFTEST, &data,479min_data_len - 1);480test_err_hwpt_alloc_nested(EFAULT, self->device_id,481parent_hwpt_id, 0,482&nested_hwpt_id[0],483IOMMU_HWPT_DATA_SELFTEST, NULL,484sizeof(data));485test_err_hwpt_alloc_nested(486EOPNOTSUPP, self->device_id, parent_hwpt_id,487IOMMU_HWPT_ALLOC_NEST_PARENT, &nested_hwpt_id[0],488IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data));489test_err_hwpt_alloc_nested(EINVAL, self->device_id,490parent_hwpt_id_not_work, 0,491&nested_hwpt_id[0],492IOMMU_HWPT_DATA_SELFTEST, &data,493sizeof(data));494495/* Allocate two nested hwpts sharing one common parent hwpt */496test_ioctl_fault_alloc(&fault_id, &fault_fd);497test_cmd_hwpt_alloc_nested(self->device_id, parent_hwpt_id, 0,498&nested_hwpt_id[0],499IOMMU_HWPT_DATA_SELFTEST, &data,500sizeof(data));501test_cmd_hwpt_alloc_nested(self->device_id, parent_hwpt_id, 0,502&nested_hwpt_id[1],503IOMMU_HWPT_DATA_SELFTEST, &data,504sizeof(data));505test_err_hwpt_alloc_iopf(ENOENT, self->device_id, parent_hwpt_id,506UINT32_MAX, IOMMU_HWPT_FAULT_ID_VALID,507&iopf_hwpt_id, IOMMU_HWPT_DATA_SELFTEST,508&data, sizeof(data));509test_cmd_hwpt_alloc_iopf(self->device_id, parent_hwpt_id, fault_id,510IOMMU_HWPT_FAULT_ID_VALID, &iopf_hwpt_id,511IOMMU_HWPT_DATA_SELFTEST, &data,512sizeof(data));513test_cmd_hwpt_check_iotlb_all(nested_hwpt_id[0],514IOMMU_TEST_IOTLB_DEFAULT);515test_cmd_hwpt_check_iotlb_all(nested_hwpt_id[1],516IOMMU_TEST_IOTLB_DEFAULT);517518/* Negative test: a nested hwpt on top of a nested hwpt */519test_err_hwpt_alloc_nested(EINVAL, self->device_id,520nested_hwpt_id[0], 0, &test_hwpt_id,521IOMMU_HWPT_DATA_SELFTEST, &data,522sizeof(data));523/* Negative test: parent hwpt now cannot be freed */524EXPECT_ERRNO(EBUSY,525_test_ioctl_destroy(self->fd, parent_hwpt_id));526527/* hwpt_invalidate does not support a parent hwpt */528num_inv = 1;529test_err_hwpt_invalidate(EINVAL, parent_hwpt_id, inv_reqs,530IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,531sizeof(*inv_reqs), &num_inv);532assert(!num_inv);533534/* Check data_type by passing zero-length array */535num_inv = 0;536test_cmd_hwpt_invalidate(nested_hwpt_id[0], inv_reqs,537IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,538sizeof(*inv_reqs), &num_inv);539assert(!num_inv);540541/* Negative test: Invalid data_type */542num_inv = 1;543test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs,544IOMMU_HWPT_INVALIDATE_DATA_SELFTEST_INVALID,545sizeof(*inv_reqs), &num_inv);546assert(!num_inv);547548/* Negative test: structure size sanity */549num_inv = 1;550test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs,551IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,552sizeof(*inv_reqs) + 1, &num_inv);553assert(!num_inv);554555num_inv = 1;556test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs,557IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,5581, &num_inv);559assert(!num_inv);560561/* Negative test: invalid flag is passed */562num_inv = 1;563inv_reqs[0].flags = 0xffffffff;564test_err_hwpt_invalidate(EOPNOTSUPP, nested_hwpt_id[0], inv_reqs,565IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,566sizeof(*inv_reqs), &num_inv);567assert(!num_inv);568569/* Negative test: invalid data_uptr when array is not empty */570num_inv = 1;571inv_reqs[0].flags = 0;572test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], NULL,573IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,574sizeof(*inv_reqs), &num_inv);575assert(!num_inv);576577/* Negative test: invalid entry_len when array is not empty */578num_inv = 1;579inv_reqs[0].flags = 0;580test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs,581IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,5820, &num_inv);583assert(!num_inv);584585/* Negative test: invalid iotlb_id */586num_inv = 1;587inv_reqs[0].flags = 0;588inv_reqs[0].iotlb_id = MOCK_NESTED_DOMAIN_IOTLB_ID_MAX + 1;589test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs,590IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,591sizeof(*inv_reqs), &num_inv);592assert(!num_inv);593594/*595* Invalidate the 1st iotlb entry but fail the 2nd request596* due to invalid flags configuration in the 2nd request.597*/598num_inv = 2;599inv_reqs[0].flags = 0;600inv_reqs[0].iotlb_id = 0;601inv_reqs[1].flags = 0xffffffff;602inv_reqs[1].iotlb_id = 1;603test_err_hwpt_invalidate(EOPNOTSUPP, nested_hwpt_id[0], inv_reqs,604IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,605sizeof(*inv_reqs), &num_inv);606assert(num_inv == 1);607test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 0, 0);608test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 1,609IOMMU_TEST_IOTLB_DEFAULT);610test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 2,611IOMMU_TEST_IOTLB_DEFAULT);612test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 3,613IOMMU_TEST_IOTLB_DEFAULT);614615/*616* Invalidate the 1st iotlb entry but fail the 2nd request617* due to invalid iotlb_id configuration in the 2nd request.618*/619num_inv = 2;620inv_reqs[0].flags = 0;621inv_reqs[0].iotlb_id = 0;622inv_reqs[1].flags = 0;623inv_reqs[1].iotlb_id = MOCK_NESTED_DOMAIN_IOTLB_ID_MAX + 1;624test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs,625IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,626sizeof(*inv_reqs), &num_inv);627assert(num_inv == 1);628test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 0, 0);629test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 1,630IOMMU_TEST_IOTLB_DEFAULT);631test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 2,632IOMMU_TEST_IOTLB_DEFAULT);633test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 3,634IOMMU_TEST_IOTLB_DEFAULT);635636/* Invalidate the 2nd iotlb entry and verify */637num_inv = 1;638inv_reqs[0].flags = 0;639inv_reqs[0].iotlb_id = 1;640test_cmd_hwpt_invalidate(nested_hwpt_id[0], inv_reqs,641IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,642sizeof(*inv_reqs), &num_inv);643assert(num_inv == 1);644test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 0, 0);645test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 1, 0);646test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 2,647IOMMU_TEST_IOTLB_DEFAULT);648test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 3,649IOMMU_TEST_IOTLB_DEFAULT);650651/* Invalidate the 3rd and 4th iotlb entries and verify */652num_inv = 2;653inv_reqs[0].flags = 0;654inv_reqs[0].iotlb_id = 2;655inv_reqs[1].flags = 0;656inv_reqs[1].iotlb_id = 3;657test_cmd_hwpt_invalidate(nested_hwpt_id[0], inv_reqs,658IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,659sizeof(*inv_reqs), &num_inv);660assert(num_inv == 2);661test_cmd_hwpt_check_iotlb_all(nested_hwpt_id[0], 0);662663/* Invalidate all iotlb entries for nested_hwpt_id[1] and verify */664num_inv = 1;665inv_reqs[0].flags = IOMMU_TEST_INVALIDATE_FLAG_ALL;666test_cmd_hwpt_invalidate(nested_hwpt_id[1], inv_reqs,667IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,668sizeof(*inv_reqs), &num_inv);669assert(num_inv == 1);670test_cmd_hwpt_check_iotlb_all(nested_hwpt_id[1], 0);671672/* Attach device to nested_hwpt_id[0] that then will be busy */673test_cmd_mock_domain_replace(self->stdev_id, nested_hwpt_id[0]);674EXPECT_ERRNO(EBUSY,675_test_ioctl_destroy(self->fd, nested_hwpt_id[0]));676677/* Switch from nested_hwpt_id[0] to nested_hwpt_id[1] */678test_cmd_mock_domain_replace(self->stdev_id, nested_hwpt_id[1]);679EXPECT_ERRNO(EBUSY,680_test_ioctl_destroy(self->fd, nested_hwpt_id[1]));681test_ioctl_destroy(nested_hwpt_id[0]);682683/* Switch from nested_hwpt_id[1] to iopf_hwpt_id */684test_cmd_mock_domain_replace(self->stdev_id, iopf_hwpt_id);685EXPECT_ERRNO(EBUSY,686_test_ioctl_destroy(self->fd, iopf_hwpt_id));687/* Trigger an IOPF on the device */688test_cmd_trigger_iopf(self->device_id, fault_fd);689690/* Detach from nested_hwpt_id[1] and destroy it */691test_cmd_mock_domain_replace(self->stdev_id, parent_hwpt_id);692test_ioctl_destroy(nested_hwpt_id[1]);693test_ioctl_destroy(iopf_hwpt_id);694695/* Detach from the parent hw_pagetable and destroy it */696test_cmd_mock_domain_replace(self->stdev_id, self->ioas_id);697test_ioctl_destroy(parent_hwpt_id);698test_ioctl_destroy(parent_hwpt_id_not_work);699close(fault_fd);700test_ioctl_destroy(fault_id);701} else {702test_err_hwpt_alloc(ENOENT, self->device_id, self->ioas_id, 0,703&parent_hwpt_id);704test_err_hwpt_alloc_nested(ENOENT, self->device_id,705parent_hwpt_id, 0,706&nested_hwpt_id[0],707IOMMU_HWPT_DATA_SELFTEST, &data,708sizeof(data));709test_err_hwpt_alloc_nested(ENOENT, self->device_id,710parent_hwpt_id, 0,711&nested_hwpt_id[1],712IOMMU_HWPT_DATA_SELFTEST, &data,713sizeof(data));714test_err_mock_domain_replace(ENOENT, self->stdev_id,715nested_hwpt_id[0]);716test_err_mock_domain_replace(ENOENT, self->stdev_id,717nested_hwpt_id[1]);718}719}720721TEST_F(iommufd_ioas, hwpt_attach)722{723/* Create a device attached directly to a hwpt */724if (self->stdev_id) {725test_cmd_mock_domain(self->hwpt_id, NULL, NULL, NULL);726} else {727test_err_mock_domain(ENOENT, self->hwpt_id, NULL, NULL);728}729}730731TEST_F(iommufd_ioas, ioas_area_destroy)732{733/* Adding an area does not change ability to destroy */734test_ioctl_ioas_map_fixed(buffer, PAGE_SIZE, self->base_iova);735if (self->stdev_id)736EXPECT_ERRNO(EBUSY,737_test_ioctl_destroy(self->fd, self->ioas_id));738else739test_ioctl_destroy(self->ioas_id);740}741742TEST_F(iommufd_ioas, ioas_area_auto_destroy)743{744int i;745746/* Can allocate and automatically free an IOAS table with many areas */747for (i = 0; i != 10; i++) {748test_ioctl_ioas_map_fixed(buffer, PAGE_SIZE,749self->base_iova + i * PAGE_SIZE);750}751}752753TEST_F(iommufd_ioas, get_hw_info)754{755struct iommu_test_hw_info buffer_exact;756struct iommu_test_hw_info_buffer_larger {757struct iommu_test_hw_info info;758uint64_t trailing_bytes;759} buffer_larger;760struct iommu_test_hw_info_buffer_smaller {761__u32 flags;762} buffer_smaller;763764if (self->device_id) {765uint8_t max_pasid = 0;766767/* Provide a zero-size user_buffer */768test_cmd_get_hw_info(self->device_id,769IOMMU_HW_INFO_TYPE_DEFAULT, NULL, 0);770/* Provide a user_buffer with exact size */771test_cmd_get_hw_info(self->device_id,772IOMMU_HW_INFO_TYPE_DEFAULT, &buffer_exact,773sizeof(buffer_exact));774775/* Request for a wrong data_type, and a correct one */776test_err_get_hw_info(EOPNOTSUPP, self->device_id,777IOMMU_HW_INFO_TYPE_SELFTEST + 1,778&buffer_exact, sizeof(buffer_exact));779test_cmd_get_hw_info(self->device_id,780IOMMU_HW_INFO_TYPE_SELFTEST, &buffer_exact,781sizeof(buffer_exact));782/*783* Provide a user_buffer with size larger than the exact size to check if784* kernel zero the trailing bytes.785*/786test_cmd_get_hw_info(self->device_id,787IOMMU_HW_INFO_TYPE_DEFAULT, &buffer_larger,788sizeof(buffer_larger));789/*790* Provide a user_buffer with size smaller than the exact size to check if791* the fields within the size range still gets updated.792*/793test_cmd_get_hw_info(self->device_id,794IOMMU_HW_INFO_TYPE_DEFAULT,795&buffer_smaller, sizeof(buffer_smaller));796test_cmd_get_hw_info_pasid(self->device_id, &max_pasid);797ASSERT_EQ(0, max_pasid);798if (variant->pasid_capable) {799test_cmd_get_hw_info_pasid(self->device_pasid_id,800&max_pasid);801ASSERT_EQ(MOCK_PASID_WIDTH, max_pasid);802}803} else {804test_err_get_hw_info(ENOENT, self->device_id,805IOMMU_HW_INFO_TYPE_DEFAULT, &buffer_exact,806sizeof(buffer_exact));807test_err_get_hw_info(ENOENT, self->device_id,808IOMMU_HW_INFO_TYPE_DEFAULT, &buffer_larger,809sizeof(buffer_larger));810}811}812813TEST_F(iommufd_ioas, area)814{815int i;816817/* Unmap fails if nothing is mapped */818for (i = 0; i != 10; i++)819test_err_ioctl_ioas_unmap(ENOENT, i * PAGE_SIZE, PAGE_SIZE);820821/* Unmap works */822for (i = 0; i != 10; i++)823test_ioctl_ioas_map_fixed(buffer, PAGE_SIZE,824self->base_iova + i * PAGE_SIZE);825for (i = 0; i != 10; i++)826test_ioctl_ioas_unmap(self->base_iova + i * PAGE_SIZE,827PAGE_SIZE);828829/* Split fails */830test_ioctl_ioas_map_fixed(buffer, PAGE_SIZE * 2,831self->base_iova + 16 * PAGE_SIZE);832test_err_ioctl_ioas_unmap(ENOENT, self->base_iova + 16 * PAGE_SIZE,833PAGE_SIZE);834test_err_ioctl_ioas_unmap(ENOENT, self->base_iova + 17 * PAGE_SIZE,835PAGE_SIZE);836837/* Over map fails */838test_err_ioctl_ioas_map_fixed(EEXIST, buffer, PAGE_SIZE * 2,839self->base_iova + 16 * PAGE_SIZE);840test_err_ioctl_ioas_map_fixed(EEXIST, buffer, PAGE_SIZE,841self->base_iova + 16 * PAGE_SIZE);842test_err_ioctl_ioas_map_fixed(EEXIST, buffer, PAGE_SIZE,843self->base_iova + 17 * PAGE_SIZE);844test_err_ioctl_ioas_map_fixed(EEXIST, buffer, PAGE_SIZE * 2,845self->base_iova + 15 * PAGE_SIZE);846test_err_ioctl_ioas_map_fixed(EEXIST, buffer, PAGE_SIZE * 3,847self->base_iova + 15 * PAGE_SIZE);848849/* unmap all works */850test_ioctl_ioas_unmap(0, UINT64_MAX);851852/* Unmap all succeeds on an empty IOAS */853test_ioctl_ioas_unmap(0, UINT64_MAX);854}855856TEST_F(iommufd_ioas, unmap_fully_contained_areas)857{858uint64_t unmap_len;859int i;860861/* Give no_domain some space to rewind base_iova */862self->base_iova += 4 * PAGE_SIZE;863864for (i = 0; i != 4; i++)865test_ioctl_ioas_map_fixed(buffer, 8 * PAGE_SIZE,866self->base_iova + i * 16 * PAGE_SIZE);867868/* Unmap not fully contained area doesn't work */869test_err_ioctl_ioas_unmap(ENOENT, self->base_iova - 4 * PAGE_SIZE,8708 * PAGE_SIZE);871test_err_ioctl_ioas_unmap(ENOENT,872self->base_iova + 3 * 16 * PAGE_SIZE +8738 * PAGE_SIZE - 4 * PAGE_SIZE,8748 * PAGE_SIZE);875876/* Unmap fully contained areas works */877ASSERT_EQ(0, _test_ioctl_ioas_unmap(self->fd, self->ioas_id,878self->base_iova - 4 * PAGE_SIZE,8793 * 16 * PAGE_SIZE + 8 * PAGE_SIZE +8804 * PAGE_SIZE,881&unmap_len));882ASSERT_EQ(32 * PAGE_SIZE, unmap_len);883}884885TEST_F(iommufd_ioas, area_auto_iova)886{887struct iommu_test_cmd test_cmd = {888.size = sizeof(test_cmd),889.op = IOMMU_TEST_OP_ADD_RESERVED,890.id = self->ioas_id,891.add_reserved = { .start = PAGE_SIZE * 4,892.length = PAGE_SIZE * 100 },893};894struct iommu_iova_range ranges[1] = {};895struct iommu_ioas_allow_iovas allow_cmd = {896.size = sizeof(allow_cmd),897.ioas_id = self->ioas_id,898.num_iovas = 1,899.allowed_iovas = (uintptr_t)ranges,900};901__u64 iovas[10];902int i;903904/* Simple 4k pages */905for (i = 0; i != 10; i++)906test_ioctl_ioas_map(buffer, PAGE_SIZE, &iovas[i]);907for (i = 0; i != 10; i++)908test_ioctl_ioas_unmap(iovas[i], PAGE_SIZE);909910/* Kernel automatically aligns IOVAs properly */911for (i = 0; i != 10; i++) {912size_t length = PAGE_SIZE * (i + 1);913914if (self->stdev_id) {915test_ioctl_ioas_map(buffer, length, &iovas[i]);916} else {917test_ioctl_ioas_map((void *)(1UL << 31), length,918&iovas[i]);919}920EXPECT_EQ(0, iovas[i] % (1UL << (ffs(length) - 1)));921}922for (i = 0; i != 10; i++)923test_ioctl_ioas_unmap(iovas[i], PAGE_SIZE * (i + 1));924925/* Avoids a reserved region */926ASSERT_EQ(0,927ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ADD_RESERVED),928&test_cmd));929for (i = 0; i != 10; i++) {930size_t length = PAGE_SIZE * (i + 1);931932test_ioctl_ioas_map(buffer, length, &iovas[i]);933EXPECT_EQ(0, iovas[i] % (1UL << (ffs(length) - 1)));934EXPECT_EQ(false,935iovas[i] > test_cmd.add_reserved.start &&936iovas[i] <937test_cmd.add_reserved.start +938test_cmd.add_reserved.length);939}940for (i = 0; i != 10; i++)941test_ioctl_ioas_unmap(iovas[i], PAGE_SIZE * (i + 1));942943/* Allowed region intersects with a reserved region */944ranges[0].start = PAGE_SIZE;945ranges[0].last = PAGE_SIZE * 600;946EXPECT_ERRNO(EADDRINUSE,947ioctl(self->fd, IOMMU_IOAS_ALLOW_IOVAS, &allow_cmd));948949/* Allocate from an allowed region */950if (self->stdev_id) {951ranges[0].start = MOCK_APERTURE_START + PAGE_SIZE;952ranges[0].last = MOCK_APERTURE_START + PAGE_SIZE * 600 - 1;953} else {954ranges[0].start = PAGE_SIZE * 200;955ranges[0].last = PAGE_SIZE * 600 - 1;956}957ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_ALLOW_IOVAS, &allow_cmd));958for (i = 0; i != 10; i++) {959size_t length = PAGE_SIZE * (i + 1);960961test_ioctl_ioas_map(buffer, length, &iovas[i]);962EXPECT_EQ(0, iovas[i] % (1UL << (ffs(length) - 1)));963EXPECT_EQ(true, iovas[i] >= ranges[0].start);964EXPECT_EQ(true, iovas[i] <= ranges[0].last);965EXPECT_EQ(true, iovas[i] + length > ranges[0].start);966EXPECT_EQ(true, iovas[i] + length <= ranges[0].last + 1);967}968for (i = 0; i != 10; i++)969test_ioctl_ioas_unmap(iovas[i], PAGE_SIZE * (i + 1));970}971972/* https://lore.kernel.org/r/[email protected] */973TEST_F(iommufd_ioas, reserved_overflow)974{975struct iommu_test_cmd test_cmd = {976.size = sizeof(test_cmd),977.op = IOMMU_TEST_OP_ADD_RESERVED,978.id = self->ioas_id,979.add_reserved.start = 6,980};981unsigned int map_len;982__u64 iova;983984if (PAGE_SIZE == 4096) {985test_cmd.add_reserved.length = 0xffffffffffff8001;986map_len = 0x5000;987} else {988test_cmd.add_reserved.length =9890xffffffffffffffff - MOCK_PAGE_SIZE * 16;990map_len = MOCK_PAGE_SIZE * 10;991}992993ASSERT_EQ(0,994ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ADD_RESERVED),995&test_cmd));996test_err_ioctl_ioas_map(ENOSPC, buffer, map_len, &iova);997}998999TEST_F(iommufd_ioas, area_allowed)1000{1001struct iommu_test_cmd test_cmd = {1002.size = sizeof(test_cmd),1003.op = IOMMU_TEST_OP_ADD_RESERVED,1004.id = self->ioas_id,1005.add_reserved = { .start = PAGE_SIZE * 4,1006.length = PAGE_SIZE * 100 },1007};1008struct iommu_iova_range ranges[1] = {};1009struct iommu_ioas_allow_iovas allow_cmd = {1010.size = sizeof(allow_cmd),1011.ioas_id = self->ioas_id,1012.num_iovas = 1,1013.allowed_iovas = (uintptr_t)ranges,1014};10151016/* Reserved intersects an allowed */1017allow_cmd.num_iovas = 1;1018ranges[0].start = self->base_iova;1019ranges[0].last = ranges[0].start + PAGE_SIZE * 600;1020ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_ALLOW_IOVAS, &allow_cmd));1021test_cmd.add_reserved.start = ranges[0].start + PAGE_SIZE;1022test_cmd.add_reserved.length = PAGE_SIZE;1023EXPECT_ERRNO(EADDRINUSE,1024ioctl(self->fd,1025_IOMMU_TEST_CMD(IOMMU_TEST_OP_ADD_RESERVED),1026&test_cmd));1027allow_cmd.num_iovas = 0;1028ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_ALLOW_IOVAS, &allow_cmd));10291030/* Allowed intersects a reserved */1031ASSERT_EQ(0,1032ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ADD_RESERVED),1033&test_cmd));1034allow_cmd.num_iovas = 1;1035ranges[0].start = self->base_iova;1036ranges[0].last = ranges[0].start + PAGE_SIZE * 600;1037EXPECT_ERRNO(EADDRINUSE,1038ioctl(self->fd, IOMMU_IOAS_ALLOW_IOVAS, &allow_cmd));1039}10401041TEST_F(iommufd_ioas, copy_area)1042{1043struct iommu_ioas_copy copy_cmd = {1044.size = sizeof(copy_cmd),1045.flags = IOMMU_IOAS_MAP_FIXED_IOVA | IOMMU_IOAS_MAP_WRITEABLE,1046.dst_ioas_id = self->ioas_id,1047.src_ioas_id = self->ioas_id,1048.length = PAGE_SIZE,1049};10501051test_ioctl_ioas_map_fixed(buffer, PAGE_SIZE, self->base_iova);10521053/* Copy inside a single IOAS */1054copy_cmd.src_iova = self->base_iova;1055copy_cmd.dst_iova = self->base_iova + PAGE_SIZE;1056ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_COPY, ©_cmd));10571058/* Copy between IOAS's */1059copy_cmd.src_iova = self->base_iova;1060copy_cmd.dst_iova = 0;1061test_ioctl_ioas_alloc(©_cmd.dst_ioas_id);1062ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_COPY, ©_cmd));1063}10641065TEST_F(iommufd_ioas, iova_ranges)1066{1067struct iommu_test_cmd test_cmd = {1068.size = sizeof(test_cmd),1069.op = IOMMU_TEST_OP_ADD_RESERVED,1070.id = self->ioas_id,1071.add_reserved = { .start = PAGE_SIZE, .length = PAGE_SIZE },1072};1073struct iommu_iova_range *ranges = buffer;1074struct iommu_ioas_iova_ranges ranges_cmd = {1075.size = sizeof(ranges_cmd),1076.ioas_id = self->ioas_id,1077.num_iovas = BUFFER_SIZE / sizeof(*ranges),1078.allowed_iovas = (uintptr_t)ranges,1079};10801081/* Range can be read */1082ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_IOVA_RANGES, &ranges_cmd));1083EXPECT_EQ(1, ranges_cmd.num_iovas);1084if (!self->stdev_id) {1085EXPECT_EQ(0, ranges[0].start);1086EXPECT_EQ(SIZE_MAX, ranges[0].last);1087EXPECT_EQ(1, ranges_cmd.out_iova_alignment);1088} else {1089EXPECT_EQ(MOCK_APERTURE_START, ranges[0].start);1090EXPECT_EQ(MOCK_APERTURE_LAST, ranges[0].last);1091EXPECT_EQ(MOCK_PAGE_SIZE, ranges_cmd.out_iova_alignment);1092}10931094/* Buffer too small */1095memset(ranges, 0, BUFFER_SIZE);1096ranges_cmd.num_iovas = 0;1097EXPECT_ERRNO(EMSGSIZE,1098ioctl(self->fd, IOMMU_IOAS_IOVA_RANGES, &ranges_cmd));1099EXPECT_EQ(1, ranges_cmd.num_iovas);1100EXPECT_EQ(0, ranges[0].start);1101EXPECT_EQ(0, ranges[0].last);11021103/* 2 ranges */1104ASSERT_EQ(0,1105ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ADD_RESERVED),1106&test_cmd));1107ranges_cmd.num_iovas = BUFFER_SIZE / sizeof(*ranges);1108ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_IOVA_RANGES, &ranges_cmd));1109if (!self->stdev_id) {1110EXPECT_EQ(2, ranges_cmd.num_iovas);1111EXPECT_EQ(0, ranges[0].start);1112EXPECT_EQ(PAGE_SIZE - 1, ranges[0].last);1113EXPECT_EQ(PAGE_SIZE * 2, ranges[1].start);1114EXPECT_EQ(SIZE_MAX, ranges[1].last);1115} else {1116EXPECT_EQ(1, ranges_cmd.num_iovas);1117EXPECT_EQ(MOCK_APERTURE_START, ranges[0].start);1118EXPECT_EQ(MOCK_APERTURE_LAST, ranges[0].last);1119}11201121/* Buffer too small */1122memset(ranges, 0, BUFFER_SIZE);1123ranges_cmd.num_iovas = 1;1124if (!self->stdev_id) {1125EXPECT_ERRNO(EMSGSIZE, ioctl(self->fd, IOMMU_IOAS_IOVA_RANGES,1126&ranges_cmd));1127EXPECT_EQ(2, ranges_cmd.num_iovas);1128EXPECT_EQ(0, ranges[0].start);1129EXPECT_EQ(PAGE_SIZE - 1, ranges[0].last);1130} else {1131ASSERT_EQ(0,1132ioctl(self->fd, IOMMU_IOAS_IOVA_RANGES, &ranges_cmd));1133EXPECT_EQ(1, ranges_cmd.num_iovas);1134EXPECT_EQ(MOCK_APERTURE_START, ranges[0].start);1135EXPECT_EQ(MOCK_APERTURE_LAST, ranges[0].last);1136}1137EXPECT_EQ(0, ranges[1].start);1138EXPECT_EQ(0, ranges[1].last);1139}11401141TEST_F(iommufd_ioas, access_domain_destory)1142{1143struct iommu_test_cmd access_cmd = {1144.size = sizeof(access_cmd),1145.op = IOMMU_TEST_OP_ACCESS_PAGES,1146.access_pages = { .iova = self->base_iova + PAGE_SIZE,1147.length = PAGE_SIZE},1148};1149size_t buf_size = 2 * HUGEPAGE_SIZE;1150uint8_t *buf;11511152buf = mmap(0, buf_size, PROT_READ | PROT_WRITE,1153MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, -1,11540);1155ASSERT_NE(MAP_FAILED, buf);1156test_ioctl_ioas_map_fixed(buf, buf_size, self->base_iova);11571158test_cmd_create_access(self->ioas_id, &access_cmd.id,1159MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES);1160access_cmd.access_pages.uptr = (uintptr_t)buf + PAGE_SIZE;1161ASSERT_EQ(0,1162ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),1163&access_cmd));11641165/* Causes a complicated unpin across a huge page boundary */1166if (self->stdev_id)1167test_ioctl_destroy(self->stdev_id);11681169test_cmd_destroy_access_pages(1170access_cmd.id, access_cmd.access_pages.out_access_pages_id);1171test_cmd_destroy_access(access_cmd.id);1172ASSERT_EQ(0, munmap(buf, buf_size));1173}11741175TEST_F(iommufd_ioas, access_pin)1176{1177struct iommu_test_cmd access_cmd = {1178.size = sizeof(access_cmd),1179.op = IOMMU_TEST_OP_ACCESS_PAGES,1180.access_pages = { .iova = MOCK_APERTURE_START,1181.length = BUFFER_SIZE,1182.uptr = (uintptr_t)buffer },1183};1184struct iommu_test_cmd check_map_cmd = {1185.size = sizeof(check_map_cmd),1186.op = IOMMU_TEST_OP_MD_CHECK_MAP,1187.check_map = { .iova = MOCK_APERTURE_START,1188.length = BUFFER_SIZE,1189.uptr = (uintptr_t)buffer },1190};1191uint32_t access_pages_id;1192unsigned int npages;11931194test_cmd_create_access(self->ioas_id, &access_cmd.id,1195MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES);11961197for (npages = 1; npages < BUFFER_SIZE / PAGE_SIZE; npages++) {1198uint32_t mock_stdev_id;1199uint32_t mock_hwpt_id;12001201access_cmd.access_pages.length = npages * PAGE_SIZE;12021203/* Single map/unmap */1204test_ioctl_ioas_map_fixed(buffer, BUFFER_SIZE,1205MOCK_APERTURE_START);1206ASSERT_EQ(0, ioctl(self->fd,1207_IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),1208&access_cmd));1209test_cmd_destroy_access_pages(1210access_cmd.id,1211access_cmd.access_pages.out_access_pages_id);12121213/* Double user */1214ASSERT_EQ(0, ioctl(self->fd,1215_IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),1216&access_cmd));1217access_pages_id = access_cmd.access_pages.out_access_pages_id;1218ASSERT_EQ(0, ioctl(self->fd,1219_IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),1220&access_cmd));1221test_cmd_destroy_access_pages(1222access_cmd.id,1223access_cmd.access_pages.out_access_pages_id);1224test_cmd_destroy_access_pages(access_cmd.id, access_pages_id);12251226/* Add/remove a domain with a user */1227ASSERT_EQ(0, ioctl(self->fd,1228_IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),1229&access_cmd));1230test_cmd_mock_domain(self->ioas_id, &mock_stdev_id,1231&mock_hwpt_id, NULL);1232check_map_cmd.id = mock_hwpt_id;1233ASSERT_EQ(0, ioctl(self->fd,1234_IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_MAP),1235&check_map_cmd));12361237test_ioctl_destroy(mock_stdev_id);1238test_cmd_destroy_access_pages(1239access_cmd.id,1240access_cmd.access_pages.out_access_pages_id);12411242test_ioctl_ioas_unmap(MOCK_APERTURE_START, BUFFER_SIZE);1243}1244test_cmd_destroy_access(access_cmd.id);1245}12461247TEST_F(iommufd_ioas, access_pin_unmap)1248{1249struct iommu_test_cmd access_pages_cmd = {1250.size = sizeof(access_pages_cmd),1251.op = IOMMU_TEST_OP_ACCESS_PAGES,1252.access_pages = { .iova = MOCK_APERTURE_START,1253.length = BUFFER_SIZE,1254.uptr = (uintptr_t)buffer },1255};12561257test_cmd_create_access(self->ioas_id, &access_pages_cmd.id,1258MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES);1259test_ioctl_ioas_map_fixed(buffer, BUFFER_SIZE, MOCK_APERTURE_START);1260ASSERT_EQ(0,1261ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),1262&access_pages_cmd));12631264/* Trigger the unmap op */1265test_ioctl_ioas_unmap(MOCK_APERTURE_START, BUFFER_SIZE);12661267/* kernel removed the item for us */1268test_err_destroy_access_pages(1269ENOENT, access_pages_cmd.id,1270access_pages_cmd.access_pages.out_access_pages_id);1271}12721273static void check_access_rw(struct __test_metadata *_metadata, int fd,1274unsigned int access_id, uint64_t iova,1275unsigned int def_flags)1276{1277uint16_t tmp[32];1278struct iommu_test_cmd access_cmd = {1279.size = sizeof(access_cmd),1280.op = IOMMU_TEST_OP_ACCESS_RW,1281.id = access_id,1282.access_rw = { .uptr = (uintptr_t)tmp },1283};1284uint16_t *buffer16 = buffer;1285unsigned int i;1286void *tmp2;12871288for (i = 0; i != BUFFER_SIZE / sizeof(*buffer16); i++)1289buffer16[i] = rand();12901291for (access_cmd.access_rw.iova = iova + PAGE_SIZE - 50;1292access_cmd.access_rw.iova < iova + PAGE_SIZE + 50;1293access_cmd.access_rw.iova++) {1294for (access_cmd.access_rw.length = 1;1295access_cmd.access_rw.length < sizeof(tmp);1296access_cmd.access_rw.length++) {1297access_cmd.access_rw.flags = def_flags;1298ASSERT_EQ(0, ioctl(fd,1299_IOMMU_TEST_CMD(1300IOMMU_TEST_OP_ACCESS_RW),1301&access_cmd));1302ASSERT_EQ(0,1303memcmp(buffer + (access_cmd.access_rw.iova -1304iova),1305tmp, access_cmd.access_rw.length));13061307for (i = 0; i != ARRAY_SIZE(tmp); i++)1308tmp[i] = rand();1309access_cmd.access_rw.flags = def_flags |1310MOCK_ACCESS_RW_WRITE;1311ASSERT_EQ(0, ioctl(fd,1312_IOMMU_TEST_CMD(1313IOMMU_TEST_OP_ACCESS_RW),1314&access_cmd));1315ASSERT_EQ(0,1316memcmp(buffer + (access_cmd.access_rw.iova -1317iova),1318tmp, access_cmd.access_rw.length));1319}1320}13211322/* Multi-page test */1323tmp2 = malloc(BUFFER_SIZE);1324ASSERT_NE(NULL, tmp2);1325access_cmd.access_rw.iova = iova;1326access_cmd.access_rw.length = BUFFER_SIZE;1327access_cmd.access_rw.flags = def_flags;1328access_cmd.access_rw.uptr = (uintptr_t)tmp2;1329ASSERT_EQ(0, ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_RW),1330&access_cmd));1331ASSERT_EQ(0, memcmp(buffer, tmp2, access_cmd.access_rw.length));1332free(tmp2);1333}13341335TEST_F(iommufd_ioas, access_rw)1336{1337__u32 access_id;1338__u64 iova;13391340test_cmd_create_access(self->ioas_id, &access_id, 0);1341test_ioctl_ioas_map(buffer, BUFFER_SIZE, &iova);1342check_access_rw(_metadata, self->fd, access_id, iova, 0);1343check_access_rw(_metadata, self->fd, access_id, iova,1344MOCK_ACCESS_RW_SLOW_PATH);1345test_ioctl_ioas_unmap(iova, BUFFER_SIZE);1346test_cmd_destroy_access(access_id);1347}13481349TEST_F(iommufd_ioas, access_rw_unaligned)1350{1351__u32 access_id;1352__u64 iova;13531354test_cmd_create_access(self->ioas_id, &access_id, 0);13551356/* Unaligned pages */1357iova = self->base_iova + MOCK_PAGE_SIZE;1358test_ioctl_ioas_map_fixed(buffer, BUFFER_SIZE, iova);1359check_access_rw(_metadata, self->fd, access_id, iova, 0);1360test_ioctl_ioas_unmap(iova, BUFFER_SIZE);1361test_cmd_destroy_access(access_id);1362}13631364TEST_F(iommufd_ioas, fork_gone)1365{1366__u32 access_id;1367pid_t child;13681369test_cmd_create_access(self->ioas_id, &access_id, 0);13701371/* Create a mapping with a different mm */1372child = fork();1373if (!child) {1374test_ioctl_ioas_map_fixed(buffer, BUFFER_SIZE,1375MOCK_APERTURE_START);1376exit(0);1377}1378ASSERT_NE(-1, child);1379ASSERT_EQ(child, waitpid(child, NULL, 0));13801381if (self->stdev_id) {1382/*1383* If a domain already existed then everything was pinned within1384* the fork, so this copies from one domain to another.1385*/1386test_cmd_mock_domain(self->ioas_id, NULL, NULL, NULL);1387check_access_rw(_metadata, self->fd, access_id,1388MOCK_APERTURE_START, 0);13891390} else {1391/*1392* Otherwise we need to actually pin pages which can't happen1393* since the fork is gone.1394*/1395test_err_mock_domain(EFAULT, self->ioas_id, NULL, NULL);1396}13971398test_cmd_destroy_access(access_id);1399}14001401TEST_F(iommufd_ioas, fork_present)1402{1403__u32 access_id;1404int pipefds[2];1405uint64_t tmp;1406pid_t child;1407int efd;14081409test_cmd_create_access(self->ioas_id, &access_id, 0);14101411ASSERT_EQ(0, pipe2(pipefds, O_CLOEXEC));1412efd = eventfd(0, EFD_CLOEXEC);1413ASSERT_NE(-1, efd);14141415/* Create a mapping with a different mm */1416child = fork();1417if (!child) {1418__u64 iova;1419uint64_t one = 1;14201421close(pipefds[1]);1422test_ioctl_ioas_map_fixed(buffer, BUFFER_SIZE,1423MOCK_APERTURE_START);1424if (write(efd, &one, sizeof(one)) != sizeof(one))1425exit(100);1426if (read(pipefds[0], &iova, 1) != 1)1427exit(100);1428exit(0);1429}1430close(pipefds[0]);1431ASSERT_NE(-1, child);1432ASSERT_EQ(8, read(efd, &tmp, sizeof(tmp)));14331434/* Read pages from the remote process */1435test_cmd_mock_domain(self->ioas_id, NULL, NULL, NULL);1436check_access_rw(_metadata, self->fd, access_id, MOCK_APERTURE_START, 0);14371438ASSERT_EQ(0, close(pipefds[1]));1439ASSERT_EQ(child, waitpid(child, NULL, 0));14401441test_cmd_destroy_access(access_id);1442}14431444TEST_F(iommufd_ioas, ioas_option_huge_pages)1445{1446struct iommu_option cmd = {1447.size = sizeof(cmd),1448.option_id = IOMMU_OPTION_HUGE_PAGES,1449.op = IOMMU_OPTION_OP_GET,1450.val64 = 3,1451.object_id = self->ioas_id,1452};14531454ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));1455ASSERT_EQ(1, cmd.val64);14561457cmd.op = IOMMU_OPTION_OP_SET;1458cmd.val64 = 0;1459ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));14601461cmd.op = IOMMU_OPTION_OP_GET;1462cmd.val64 = 3;1463ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));1464ASSERT_EQ(0, cmd.val64);14651466cmd.op = IOMMU_OPTION_OP_SET;1467cmd.val64 = 2;1468EXPECT_ERRNO(EINVAL, ioctl(self->fd, IOMMU_OPTION, &cmd));14691470cmd.op = IOMMU_OPTION_OP_SET;1471cmd.val64 = 1;1472ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));1473}14741475TEST_F(iommufd_ioas, ioas_iova_alloc)1476{1477unsigned int length;1478__u64 iova;14791480for (length = 1; length != PAGE_SIZE * 2; length++) {1481if (variant->mock_domains && (length % MOCK_PAGE_SIZE)) {1482test_err_ioctl_ioas_map(EINVAL, buffer, length, &iova);1483} else {1484test_ioctl_ioas_map(buffer, length, &iova);1485test_ioctl_ioas_unmap(iova, length);1486}1487}1488}14891490TEST_F(iommufd_ioas, ioas_align_change)1491{1492struct iommu_option cmd = {1493.size = sizeof(cmd),1494.option_id = IOMMU_OPTION_HUGE_PAGES,1495.op = IOMMU_OPTION_OP_SET,1496.object_id = self->ioas_id,1497/* 0 means everything must be aligned to PAGE_SIZE */1498.val64 = 0,1499};15001501/*1502* We cannot upgrade the alignment using OPTION_HUGE_PAGES when a domain1503* and map are present.1504*/1505if (variant->mock_domains)1506return;15071508/*1509* We can upgrade to PAGE_SIZE alignment when things are aligned right1510*/1511test_ioctl_ioas_map_fixed(buffer, PAGE_SIZE, MOCK_APERTURE_START);1512ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));15131514/* Misalignment is rejected at map time */1515test_err_ioctl_ioas_map_fixed(EINVAL, buffer + MOCK_PAGE_SIZE,1516PAGE_SIZE,1517MOCK_APERTURE_START + PAGE_SIZE);1518ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));15191520/* Reduce alignment */1521cmd.val64 = 1;1522ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));15231524/* Confirm misalignment is rejected during alignment upgrade */1525test_ioctl_ioas_map_fixed(buffer + MOCK_PAGE_SIZE, PAGE_SIZE,1526MOCK_APERTURE_START + PAGE_SIZE);1527cmd.val64 = 0;1528EXPECT_ERRNO(EADDRINUSE, ioctl(self->fd, IOMMU_OPTION, &cmd));15291530test_ioctl_ioas_unmap(MOCK_APERTURE_START + PAGE_SIZE, PAGE_SIZE);1531test_ioctl_ioas_unmap(MOCK_APERTURE_START, PAGE_SIZE);1532}15331534TEST_F(iommufd_ioas, copy_sweep)1535{1536struct iommu_ioas_copy copy_cmd = {1537.size = sizeof(copy_cmd),1538.flags = IOMMU_IOAS_MAP_FIXED_IOVA | IOMMU_IOAS_MAP_WRITEABLE,1539.src_ioas_id = self->ioas_id,1540.dst_iova = MOCK_APERTURE_START,1541.length = MOCK_PAGE_SIZE,1542};1543unsigned int dst_ioas_id;1544uint64_t last_iova;1545uint64_t iova;15461547test_ioctl_ioas_alloc(&dst_ioas_id);1548copy_cmd.dst_ioas_id = dst_ioas_id;15491550if (variant->mock_domains)1551last_iova = MOCK_APERTURE_START + BUFFER_SIZE - 1;1552else1553last_iova = MOCK_APERTURE_START + BUFFER_SIZE - 2;15541555test_ioctl_ioas_map_fixed(buffer, last_iova - MOCK_APERTURE_START + 1,1556MOCK_APERTURE_START);15571558for (iova = MOCK_APERTURE_START - PAGE_SIZE; iova <= last_iova;1559iova += 511) {1560copy_cmd.src_iova = iova;1561if (iova < MOCK_APERTURE_START ||1562iova + copy_cmd.length - 1 > last_iova) {1563EXPECT_ERRNO(ENOENT, ioctl(self->fd, IOMMU_IOAS_COPY,1564©_cmd));1565} else {1566ASSERT_EQ(0,1567ioctl(self->fd, IOMMU_IOAS_COPY, ©_cmd));1568test_ioctl_ioas_unmap_id(dst_ioas_id, copy_cmd.dst_iova,1569copy_cmd.length);1570}1571}15721573test_ioctl_destroy(dst_ioas_id);1574}15751576FIXTURE(iommufd_mock_domain)1577{1578int fd;1579uint32_t ioas_id;1580uint32_t hwpt_id;1581uint32_t hwpt_ids[2];1582uint32_t stdev_ids[2];1583uint32_t idev_ids[2];1584int mmap_flags;1585size_t mmap_buf_size;1586};15871588FIXTURE_VARIANT(iommufd_mock_domain)1589{1590unsigned int mock_domains;1591bool hugepages;1592bool file;1593};15941595FIXTURE_SETUP(iommufd_mock_domain)1596{1597unsigned int i;15981599self->fd = open("/dev/iommu", O_RDWR);1600ASSERT_NE(-1, self->fd);1601test_ioctl_ioas_alloc(&self->ioas_id);16021603ASSERT_GE(ARRAY_SIZE(self->hwpt_ids), variant->mock_domains);16041605for (i = 0; i != variant->mock_domains; i++) {1606test_cmd_mock_domain(self->ioas_id, &self->stdev_ids[i],1607&self->hwpt_ids[i], &self->idev_ids[i]);1608test_cmd_dev_check_cache_all(self->idev_ids[0],1609IOMMU_TEST_DEV_CACHE_DEFAULT);1610}1611self->hwpt_id = self->hwpt_ids[0];16121613self->mmap_flags = MAP_SHARED | MAP_ANONYMOUS;1614self->mmap_buf_size = PAGE_SIZE * 8;1615if (variant->hugepages) {1616/*1617* MAP_POPULATE will cause the kernel to fail mmap if THPs are1618* not available.1619*/1620self->mmap_flags |= MAP_HUGETLB | MAP_POPULATE;1621self->mmap_buf_size = HUGEPAGE_SIZE * 2;1622}1623}16241625FIXTURE_TEARDOWN(iommufd_mock_domain)1626{1627teardown_iommufd(self->fd, _metadata);1628}16291630FIXTURE_VARIANT_ADD(iommufd_mock_domain, one_domain)1631{1632.mock_domains = 1,1633.hugepages = false,1634.file = false,1635};16361637FIXTURE_VARIANT_ADD(iommufd_mock_domain, two_domains)1638{1639.mock_domains = 2,1640.hugepages = false,1641.file = false,1642};16431644FIXTURE_VARIANT_ADD(iommufd_mock_domain, one_domain_hugepage)1645{1646.mock_domains = 1,1647.hugepages = true,1648.file = false,1649};16501651FIXTURE_VARIANT_ADD(iommufd_mock_domain, two_domains_hugepage)1652{1653.mock_domains = 2,1654.hugepages = true,1655.file = false,1656};16571658FIXTURE_VARIANT_ADD(iommufd_mock_domain, one_domain_file)1659{1660.mock_domains = 1,1661.hugepages = false,1662.file = true,1663};16641665FIXTURE_VARIANT_ADD(iommufd_mock_domain, one_domain_file_hugepage)1666{1667.mock_domains = 1,1668.hugepages = true,1669.file = true,1670};167116721673/* Have the kernel check that the user pages made it to the iommu_domain */1674#define check_mock_iova(_ptr, _iova, _length) \1675({ \1676struct iommu_test_cmd check_map_cmd = { \1677.size = sizeof(check_map_cmd), \1678.op = IOMMU_TEST_OP_MD_CHECK_MAP, \1679.id = self->hwpt_id, \1680.check_map = { .iova = _iova, \1681.length = _length, \1682.uptr = (uintptr_t)(_ptr) }, \1683}; \1684ASSERT_EQ(0, \1685ioctl(self->fd, \1686_IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_MAP), \1687&check_map_cmd)); \1688if (self->hwpt_ids[1]) { \1689check_map_cmd.id = self->hwpt_ids[1]; \1690ASSERT_EQ(0, \1691ioctl(self->fd, \1692_IOMMU_TEST_CMD( \1693IOMMU_TEST_OP_MD_CHECK_MAP), \1694&check_map_cmd)); \1695} \1696})16971698static void1699test_basic_mmap(struct __test_metadata *_metadata,1700struct _test_data_iommufd_mock_domain *self,1701const struct _fixture_variant_iommufd_mock_domain *variant)1702{1703size_t buf_size = self->mmap_buf_size;1704uint8_t *buf;1705__u64 iova;17061707/* Simple one page map */1708test_ioctl_ioas_map(buffer, PAGE_SIZE, &iova);1709check_mock_iova(buffer, iova, PAGE_SIZE);17101711buf = mmap(0, buf_size, PROT_READ | PROT_WRITE, self->mmap_flags, -1,17120);1713ASSERT_NE(MAP_FAILED, buf);17141715/* EFAULT half way through mapping */1716ASSERT_EQ(0, munmap(buf + buf_size / 2, buf_size / 2));1717test_err_ioctl_ioas_map(EFAULT, buf, buf_size, &iova);17181719/* EFAULT on first page */1720ASSERT_EQ(0, munmap(buf, buf_size / 2));1721test_err_ioctl_ioas_map(EFAULT, buf, buf_size, &iova);1722}17231724static void1725test_basic_file(struct __test_metadata *_metadata,1726struct _test_data_iommufd_mock_domain *self,1727const struct _fixture_variant_iommufd_mock_domain *variant)1728{1729size_t buf_size = self->mmap_buf_size;1730uint8_t *buf;1731__u64 iova;1732int mfd_tmp;1733int prot = PROT_READ | PROT_WRITE;17341735/* Simple one page map */1736test_ioctl_ioas_map_file(mfd, 0, PAGE_SIZE, &iova);1737check_mock_iova(mfd_buffer, iova, PAGE_SIZE);17381739buf = memfd_mmap(buf_size, prot, MAP_SHARED, &mfd_tmp);1740ASSERT_NE(MAP_FAILED, buf);17411742test_err_ioctl_ioas_map_file(EINVAL, mfd_tmp, 0, buf_size + 1, &iova);17431744ASSERT_EQ(0, ftruncate(mfd_tmp, 0));1745test_err_ioctl_ioas_map_file(EINVAL, mfd_tmp, 0, buf_size, &iova);17461747close(mfd_tmp);1748}17491750TEST_F(iommufd_mock_domain, basic)1751{1752if (variant->file)1753test_basic_file(_metadata, self, variant);1754else1755test_basic_mmap(_metadata, self, variant);1756}17571758TEST_F(iommufd_mock_domain, ro_unshare)1759{1760uint8_t *buf;1761__u64 iova;1762int fd;17631764fd = open("/proc/self/exe", O_RDONLY);1765ASSERT_NE(-1, fd);17661767buf = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);1768ASSERT_NE(MAP_FAILED, buf);1769close(fd);17701771/*1772* There have been lots of changes to the "unshare" mechanism in1773* get_user_pages(), make sure it works right. The write to the page1774* after we map it for reading should not change the assigned PFN.1775*/1776ASSERT_EQ(0,1777_test_ioctl_ioas_map(self->fd, self->ioas_id, buf, PAGE_SIZE,1778&iova, IOMMU_IOAS_MAP_READABLE));1779check_mock_iova(buf, iova, PAGE_SIZE);1780memset(buf, 1, PAGE_SIZE);1781check_mock_iova(buf, iova, PAGE_SIZE);1782ASSERT_EQ(0, munmap(buf, PAGE_SIZE));1783}17841785TEST_F(iommufd_mock_domain, all_aligns)1786{1787size_t test_step = variant->hugepages ? (self->mmap_buf_size / 16) :1788MOCK_PAGE_SIZE;1789size_t buf_size = self->mmap_buf_size;1790unsigned int start;1791unsigned int end;1792uint8_t *buf;1793int prot = PROT_READ | PROT_WRITE;1794int mfd = -1;17951796if (variant->file)1797buf = memfd_mmap(buf_size, prot, MAP_SHARED, &mfd);1798else1799buf = mmap(0, buf_size, prot, self->mmap_flags, -1, 0);1800ASSERT_NE(MAP_FAILED, buf);1801if (variant->file)1802ASSERT_GT(mfd, 0);1803check_refs(buf, buf_size, 0);18041805/*1806* Map every combination of page size and alignment within a big region,1807* less for hugepage case as it takes so long to finish.1808*/1809for (start = 0; start < buf_size; start += test_step) {1810if (variant->hugepages)1811end = buf_size;1812else1813end = start + MOCK_PAGE_SIZE;1814for (; end < buf_size; end += MOCK_PAGE_SIZE) {1815size_t length = end - start;1816__u64 iova;18171818if (variant->file) {1819test_ioctl_ioas_map_file(mfd, start, length,1820&iova);1821} else {1822test_ioctl_ioas_map(buf + start, length, &iova);1823}1824check_mock_iova(buf + start, iova, length);1825check_refs(buf + start / PAGE_SIZE * PAGE_SIZE,1826end / PAGE_SIZE * PAGE_SIZE -1827start / PAGE_SIZE * PAGE_SIZE,18281);18291830test_ioctl_ioas_unmap(iova, length);1831}1832}1833check_refs(buf, buf_size, 0);1834ASSERT_EQ(0, munmap(buf, buf_size));1835if (variant->file)1836close(mfd);1837}18381839TEST_F(iommufd_mock_domain, all_aligns_copy)1840{1841size_t test_step = variant->hugepages ? self->mmap_buf_size / 16 :1842MOCK_PAGE_SIZE;1843size_t buf_size = self->mmap_buf_size;1844unsigned int start;1845unsigned int end;1846uint8_t *buf;1847int prot = PROT_READ | PROT_WRITE;1848int mfd = -1;18491850if (variant->file)1851buf = memfd_mmap(buf_size, prot, MAP_SHARED, &mfd);1852else1853buf = mmap(0, buf_size, prot, self->mmap_flags, -1, 0);1854ASSERT_NE(MAP_FAILED, buf);1855if (variant->file)1856ASSERT_GT(mfd, 0);1857check_refs(buf, buf_size, 0);18581859/*1860* Map every combination of page size and alignment within a big region,1861* less for hugepage case as it takes so long to finish.1862*/1863for (start = 0; start < buf_size; start += test_step) {1864if (variant->hugepages)1865end = buf_size;1866else1867end = start + MOCK_PAGE_SIZE;1868for (; end < buf_size; end += MOCK_PAGE_SIZE) {1869size_t length = end - start;1870unsigned int old_id;1871uint32_t mock_stdev_id;1872__u64 iova;18731874if (variant->file) {1875test_ioctl_ioas_map_file(mfd, start, length,1876&iova);1877} else {1878test_ioctl_ioas_map(buf + start, length, &iova);1879}18801881/* Add and destroy a domain while the area exists */1882old_id = self->hwpt_ids[1];1883test_cmd_mock_domain(self->ioas_id, &mock_stdev_id,1884&self->hwpt_ids[1], NULL);18851886check_mock_iova(buf + start, iova, length);1887check_refs(buf + start / PAGE_SIZE * PAGE_SIZE,1888end / PAGE_SIZE * PAGE_SIZE -1889start / PAGE_SIZE * PAGE_SIZE,18901);18911892test_ioctl_destroy(mock_stdev_id);1893self->hwpt_ids[1] = old_id;18941895test_ioctl_ioas_unmap(iova, length);1896}1897}1898check_refs(buf, buf_size, 0);1899ASSERT_EQ(0, munmap(buf, buf_size));1900if (variant->file)1901close(mfd);1902}19031904TEST_F(iommufd_mock_domain, user_copy)1905{1906void *buf = variant->file ? mfd_buffer : buffer;1907struct iommu_test_cmd access_cmd = {1908.size = sizeof(access_cmd),1909.op = IOMMU_TEST_OP_ACCESS_PAGES,1910.access_pages = { .length = BUFFER_SIZE,1911.uptr = (uintptr_t)buf },1912};1913struct iommu_ioas_copy copy_cmd = {1914.size = sizeof(copy_cmd),1915.flags = IOMMU_IOAS_MAP_FIXED_IOVA | IOMMU_IOAS_MAP_WRITEABLE,1916.dst_ioas_id = self->ioas_id,1917.dst_iova = MOCK_APERTURE_START,1918.length = BUFFER_SIZE,1919};1920struct iommu_ioas_unmap unmap_cmd = {1921.size = sizeof(unmap_cmd),1922.ioas_id = self->ioas_id,1923.iova = MOCK_APERTURE_START,1924.length = BUFFER_SIZE,1925};1926unsigned int new_ioas_id, ioas_id;19271928/* Pin the pages in an IOAS with no domains then copy to an IOAS with domains */1929test_ioctl_ioas_alloc(&ioas_id);1930if (variant->file) {1931test_ioctl_ioas_map_id_file(ioas_id, mfd, 0, BUFFER_SIZE,1932©_cmd.src_iova);1933} else {1934test_ioctl_ioas_map_id(ioas_id, buf, BUFFER_SIZE,1935©_cmd.src_iova);1936}1937test_cmd_create_access(ioas_id, &access_cmd.id,1938MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES);19391940access_cmd.access_pages.iova = copy_cmd.src_iova;1941ASSERT_EQ(0,1942ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),1943&access_cmd));1944copy_cmd.src_ioas_id = ioas_id;1945ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_COPY, ©_cmd));1946check_mock_iova(buf, MOCK_APERTURE_START, BUFFER_SIZE);19471948/* Now replace the ioas with a new one */1949test_ioctl_ioas_alloc(&new_ioas_id);1950if (variant->file) {1951test_ioctl_ioas_map_id_file(new_ioas_id, mfd, 0, BUFFER_SIZE,1952©_cmd.src_iova);1953} else {1954test_ioctl_ioas_map_id(new_ioas_id, buf, BUFFER_SIZE,1955©_cmd.src_iova);1956}1957test_cmd_access_replace_ioas(access_cmd.id, new_ioas_id);19581959/* Destroy the old ioas and cleanup copied mapping */1960ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_UNMAP, &unmap_cmd));1961test_ioctl_destroy(ioas_id);19621963/* Then run the same test again with the new ioas */1964access_cmd.access_pages.iova = copy_cmd.src_iova;1965ASSERT_EQ(0,1966ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),1967&access_cmd));1968copy_cmd.src_ioas_id = new_ioas_id;1969ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_COPY, ©_cmd));1970check_mock_iova(buf, MOCK_APERTURE_START, BUFFER_SIZE);19711972test_cmd_destroy_access_pages(1973access_cmd.id, access_cmd.access_pages.out_access_pages_id);1974test_cmd_destroy_access(access_cmd.id);19751976test_ioctl_destroy(new_ioas_id);1977}19781979TEST_F(iommufd_mock_domain, replace)1980{1981uint32_t ioas_id;19821983test_ioctl_ioas_alloc(&ioas_id);19841985test_cmd_mock_domain_replace(self->stdev_ids[0], ioas_id);19861987/*1988* Replacing the IOAS causes the prior HWPT to be deallocated, thus we1989* should get enoent when we try to use it.1990*/1991if (variant->mock_domains == 1)1992test_err_mock_domain_replace(ENOENT, self->stdev_ids[0],1993self->hwpt_ids[0]);19941995test_cmd_mock_domain_replace(self->stdev_ids[0], ioas_id);1996if (variant->mock_domains >= 2) {1997test_cmd_mock_domain_replace(self->stdev_ids[0],1998self->hwpt_ids[1]);1999test_cmd_mock_domain_replace(self->stdev_ids[0],2000self->hwpt_ids[1]);2001test_cmd_mock_domain_replace(self->stdev_ids[0],2002self->hwpt_ids[0]);2003}20042005test_cmd_mock_domain_replace(self->stdev_ids[0], self->ioas_id);2006test_ioctl_destroy(ioas_id);2007}20082009TEST_F(iommufd_mock_domain, alloc_hwpt)2010{2011int i;20122013for (i = 0; i != variant->mock_domains; i++) {2014uint32_t hwpt_id[2];2015uint32_t stddev_id;20162017test_err_hwpt_alloc(EOPNOTSUPP,2018self->idev_ids[i], self->ioas_id,2019~IOMMU_HWPT_ALLOC_NEST_PARENT, &hwpt_id[0]);2020test_cmd_hwpt_alloc(self->idev_ids[i], self->ioas_id,20210, &hwpt_id[0]);2022test_cmd_hwpt_alloc(self->idev_ids[i], self->ioas_id,2023IOMMU_HWPT_ALLOC_NEST_PARENT, &hwpt_id[1]);20242025/* Do a hw_pagetable rotation test */2026test_cmd_mock_domain_replace(self->stdev_ids[i], hwpt_id[0]);2027EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, hwpt_id[0]));2028test_cmd_mock_domain_replace(self->stdev_ids[i], hwpt_id[1]);2029EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, hwpt_id[1]));2030test_cmd_mock_domain_replace(self->stdev_ids[i], self->ioas_id);2031test_ioctl_destroy(hwpt_id[1]);20322033test_cmd_mock_domain(hwpt_id[0], &stddev_id, NULL, NULL);2034test_ioctl_destroy(stddev_id);2035test_ioctl_destroy(hwpt_id[0]);2036}2037}20382039FIXTURE(iommufd_dirty_tracking)2040{2041int fd;2042uint32_t ioas_id;2043uint32_t hwpt_id;2044uint32_t stdev_id;2045uint32_t idev_id;2046unsigned long page_size;2047unsigned long bitmap_size;2048void *bitmap;2049void *buffer;2050};20512052FIXTURE_VARIANT(iommufd_dirty_tracking)2053{2054unsigned long buffer_size;2055bool hugepages;2056};20572058FIXTURE_SETUP(iommufd_dirty_tracking)2059{2060size_t mmap_buffer_size;2061unsigned long size;2062int mmap_flags;2063void *vrc;2064int rc;20652066if (variant->buffer_size < MOCK_PAGE_SIZE) {2067SKIP(return,2068"Skipping buffer_size=%lu, less than MOCK_PAGE_SIZE=%lu",2069variant->buffer_size, MOCK_PAGE_SIZE);2070}20712072self->fd = open("/dev/iommu", O_RDWR);2073ASSERT_NE(-1, self->fd);20742075mmap_flags = MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED;2076mmap_buffer_size = variant->buffer_size;2077if (variant->hugepages) {2078/*2079* MAP_POPULATE will cause the kernel to fail mmap if THPs are2080* not available.2081*/2082mmap_flags |= MAP_HUGETLB | MAP_POPULATE;20832084/*2085* Allocation must be aligned to the HUGEPAGE_SIZE, because the2086* following mmap() will automatically align the length to be a2087* multiple of the underlying huge page size. Failing to do the2088* same at this allocation will result in a memory overwrite by2089* the mmap().2090*/2091if (mmap_buffer_size < HUGEPAGE_SIZE)2092mmap_buffer_size = HUGEPAGE_SIZE;2093}20942095rc = posix_memalign(&self->buffer, HUGEPAGE_SIZE, mmap_buffer_size);2096if (rc || !self->buffer) {2097SKIP(return, "Skipping buffer_size=%lu due to errno=%d",2098mmap_buffer_size, rc);2099}2100assert((uintptr_t)self->buffer % HUGEPAGE_SIZE == 0);2101vrc = mmap(self->buffer, mmap_buffer_size, PROT_READ | PROT_WRITE,2102mmap_flags, -1, 0);2103assert(vrc == self->buffer);21042105self->page_size = MOCK_PAGE_SIZE;2106self->bitmap_size = variant->buffer_size / self->page_size;21072108/* Provision with an extra (PAGE_SIZE) for the unaligned case */2109size = DIV_ROUND_UP(self->bitmap_size, BITS_PER_BYTE);2110rc = posix_memalign(&self->bitmap, PAGE_SIZE, size + PAGE_SIZE);2111assert(!rc);2112assert(self->bitmap);2113assert((uintptr_t)self->bitmap % PAGE_SIZE == 0);21142115test_ioctl_ioas_alloc(&self->ioas_id);2116/* Enable 1M mock IOMMU hugepages */2117if (variant->hugepages) {2118test_cmd_mock_domain_flags(self->ioas_id,2119MOCK_FLAGS_DEVICE_HUGE_IOVA,2120&self->stdev_id, &self->hwpt_id,2121&self->idev_id);2122} else {2123test_cmd_mock_domain(self->ioas_id, &self->stdev_id,2124&self->hwpt_id, &self->idev_id);2125}2126}21272128FIXTURE_TEARDOWN(iommufd_dirty_tracking)2129{2130free(self->buffer);2131free(self->bitmap);2132teardown_iommufd(self->fd, _metadata);2133}21342135FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty8k)2136{2137/* half of an u8 index bitmap */2138.buffer_size = 8UL * 1024UL,2139};21402141FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty16k)2142{2143/* one u8 index bitmap */2144.buffer_size = 16UL * 1024UL,2145};21462147FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty64k)2148{2149/* one u32 index bitmap */2150.buffer_size = 64UL * 1024UL,2151};21522153FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128k)2154{2155/* one u64 index bitmap */2156.buffer_size = 128UL * 1024UL,2157};21582159FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty320k)2160{2161/* two u64 index and trailing end bitmap */2162.buffer_size = 320UL * 1024UL,2163};21642165FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty64M)2166{2167/* 4K bitmap (64M IOVA range) */2168.buffer_size = 64UL * 1024UL * 1024UL,2169};21702171FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty64M_huge)2172{2173/* 4K bitmap (64M IOVA range) */2174.buffer_size = 64UL * 1024UL * 1024UL,2175.hugepages = true,2176};21772178FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128M)2179{2180/* 8K bitmap (128M IOVA range) */2181.buffer_size = 128UL * 1024UL * 1024UL,2182};21832184FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128M_huge)2185{2186/* 8K bitmap (128M IOVA range) */2187.buffer_size = 128UL * 1024UL * 1024UL,2188.hugepages = true,2189};21902191TEST_F(iommufd_dirty_tracking, enforce_dirty)2192{2193uint32_t ioas_id, stddev_id, idev_id;2194uint32_t hwpt_id, _hwpt_id;2195uint32_t dev_flags;21962197/* Regular case */2198dev_flags = MOCK_FLAGS_DEVICE_NO_DIRTY;2199test_cmd_hwpt_alloc(self->idev_id, self->ioas_id,2200IOMMU_HWPT_ALLOC_DIRTY_TRACKING, &hwpt_id);2201test_cmd_mock_domain(hwpt_id, &stddev_id, NULL, NULL);2202test_err_mock_domain_flags(EINVAL, hwpt_id, dev_flags, &stddev_id,2203NULL);2204test_ioctl_destroy(stddev_id);2205test_ioctl_destroy(hwpt_id);22062207/* IOMMU device does not support dirty tracking */2208test_ioctl_ioas_alloc(&ioas_id);2209test_cmd_mock_domain_flags(ioas_id, dev_flags, &stddev_id, &_hwpt_id,2210&idev_id);2211test_err_hwpt_alloc(EOPNOTSUPP, idev_id, ioas_id,2212IOMMU_HWPT_ALLOC_DIRTY_TRACKING, &hwpt_id);2213test_ioctl_destroy(stddev_id);2214}22152216TEST_F(iommufd_dirty_tracking, set_dirty_tracking)2217{2218uint32_t stddev_id;2219uint32_t hwpt_id;22202221test_cmd_hwpt_alloc(self->idev_id, self->ioas_id,2222IOMMU_HWPT_ALLOC_DIRTY_TRACKING, &hwpt_id);2223test_cmd_mock_domain(hwpt_id, &stddev_id, NULL, NULL);2224test_cmd_set_dirty_tracking(hwpt_id, true);2225test_cmd_set_dirty_tracking(hwpt_id, false);22262227test_ioctl_destroy(stddev_id);2228test_ioctl_destroy(hwpt_id);2229}22302231TEST_F(iommufd_dirty_tracking, device_dirty_capability)2232{2233uint32_t caps = 0;2234uint32_t stddev_id;2235uint32_t hwpt_id;22362237test_cmd_hwpt_alloc(self->idev_id, self->ioas_id, 0, &hwpt_id);2238test_cmd_mock_domain(hwpt_id, &stddev_id, NULL, NULL);2239test_cmd_get_hw_capabilities(self->idev_id, caps);2240ASSERT_EQ(IOMMU_HW_CAP_DIRTY_TRACKING,2241caps & IOMMU_HW_CAP_DIRTY_TRACKING);22422243test_ioctl_destroy(stddev_id);2244test_ioctl_destroy(hwpt_id);2245}22462247TEST_F(iommufd_dirty_tracking, get_dirty_bitmap)2248{2249uint32_t page_size = MOCK_PAGE_SIZE;2250uint32_t hwpt_id;2251uint32_t ioas_id;22522253if (variant->hugepages)2254page_size = MOCK_HUGE_PAGE_SIZE;22552256test_ioctl_ioas_alloc(&ioas_id);2257test_ioctl_ioas_map_fixed_id(ioas_id, self->buffer,2258variant->buffer_size, MOCK_APERTURE_START);22592260test_cmd_hwpt_alloc(self->idev_id, ioas_id,2261IOMMU_HWPT_ALLOC_DIRTY_TRACKING, &hwpt_id);22622263test_cmd_set_dirty_tracking(hwpt_id, true);22642265test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,2266MOCK_APERTURE_START, self->page_size, page_size,2267self->bitmap, self->bitmap_size, 0, _metadata);22682269/* PAGE_SIZE unaligned bitmap */2270test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,2271MOCK_APERTURE_START, self->page_size, page_size,2272self->bitmap + MOCK_PAGE_SIZE,2273self->bitmap_size, 0, _metadata);22742275/* u64 unaligned bitmap */2276test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,2277MOCK_APERTURE_START, self->page_size, page_size,2278self->bitmap + 0xff1, self->bitmap_size, 0,2279_metadata);22802281test_ioctl_destroy(hwpt_id);2282}22832284TEST_F(iommufd_dirty_tracking, get_dirty_bitmap_no_clear)2285{2286uint32_t page_size = MOCK_PAGE_SIZE;2287uint32_t hwpt_id;2288uint32_t ioas_id;22892290if (variant->hugepages)2291page_size = MOCK_HUGE_PAGE_SIZE;22922293test_ioctl_ioas_alloc(&ioas_id);2294test_ioctl_ioas_map_fixed_id(ioas_id, self->buffer,2295variant->buffer_size, MOCK_APERTURE_START);22962297test_cmd_hwpt_alloc(self->idev_id, ioas_id,2298IOMMU_HWPT_ALLOC_DIRTY_TRACKING, &hwpt_id);22992300test_cmd_set_dirty_tracking(hwpt_id, true);23012302test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,2303MOCK_APERTURE_START, self->page_size, page_size,2304self->bitmap, self->bitmap_size,2305IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR,2306_metadata);23072308/* Unaligned bitmap */2309test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,2310MOCK_APERTURE_START, self->page_size, page_size,2311self->bitmap + MOCK_PAGE_SIZE,2312self->bitmap_size,2313IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR,2314_metadata);23152316/* u64 unaligned bitmap */2317test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,2318MOCK_APERTURE_START, self->page_size, page_size,2319self->bitmap + 0xff1, self->bitmap_size,2320IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR,2321_metadata);23222323test_ioctl_destroy(hwpt_id);2324}23252326/* VFIO compatibility IOCTLs */23272328TEST_F(iommufd, simple_ioctls)2329{2330ASSERT_EQ(VFIO_API_VERSION, ioctl(self->fd, VFIO_GET_API_VERSION));2331ASSERT_EQ(1, ioctl(self->fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU));2332}23332334TEST_F(iommufd, unmap_cmd)2335{2336struct vfio_iommu_type1_dma_unmap unmap_cmd = {2337.iova = MOCK_APERTURE_START,2338.size = PAGE_SIZE,2339};23402341unmap_cmd.argsz = 1;2342EXPECT_ERRNO(EINVAL, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));23432344unmap_cmd.argsz = sizeof(unmap_cmd);2345unmap_cmd.flags = 1 << 31;2346EXPECT_ERRNO(EINVAL, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));23472348unmap_cmd.flags = 0;2349EXPECT_ERRNO(ENODEV, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));2350}23512352TEST_F(iommufd, map_cmd)2353{2354struct vfio_iommu_type1_dma_map map_cmd = {2355.iova = MOCK_APERTURE_START,2356.size = PAGE_SIZE,2357.vaddr = (__u64)buffer,2358};23592360map_cmd.argsz = 1;2361EXPECT_ERRNO(EINVAL, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));23622363map_cmd.argsz = sizeof(map_cmd);2364map_cmd.flags = 1 << 31;2365EXPECT_ERRNO(EINVAL, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));23662367/* Requires a domain to be attached */2368map_cmd.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE;2369EXPECT_ERRNO(ENODEV, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));2370}23712372TEST_F(iommufd, info_cmd)2373{2374struct vfio_iommu_type1_info info_cmd = {};23752376/* Invalid argsz */2377info_cmd.argsz = 1;2378EXPECT_ERRNO(EINVAL, ioctl(self->fd, VFIO_IOMMU_GET_INFO, &info_cmd));23792380info_cmd.argsz = sizeof(info_cmd);2381EXPECT_ERRNO(ENODEV, ioctl(self->fd, VFIO_IOMMU_GET_INFO, &info_cmd));2382}23832384TEST_F(iommufd, set_iommu_cmd)2385{2386/* Requires a domain to be attached */2387EXPECT_ERRNO(ENODEV,2388ioctl(self->fd, VFIO_SET_IOMMU, VFIO_TYPE1v2_IOMMU));2389EXPECT_ERRNO(ENODEV, ioctl(self->fd, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU));2390}23912392TEST_F(iommufd, vfio_ioas)2393{2394struct iommu_vfio_ioas vfio_ioas_cmd = {2395.size = sizeof(vfio_ioas_cmd),2396.op = IOMMU_VFIO_IOAS_GET,2397};2398__u32 ioas_id;23992400/* ENODEV if there is no compat ioas */2401EXPECT_ERRNO(ENODEV, ioctl(self->fd, IOMMU_VFIO_IOAS, &vfio_ioas_cmd));24022403/* Invalid id for set */2404vfio_ioas_cmd.op = IOMMU_VFIO_IOAS_SET;2405EXPECT_ERRNO(ENOENT, ioctl(self->fd, IOMMU_VFIO_IOAS, &vfio_ioas_cmd));24062407/* Valid id for set*/2408test_ioctl_ioas_alloc(&ioas_id);2409vfio_ioas_cmd.ioas_id = ioas_id;2410ASSERT_EQ(0, ioctl(self->fd, IOMMU_VFIO_IOAS, &vfio_ioas_cmd));24112412/* Same id comes back from get */2413vfio_ioas_cmd.op = IOMMU_VFIO_IOAS_GET;2414ASSERT_EQ(0, ioctl(self->fd, IOMMU_VFIO_IOAS, &vfio_ioas_cmd));2415ASSERT_EQ(ioas_id, vfio_ioas_cmd.ioas_id);24162417/* Clear works */2418vfio_ioas_cmd.op = IOMMU_VFIO_IOAS_CLEAR;2419ASSERT_EQ(0, ioctl(self->fd, IOMMU_VFIO_IOAS, &vfio_ioas_cmd));2420vfio_ioas_cmd.op = IOMMU_VFIO_IOAS_GET;2421EXPECT_ERRNO(ENODEV, ioctl(self->fd, IOMMU_VFIO_IOAS, &vfio_ioas_cmd));2422}24232424FIXTURE(vfio_compat_mock_domain)2425{2426int fd;2427uint32_t ioas_id;2428};24292430FIXTURE_VARIANT(vfio_compat_mock_domain)2431{2432unsigned int version;2433};24342435FIXTURE_SETUP(vfio_compat_mock_domain)2436{2437struct iommu_vfio_ioas vfio_ioas_cmd = {2438.size = sizeof(vfio_ioas_cmd),2439.op = IOMMU_VFIO_IOAS_SET,2440};24412442self->fd = open("/dev/iommu", O_RDWR);2443ASSERT_NE(-1, self->fd);24442445/* Create what VFIO would consider a group */2446test_ioctl_ioas_alloc(&self->ioas_id);2447test_cmd_mock_domain(self->ioas_id, NULL, NULL, NULL);24482449/* Attach it to the vfio compat */2450vfio_ioas_cmd.ioas_id = self->ioas_id;2451ASSERT_EQ(0, ioctl(self->fd, IOMMU_VFIO_IOAS, &vfio_ioas_cmd));2452ASSERT_EQ(0, ioctl(self->fd, VFIO_SET_IOMMU, variant->version));2453}24542455FIXTURE_TEARDOWN(vfio_compat_mock_domain)2456{2457teardown_iommufd(self->fd, _metadata);2458}24592460FIXTURE_VARIANT_ADD(vfio_compat_mock_domain, Ver1v2)2461{2462.version = VFIO_TYPE1v2_IOMMU,2463};24642465FIXTURE_VARIANT_ADD(vfio_compat_mock_domain, Ver1v0)2466{2467.version = VFIO_TYPE1_IOMMU,2468};24692470TEST_F(vfio_compat_mock_domain, simple_close)2471{2472}24732474TEST_F(vfio_compat_mock_domain, option_huge_pages)2475{2476struct iommu_option cmd = {2477.size = sizeof(cmd),2478.option_id = IOMMU_OPTION_HUGE_PAGES,2479.op = IOMMU_OPTION_OP_GET,2480.val64 = 3,2481.object_id = self->ioas_id,2482};24832484ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));2485if (variant->version == VFIO_TYPE1_IOMMU) {2486ASSERT_EQ(0, cmd.val64);2487} else {2488ASSERT_EQ(1, cmd.val64);2489}2490}24912492/*2493* Execute an ioctl command stored in buffer and check that the result does not2494* overflow memory.2495*/2496static bool is_filled(const void *buf, uint8_t c, size_t len)2497{2498const uint8_t *cbuf = buf;24992500for (; len; cbuf++, len--)2501if (*cbuf != c)2502return false;2503return true;2504}25052506#define ioctl_check_buf(fd, cmd) \2507({ \2508size_t _cmd_len = *(__u32 *)buffer; \2509\2510memset(buffer + _cmd_len, 0xAA, BUFFER_SIZE - _cmd_len); \2511ASSERT_EQ(0, ioctl(fd, cmd, buffer)); \2512ASSERT_EQ(true, is_filled(buffer + _cmd_len, 0xAA, \2513BUFFER_SIZE - _cmd_len)); \2514})25152516static void check_vfio_info_cap_chain(struct __test_metadata *_metadata,2517struct vfio_iommu_type1_info *info_cmd)2518{2519const struct vfio_info_cap_header *cap;25202521ASSERT_GE(info_cmd->argsz, info_cmd->cap_offset + sizeof(*cap));2522cap = buffer + info_cmd->cap_offset;2523while (true) {2524size_t cap_size;25252526if (cap->next)2527cap_size = (buffer + cap->next) - (void *)cap;2528else2529cap_size = (buffer + info_cmd->argsz) - (void *)cap;25302531switch (cap->id) {2532case VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE: {2533struct vfio_iommu_type1_info_cap_iova_range *data =2534(void *)cap;25352536ASSERT_EQ(1, data->header.version);2537ASSERT_EQ(1, data->nr_iovas);2538EXPECT_EQ(MOCK_APERTURE_START,2539data->iova_ranges[0].start);2540EXPECT_EQ(MOCK_APERTURE_LAST, data->iova_ranges[0].end);2541break;2542}2543case VFIO_IOMMU_TYPE1_INFO_DMA_AVAIL: {2544struct vfio_iommu_type1_info_dma_avail *data =2545(void *)cap;25462547ASSERT_EQ(1, data->header.version);2548ASSERT_EQ(sizeof(*data), cap_size);2549break;2550}2551default:2552ASSERT_EQ(false, true);2553break;2554}2555if (!cap->next)2556break;25572558ASSERT_GE(info_cmd->argsz, cap->next + sizeof(*cap));2559ASSERT_GE(buffer + cap->next, (void *)cap);2560cap = buffer + cap->next;2561}2562}25632564TEST_F(vfio_compat_mock_domain, get_info)2565{2566struct vfio_iommu_type1_info *info_cmd = buffer;2567unsigned int i;2568size_t caplen;25692570/* Pre-cap ABI */2571*info_cmd = (struct vfio_iommu_type1_info){2572.argsz = offsetof(struct vfio_iommu_type1_info, cap_offset),2573};2574ioctl_check_buf(self->fd, VFIO_IOMMU_GET_INFO);2575ASSERT_NE(0, info_cmd->iova_pgsizes);2576ASSERT_EQ(VFIO_IOMMU_INFO_PGSIZES | VFIO_IOMMU_INFO_CAPS,2577info_cmd->flags);25782579/* Read the cap chain size */2580*info_cmd = (struct vfio_iommu_type1_info){2581.argsz = sizeof(*info_cmd),2582};2583ioctl_check_buf(self->fd, VFIO_IOMMU_GET_INFO);2584ASSERT_NE(0, info_cmd->iova_pgsizes);2585ASSERT_EQ(VFIO_IOMMU_INFO_PGSIZES | VFIO_IOMMU_INFO_CAPS,2586info_cmd->flags);2587ASSERT_EQ(0, info_cmd->cap_offset);2588ASSERT_LT(sizeof(*info_cmd), info_cmd->argsz);25892590/* Read the caps, kernel should never create a corrupted caps */2591caplen = info_cmd->argsz;2592for (i = sizeof(*info_cmd); i < caplen; i++) {2593*info_cmd = (struct vfio_iommu_type1_info){2594.argsz = i,2595};2596ioctl_check_buf(self->fd, VFIO_IOMMU_GET_INFO);2597ASSERT_EQ(VFIO_IOMMU_INFO_PGSIZES | VFIO_IOMMU_INFO_CAPS,2598info_cmd->flags);2599if (!info_cmd->cap_offset)2600continue;2601check_vfio_info_cap_chain(_metadata, info_cmd);2602}2603}26042605static void shuffle_array(unsigned long *array, size_t nelms)2606{2607unsigned int i;26082609/* Shuffle */2610for (i = 0; i != nelms; i++) {2611unsigned long tmp = array[i];2612unsigned int other = rand() % (nelms - i);26132614array[i] = array[other];2615array[other] = tmp;2616}2617}26182619TEST_F(vfio_compat_mock_domain, map)2620{2621struct vfio_iommu_type1_dma_map map_cmd = {2622.argsz = sizeof(map_cmd),2623.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE,2624.vaddr = (uintptr_t)buffer,2625.size = BUFFER_SIZE,2626.iova = MOCK_APERTURE_START,2627};2628struct vfio_iommu_type1_dma_unmap unmap_cmd = {2629.argsz = sizeof(unmap_cmd),2630.size = BUFFER_SIZE,2631.iova = MOCK_APERTURE_START,2632};2633unsigned long pages_iova[BUFFER_SIZE / PAGE_SIZE];2634unsigned int i;26352636/* Simple map/unmap */2637ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));2638ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));2639ASSERT_EQ(BUFFER_SIZE, unmap_cmd.size);26402641/* UNMAP_FLAG_ALL requires 0 iova/size */2642ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));2643unmap_cmd.flags = VFIO_DMA_UNMAP_FLAG_ALL;2644EXPECT_ERRNO(EINVAL, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));26452646unmap_cmd.iova = 0;2647unmap_cmd.size = 0;2648ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));2649ASSERT_EQ(BUFFER_SIZE, unmap_cmd.size);26502651/* Small pages */2652for (i = 0; i != ARRAY_SIZE(pages_iova); i++) {2653map_cmd.iova = pages_iova[i] =2654MOCK_APERTURE_START + i * PAGE_SIZE;2655map_cmd.vaddr = (uintptr_t)buffer + i * PAGE_SIZE;2656map_cmd.size = PAGE_SIZE;2657ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));2658}2659shuffle_array(pages_iova, ARRAY_SIZE(pages_iova));26602661unmap_cmd.flags = 0;2662unmap_cmd.size = PAGE_SIZE;2663for (i = 0; i != ARRAY_SIZE(pages_iova); i++) {2664unmap_cmd.iova = pages_iova[i];2665ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));2666}2667}26682669TEST_F(vfio_compat_mock_domain, huge_map)2670{2671size_t buf_size = HUGEPAGE_SIZE * 2;2672struct vfio_iommu_type1_dma_map map_cmd = {2673.argsz = sizeof(map_cmd),2674.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE,2675.size = buf_size,2676.iova = MOCK_APERTURE_START,2677};2678struct vfio_iommu_type1_dma_unmap unmap_cmd = {2679.argsz = sizeof(unmap_cmd),2680};2681unsigned long pages_iova[16];2682unsigned int i;2683void *buf;26842685/* Test huge pages and splitting */2686buf = mmap(0, buf_size, PROT_READ | PROT_WRITE,2687MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, -1,26880);2689ASSERT_NE(MAP_FAILED, buf);2690map_cmd.vaddr = (uintptr_t)buf;2691ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));26922693unmap_cmd.size = buf_size / ARRAY_SIZE(pages_iova);2694for (i = 0; i != ARRAY_SIZE(pages_iova); i++)2695pages_iova[i] = MOCK_APERTURE_START + (i * unmap_cmd.size);2696shuffle_array(pages_iova, ARRAY_SIZE(pages_iova));26972698/* type1 mode can cut up larger mappings, type1v2 always fails */2699for (i = 0; i != ARRAY_SIZE(pages_iova); i++) {2700unmap_cmd.iova = pages_iova[i];2701unmap_cmd.size = buf_size / ARRAY_SIZE(pages_iova);2702if (variant->version == VFIO_TYPE1_IOMMU) {2703ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA,2704&unmap_cmd));2705} else {2706EXPECT_ERRNO(ENOENT,2707ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA,2708&unmap_cmd));2709}2710}2711}27122713FIXTURE(iommufd_viommu)2714{2715int fd;2716uint32_t ioas_id;2717uint32_t stdev_id;2718uint32_t hwpt_id;2719uint32_t nested_hwpt_id;2720uint32_t device_id;2721uint32_t viommu_id;2722};27232724FIXTURE_VARIANT(iommufd_viommu)2725{2726unsigned int viommu;2727};27282729FIXTURE_SETUP(iommufd_viommu)2730{2731self->fd = open("/dev/iommu", O_RDWR);2732ASSERT_NE(-1, self->fd);2733test_ioctl_ioas_alloc(&self->ioas_id);2734test_ioctl_set_default_memory_limit();27352736if (variant->viommu) {2737struct iommu_hwpt_selftest data = {2738.iotlb = IOMMU_TEST_IOTLB_DEFAULT,2739};27402741test_cmd_mock_domain(self->ioas_id, &self->stdev_id, NULL,2742&self->device_id);27432744/* Allocate a nesting parent hwpt */2745test_cmd_hwpt_alloc(self->device_id, self->ioas_id,2746IOMMU_HWPT_ALLOC_NEST_PARENT,2747&self->hwpt_id);27482749/* Allocate a vIOMMU taking refcount of the parent hwpt */2750test_cmd_viommu_alloc(self->device_id, self->hwpt_id,2751IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0,2752&self->viommu_id);27532754/* Allocate a regular nested hwpt */2755test_cmd_hwpt_alloc_nested(self->device_id, self->viommu_id, 0,2756&self->nested_hwpt_id,2757IOMMU_HWPT_DATA_SELFTEST, &data,2758sizeof(data));2759}2760}27612762FIXTURE_TEARDOWN(iommufd_viommu)2763{2764teardown_iommufd(self->fd, _metadata);2765}27662767FIXTURE_VARIANT_ADD(iommufd_viommu, no_viommu)2768{2769.viommu = 0,2770};27712772FIXTURE_VARIANT_ADD(iommufd_viommu, mock_viommu)2773{2774.viommu = 1,2775};27762777TEST_F(iommufd_viommu, viommu_auto_destroy)2778{2779}27802781TEST_F(iommufd_viommu, viommu_negative_tests)2782{2783uint32_t device_id = self->device_id;2784uint32_t ioas_id = self->ioas_id;2785uint32_t hwpt_id;27862787if (self->device_id) {2788/* Negative test -- invalid hwpt (hwpt_id=0) */2789test_err_viommu_alloc(ENOENT, device_id, 0,2790IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0,2791NULL);27922793/* Negative test -- not a nesting parent hwpt */2794test_cmd_hwpt_alloc(device_id, ioas_id, 0, &hwpt_id);2795test_err_viommu_alloc(EINVAL, device_id, hwpt_id,2796IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0,2797NULL);2798test_ioctl_destroy(hwpt_id);27992800/* Negative test -- unsupported viommu type */2801test_err_viommu_alloc(EOPNOTSUPP, device_id, self->hwpt_id,28020xdead, NULL, 0, NULL);2803EXPECT_ERRNO(EBUSY,2804_test_ioctl_destroy(self->fd, self->hwpt_id));2805EXPECT_ERRNO(EBUSY,2806_test_ioctl_destroy(self->fd, self->viommu_id));2807} else {2808test_err_viommu_alloc(ENOENT, self->device_id, self->hwpt_id,2809IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0,2810NULL);2811}2812}28132814TEST_F(iommufd_viommu, viommu_alloc_nested_iopf)2815{2816struct iommu_hwpt_selftest data = {2817.iotlb = IOMMU_TEST_IOTLB_DEFAULT,2818};2819uint32_t viommu_id = self->viommu_id;2820uint32_t dev_id = self->device_id;2821uint32_t iopf_hwpt_id;2822uint32_t fault_id;2823uint32_t fault_fd;2824uint32_t vdev_id;28252826if (!dev_id)2827SKIP(return, "Skipping test for variant no_viommu");28282829test_ioctl_fault_alloc(&fault_id, &fault_fd);2830test_err_hwpt_alloc_iopf(ENOENT, dev_id, viommu_id, UINT32_MAX,2831IOMMU_HWPT_FAULT_ID_VALID, &iopf_hwpt_id,2832IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data));2833test_err_hwpt_alloc_iopf(EOPNOTSUPP, dev_id, viommu_id, fault_id,2834IOMMU_HWPT_FAULT_ID_VALID | (1 << 31),2835&iopf_hwpt_id, IOMMU_HWPT_DATA_SELFTEST, &data,2836sizeof(data));2837test_cmd_hwpt_alloc_iopf(dev_id, viommu_id, fault_id,2838IOMMU_HWPT_FAULT_ID_VALID, &iopf_hwpt_id,2839IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data));28402841/* Must allocate vdevice before attaching to a nested hwpt */2842test_err_mock_domain_replace(ENOENT, self->stdev_id, iopf_hwpt_id);2843test_cmd_vdevice_alloc(viommu_id, dev_id, 0x99, &vdev_id);2844test_cmd_mock_domain_replace(self->stdev_id, iopf_hwpt_id);2845EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, iopf_hwpt_id));2846test_cmd_trigger_iopf(dev_id, fault_fd);28472848test_cmd_mock_domain_replace(self->stdev_id, self->ioas_id);2849test_ioctl_destroy(iopf_hwpt_id);2850close(fault_fd);2851test_ioctl_destroy(fault_id);2852}28532854TEST_F(iommufd_viommu, viommu_alloc_with_data)2855{2856struct iommu_viommu_selftest data = {2857.in_data = 0xbeef,2858};2859uint32_t *test;28602861if (!self->device_id)2862SKIP(return, "Skipping test for variant no_viommu");28632864test_cmd_viommu_alloc(self->device_id, self->hwpt_id,2865IOMMU_VIOMMU_TYPE_SELFTEST, &data, sizeof(data),2866&self->viommu_id);2867ASSERT_EQ(data.out_data, data.in_data);28682869/* Negative mmap tests -- offset and length cannot be changed */2870test_err_mmap(ENXIO, data.out_mmap_length,2871data.out_mmap_offset + PAGE_SIZE);2872test_err_mmap(ENXIO, data.out_mmap_length,2873data.out_mmap_offset + PAGE_SIZE * 2);2874test_err_mmap(ENXIO, data.out_mmap_length / 2, data.out_mmap_offset);2875test_err_mmap(ENXIO, data.out_mmap_length * 2, data.out_mmap_offset);28762877/* Now do a correct mmap for a loopback test */2878test = mmap(NULL, data.out_mmap_length, PROT_READ | PROT_WRITE,2879MAP_SHARED, self->fd, data.out_mmap_offset);2880ASSERT_NE(MAP_FAILED, test);2881ASSERT_EQ(data.in_data, *test);28822883/* The owner of the mmap region should be blocked */2884EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, self->viommu_id));2885munmap(test, data.out_mmap_length);2886}28872888TEST_F(iommufd_viommu, vdevice_alloc)2889{2890uint32_t viommu_id = self->viommu_id;2891uint32_t dev_id = self->device_id;2892uint32_t vdev_id = 0;2893uint32_t veventq_id;2894uint32_t veventq_fd;2895int prev_seq = -1;28962897if (dev_id) {2898/* Must allocate vdevice before attaching to a nested hwpt */2899test_err_mock_domain_replace(ENOENT, self->stdev_id,2900self->nested_hwpt_id);29012902/* Allocate a vEVENTQ with veventq_depth=2 */2903test_cmd_veventq_alloc(viommu_id, IOMMU_VEVENTQ_TYPE_SELFTEST,2904&veventq_id, &veventq_fd);2905test_err_veventq_alloc(EEXIST, viommu_id,2906IOMMU_VEVENTQ_TYPE_SELFTEST, NULL, NULL);2907/* Set vdev_id to 0x99, unset it, and set to 0x88 */2908test_cmd_vdevice_alloc(viommu_id, dev_id, 0x99, &vdev_id);2909test_cmd_mock_domain_replace(self->stdev_id,2910self->nested_hwpt_id);2911test_cmd_trigger_vevents(dev_id, 1);2912test_cmd_read_vevents(veventq_fd, 1, 0x99, &prev_seq);2913test_err_vdevice_alloc(EEXIST, viommu_id, dev_id, 0x99,2914&vdev_id);2915test_cmd_mock_domain_replace(self->stdev_id, self->ioas_id);2916test_ioctl_destroy(vdev_id);29172918/* Try again with 0x88 */2919test_cmd_vdevice_alloc(viommu_id, dev_id, 0x88, &vdev_id);2920test_cmd_mock_domain_replace(self->stdev_id,2921self->nested_hwpt_id);2922/* Trigger an overflow with three events */2923test_cmd_trigger_vevents(dev_id, 3);2924test_err_read_vevents(EOVERFLOW, veventq_fd, 3, 0x88,2925&prev_seq);2926/* Overflow must be gone after the previous reads */2927test_cmd_trigger_vevents(dev_id, 1);2928test_cmd_read_vevents(veventq_fd, 1, 0x88, &prev_seq);2929close(veventq_fd);2930test_cmd_mock_domain_replace(self->stdev_id, self->ioas_id);2931test_ioctl_destroy(vdev_id);2932test_ioctl_destroy(veventq_id);2933} else {2934test_err_vdevice_alloc(ENOENT, viommu_id, dev_id, 0x99, NULL);2935}2936}29372938TEST_F(iommufd_viommu, vdevice_cache)2939{2940struct iommu_viommu_invalidate_selftest inv_reqs[2] = {};2941uint32_t viommu_id = self->viommu_id;2942uint32_t dev_id = self->device_id;2943uint32_t vdev_id = 0;2944uint32_t num_inv;29452946if (!dev_id)2947SKIP(return, "Skipping test for variant no_viommu");29482949test_cmd_vdevice_alloc(viommu_id, dev_id, 0x99, &vdev_id);29502951test_cmd_dev_check_cache_all(dev_id, IOMMU_TEST_DEV_CACHE_DEFAULT);29522953/* Check data_type by passing zero-length array */2954num_inv = 0;2955test_cmd_viommu_invalidate(viommu_id, inv_reqs, sizeof(*inv_reqs),2956&num_inv);2957assert(!num_inv);29582959/* Negative test: Invalid data_type */2960num_inv = 1;2961test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs,2962IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST_INVALID,2963sizeof(*inv_reqs), &num_inv);2964assert(!num_inv);29652966/* Negative test: structure size sanity */2967num_inv = 1;2968test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs,2969IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST,2970sizeof(*inv_reqs) + 1, &num_inv);2971assert(!num_inv);29722973num_inv = 1;2974test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs,2975IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, 1,2976&num_inv);2977assert(!num_inv);29782979/* Negative test: invalid flag is passed */2980num_inv = 1;2981inv_reqs[0].flags = 0xffffffff;2982inv_reqs[0].vdev_id = 0x99;2983test_err_viommu_invalidate(EOPNOTSUPP, viommu_id, inv_reqs,2984IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST,2985sizeof(*inv_reqs), &num_inv);2986assert(!num_inv);29872988/* Negative test: invalid data_uptr when array is not empty */2989num_inv = 1;2990inv_reqs[0].flags = 0;2991inv_reqs[0].vdev_id = 0x99;2992test_err_viommu_invalidate(EINVAL, viommu_id, NULL,2993IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST,2994sizeof(*inv_reqs), &num_inv);2995assert(!num_inv);29962997/* Negative test: invalid entry_len when array is not empty */2998num_inv = 1;2999inv_reqs[0].flags = 0;3000inv_reqs[0].vdev_id = 0x99;3001test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs,3002IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, 0,3003&num_inv);3004assert(!num_inv);30053006/* Negative test: invalid cache_id */3007num_inv = 1;3008inv_reqs[0].flags = 0;3009inv_reqs[0].vdev_id = 0x99;3010inv_reqs[0].cache_id = MOCK_DEV_CACHE_ID_MAX + 1;3011test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs,3012IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST,3013sizeof(*inv_reqs), &num_inv);3014assert(!num_inv);30153016/* Negative test: invalid vdev_id */3017num_inv = 1;3018inv_reqs[0].flags = 0;3019inv_reqs[0].vdev_id = 0x9;3020inv_reqs[0].cache_id = 0;3021test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs,3022IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST,3023sizeof(*inv_reqs), &num_inv);3024assert(!num_inv);30253026/*3027* Invalidate the 1st cache entry but fail the 2nd request3028* due to invalid flags configuration in the 2nd request.3029*/3030num_inv = 2;3031inv_reqs[0].flags = 0;3032inv_reqs[0].vdev_id = 0x99;3033inv_reqs[0].cache_id = 0;3034inv_reqs[1].flags = 0xffffffff;3035inv_reqs[1].vdev_id = 0x99;3036inv_reqs[1].cache_id = 1;3037test_err_viommu_invalidate(EOPNOTSUPP, viommu_id, inv_reqs,3038IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST,3039sizeof(*inv_reqs), &num_inv);3040assert(num_inv == 1);3041test_cmd_dev_check_cache(dev_id, 0, 0);3042test_cmd_dev_check_cache(dev_id, 1, IOMMU_TEST_DEV_CACHE_DEFAULT);3043test_cmd_dev_check_cache(dev_id, 2, IOMMU_TEST_DEV_CACHE_DEFAULT);3044test_cmd_dev_check_cache(dev_id, 3, IOMMU_TEST_DEV_CACHE_DEFAULT);30453046/*3047* Invalidate the 1st cache entry but fail the 2nd request3048* due to invalid cache_id configuration in the 2nd request.3049*/3050num_inv = 2;3051inv_reqs[0].flags = 0;3052inv_reqs[0].vdev_id = 0x99;3053inv_reqs[0].cache_id = 0;3054inv_reqs[1].flags = 0;3055inv_reqs[1].vdev_id = 0x99;3056inv_reqs[1].cache_id = MOCK_DEV_CACHE_ID_MAX + 1;3057test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs,3058IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST,3059sizeof(*inv_reqs), &num_inv);3060assert(num_inv == 1);3061test_cmd_dev_check_cache(dev_id, 0, 0);3062test_cmd_dev_check_cache(dev_id, 1, IOMMU_TEST_DEV_CACHE_DEFAULT);3063test_cmd_dev_check_cache(dev_id, 2, IOMMU_TEST_DEV_CACHE_DEFAULT);3064test_cmd_dev_check_cache(dev_id, 3, IOMMU_TEST_DEV_CACHE_DEFAULT);30653066/* Invalidate the 2nd cache entry and verify */3067num_inv = 1;3068inv_reqs[0].flags = 0;3069inv_reqs[0].vdev_id = 0x99;3070inv_reqs[0].cache_id = 1;3071test_cmd_viommu_invalidate(viommu_id, inv_reqs, sizeof(*inv_reqs),3072&num_inv);3073assert(num_inv == 1);3074test_cmd_dev_check_cache(dev_id, 0, 0);3075test_cmd_dev_check_cache(dev_id, 1, 0);3076test_cmd_dev_check_cache(dev_id, 2, IOMMU_TEST_DEV_CACHE_DEFAULT);3077test_cmd_dev_check_cache(dev_id, 3, IOMMU_TEST_DEV_CACHE_DEFAULT);30783079/* Invalidate the 3rd and 4th cache entries and verify */3080num_inv = 2;3081inv_reqs[0].flags = 0;3082inv_reqs[0].vdev_id = 0x99;3083inv_reqs[0].cache_id = 2;3084inv_reqs[1].flags = 0;3085inv_reqs[1].vdev_id = 0x99;3086inv_reqs[1].cache_id = 3;3087test_cmd_viommu_invalidate(viommu_id, inv_reqs, sizeof(*inv_reqs),3088&num_inv);3089assert(num_inv == 2);3090test_cmd_dev_check_cache_all(dev_id, 0);30913092/* Invalidate all cache entries for nested_dev_id[1] and verify */3093num_inv = 1;3094inv_reqs[0].vdev_id = 0x99;3095inv_reqs[0].flags = IOMMU_TEST_INVALIDATE_FLAG_ALL;3096test_cmd_viommu_invalidate(viommu_id, inv_reqs, sizeof(*inv_reqs),3097&num_inv);3098assert(num_inv == 1);3099test_cmd_dev_check_cache_all(dev_id, 0);3100test_ioctl_destroy(vdev_id);3101}31023103TEST_F(iommufd_viommu, hw_queue)3104{3105__u64 iova = MOCK_APERTURE_START, iova2;3106uint32_t viommu_id = self->viommu_id;3107uint32_t hw_queue_id[2];31083109if (!viommu_id)3110SKIP(return, "Skipping test for variant no_viommu");31113112/* Fail IOMMU_HW_QUEUE_TYPE_DEFAULT */3113test_err_hw_queue_alloc(EOPNOTSUPP, viommu_id,3114IOMMU_HW_QUEUE_TYPE_DEFAULT, 0, iova, PAGE_SIZE,3115&hw_queue_id[0]);3116/* Fail queue addr and length */3117test_err_hw_queue_alloc(EINVAL, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST,31180, iova, 0, &hw_queue_id[0]);3119test_err_hw_queue_alloc(EOVERFLOW, viommu_id,3120IOMMU_HW_QUEUE_TYPE_SELFTEST, 0, ~(uint64_t)0,3121PAGE_SIZE, &hw_queue_id[0]);3122/* Fail missing iova */3123test_err_hw_queue_alloc(ENOENT, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST,31240, iova, PAGE_SIZE, &hw_queue_id[0]);31253126/* Map iova */3127test_ioctl_ioas_map(buffer, PAGE_SIZE, &iova);3128test_ioctl_ioas_map(buffer + PAGE_SIZE, PAGE_SIZE, &iova2);31293130/* Fail index=1 and =MAX; must start from index=0 */3131test_err_hw_queue_alloc(EIO, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST, 1,3132iova, PAGE_SIZE, &hw_queue_id[0]);3133test_err_hw_queue_alloc(EINVAL, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST,3134IOMMU_TEST_HW_QUEUE_MAX, iova, PAGE_SIZE,3135&hw_queue_id[0]);31363137/* Allocate index=0, declare ownership of the iova */3138test_cmd_hw_queue_alloc(viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST, 0,3139iova, PAGE_SIZE, &hw_queue_id[0]);3140/* Fail duplicated index */3141test_err_hw_queue_alloc(EEXIST, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST,31420, iova, PAGE_SIZE, &hw_queue_id[0]);3143/* Fail unmap, due to iova ownership */3144test_err_ioctl_ioas_unmap(EBUSY, iova, PAGE_SIZE);3145/* The 2nd page is not pinned, so it can be unmmap */3146test_ioctl_ioas_unmap(iova2, PAGE_SIZE);31473148/* Allocate index=1, with an unaligned case */3149test_cmd_hw_queue_alloc(viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST, 1,3150iova + PAGE_SIZE / 2, PAGE_SIZE / 2,3151&hw_queue_id[1]);3152/* Fail to destroy, due to dependency */3153EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, hw_queue_id[0]));31543155/* Destroy in descending order */3156test_ioctl_destroy(hw_queue_id[1]);3157test_ioctl_destroy(hw_queue_id[0]);3158/* Now it can unmap the first page */3159test_ioctl_ioas_unmap(iova, PAGE_SIZE);3160}31613162TEST_F(iommufd_viommu, vdevice_tombstone)3163{3164uint32_t viommu_id = self->viommu_id;3165uint32_t dev_id = self->device_id;3166uint32_t vdev_id = 0;31673168if (!dev_id)3169SKIP(return, "Skipping test for variant no_viommu");31703171test_cmd_vdevice_alloc(viommu_id, dev_id, 0x99, &vdev_id);3172test_ioctl_destroy(self->stdev_id);3173EXPECT_ERRNO(ENOENT, _test_ioctl_destroy(self->fd, vdev_id));3174}31753176FIXTURE(iommufd_device_pasid)3177{3178int fd;3179uint32_t ioas_id;3180uint32_t hwpt_id;3181uint32_t stdev_id;3182uint32_t device_id;3183uint32_t no_pasid_stdev_id;3184uint32_t no_pasid_device_id;3185};31863187FIXTURE_VARIANT(iommufd_device_pasid)3188{3189bool pasid_capable;3190};31913192FIXTURE_SETUP(iommufd_device_pasid)3193{3194self->fd = open("/dev/iommu", O_RDWR);3195ASSERT_NE(-1, self->fd);3196test_ioctl_ioas_alloc(&self->ioas_id);31973198test_cmd_mock_domain_flags(self->ioas_id,3199MOCK_FLAGS_DEVICE_PASID,3200&self->stdev_id, &self->hwpt_id,3201&self->device_id);3202if (!variant->pasid_capable)3203test_cmd_mock_domain_flags(self->ioas_id, 0,3204&self->no_pasid_stdev_id, NULL,3205&self->no_pasid_device_id);3206}32073208FIXTURE_TEARDOWN(iommufd_device_pasid)3209{3210teardown_iommufd(self->fd, _metadata);3211}32123213FIXTURE_VARIANT_ADD(iommufd_device_pasid, no_pasid)3214{3215.pasid_capable = false,3216};32173218FIXTURE_VARIANT_ADD(iommufd_device_pasid, has_pasid)3219{3220.pasid_capable = true,3221};32223223TEST_F(iommufd_device_pasid, pasid_attach)3224{3225struct iommu_hwpt_selftest data = {3226.iotlb = IOMMU_TEST_IOTLB_DEFAULT,3227};3228uint32_t nested_hwpt_id[3] = {};3229uint32_t parent_hwpt_id = 0;3230uint32_t fault_id, fault_fd;3231uint32_t s2_hwpt_id = 0;3232uint32_t iopf_hwpt_id;3233uint32_t pasid = 100;3234uint32_t viommu_id;32353236/*3237* Negative, detach pasid without attaching, this is not expected.3238* But it should not result in failure anyway.3239*/3240test_cmd_pasid_detach(pasid);32413242/* Allocate two nested hwpts sharing one common parent hwpt */3243test_cmd_hwpt_alloc(self->device_id, self->ioas_id,3244IOMMU_HWPT_ALLOC_NEST_PARENT,3245&parent_hwpt_id);3246test_cmd_hwpt_alloc_nested(self->device_id, parent_hwpt_id,3247IOMMU_HWPT_ALLOC_PASID,3248&nested_hwpt_id[0],3249IOMMU_HWPT_DATA_SELFTEST,3250&data, sizeof(data));3251test_cmd_hwpt_alloc_nested(self->device_id, parent_hwpt_id,3252IOMMU_HWPT_ALLOC_PASID,3253&nested_hwpt_id[1],3254IOMMU_HWPT_DATA_SELFTEST,3255&data, sizeof(data));32563257/* Fault related preparation */3258test_ioctl_fault_alloc(&fault_id, &fault_fd);3259test_cmd_hwpt_alloc_iopf(self->device_id, parent_hwpt_id, fault_id,3260IOMMU_HWPT_FAULT_ID_VALID | IOMMU_HWPT_ALLOC_PASID,3261&iopf_hwpt_id,3262IOMMU_HWPT_DATA_SELFTEST, &data,3263sizeof(data));32643265/* Allocate a regular nested hwpt based on viommu */3266test_cmd_viommu_alloc(self->device_id, parent_hwpt_id,3267IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0, &viommu_id);3268test_cmd_hwpt_alloc_nested(self->device_id, viommu_id,3269IOMMU_HWPT_ALLOC_PASID,3270&nested_hwpt_id[2],3271IOMMU_HWPT_DATA_SELFTEST, &data,3272sizeof(data));32733274test_cmd_hwpt_alloc(self->device_id, self->ioas_id,3275IOMMU_HWPT_ALLOC_PASID,3276&s2_hwpt_id);32773278/* Attach RID to non-pasid compat domain, */3279test_cmd_mock_domain_replace(self->stdev_id, parent_hwpt_id);3280/* then attach to pasid should fail */3281test_err_pasid_attach(EINVAL, pasid, s2_hwpt_id);32823283/* Attach RID to pasid compat domain, */3284test_cmd_mock_domain_replace(self->stdev_id, s2_hwpt_id);3285/* then attach to pasid should succeed, */3286test_cmd_pasid_attach(pasid, nested_hwpt_id[0]);3287/* but attach RID to non-pasid compat domain should fail now. */3288test_err_mock_domain_replace(EINVAL, self->stdev_id, parent_hwpt_id);3289/*3290* Detach hwpt from pasid 100, and check if the pasid 1003291* has null domain.3292*/3293test_cmd_pasid_detach(pasid);3294ASSERT_EQ(0,3295test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3296pasid, 0));3297/* RID is attached to pasid-comapt domain, pasid path is not used */32983299if (!variant->pasid_capable) {3300/*3301* PASID-compatible domain can be used by non-PASID-capable3302* device.3303*/3304test_cmd_mock_domain_replace(self->no_pasid_stdev_id, nested_hwpt_id[0]);3305test_cmd_mock_domain_replace(self->no_pasid_stdev_id, self->ioas_id);3306/*3307* Attach hwpt to pasid 100 of non-PASID-capable device,3308* should fail, no matter domain is pasid-comapt or not.3309*/3310EXPECT_ERRNO(EINVAL,3311_test_cmd_pasid_attach(self->fd, self->no_pasid_stdev_id,3312pasid, parent_hwpt_id));3313EXPECT_ERRNO(EINVAL,3314_test_cmd_pasid_attach(self->fd, self->no_pasid_stdev_id,3315pasid, s2_hwpt_id));3316}33173318/*3319* Attach non pasid compat hwpt to pasid-capable device, should3320* fail, and have null domain.3321*/3322test_err_pasid_attach(EINVAL, pasid, parent_hwpt_id);3323ASSERT_EQ(0,3324test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3325pasid, 0));33263327/*3328* Attach ioas to pasid 100, should fail, domain should3329* be null.3330*/3331test_err_pasid_attach(EINVAL, pasid, self->ioas_id);3332ASSERT_EQ(0,3333test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3334pasid, 0));33353336/*3337* Attach the s2_hwpt to pasid 100, should succeed, domain should3338* be valid.3339*/3340test_cmd_pasid_attach(pasid, s2_hwpt_id);3341ASSERT_EQ(0,3342test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3343pasid, s2_hwpt_id));33443345/*3346* Try attach pasid 100 with another hwpt, should FAIL3347* as attach does not allow overwrite, use REPLACE instead.3348*/3349test_err_pasid_attach(EBUSY, pasid, nested_hwpt_id[0]);33503351/*3352* Detach hwpt from pasid 100 for next test, should succeed,3353* and have null domain.3354*/3355test_cmd_pasid_detach(pasid);3356ASSERT_EQ(0,3357test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3358pasid, 0));33593360/*3361* Attach nested hwpt to pasid 100, should succeed, domain3362* should be valid.3363*/3364test_cmd_pasid_attach(pasid, nested_hwpt_id[0]);3365ASSERT_EQ(0,3366test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3367pasid, nested_hwpt_id[0]));33683369/* Attach to pasid 100 which has been attached, should fail. */3370test_err_pasid_attach(EBUSY, pasid, nested_hwpt_id[0]);33713372/* cleanup pasid 100 */3373test_cmd_pasid_detach(pasid);33743375/* Replace tests */33763377pasid = 200;3378/*3379* Replace pasid 200 without attaching it, should fail3380* with -EINVAL.3381*/3382test_err_pasid_replace(EINVAL, pasid, s2_hwpt_id);33833384/*3385* Attach the s2 hwpt to pasid 200, should succeed, domain should3386* be valid.3387*/3388test_cmd_pasid_attach(pasid, s2_hwpt_id);3389ASSERT_EQ(0,3390test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3391pasid, s2_hwpt_id));33923393/*3394* Replace pasid 200 with self->ioas_id, should fail3395* and domain should be the prior s2 hwpt.3396*/3397test_err_pasid_replace(EINVAL, pasid, self->ioas_id);3398ASSERT_EQ(0,3399test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3400pasid, s2_hwpt_id));34013402/*3403* Replace a nested hwpt for pasid 200, should succeed,3404* and have valid domain.3405*/3406test_cmd_pasid_replace(pasid, nested_hwpt_id[0]);3407ASSERT_EQ(0,3408test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3409pasid, nested_hwpt_id[0]));34103411/*3412* Replace with another nested hwpt for pasid 200, should3413* succeed, and have valid domain.3414*/3415test_cmd_pasid_replace(pasid, nested_hwpt_id[1]);3416ASSERT_EQ(0,3417test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3418pasid, nested_hwpt_id[1]));34193420/* cleanup pasid 200 */3421test_cmd_pasid_detach(pasid);34223423/* Negative Tests for pasid replace, use pasid 1024 */34243425/*3426* Attach the s2 hwpt to pasid 1024, should succeed, domain should3427* be valid.3428*/3429pasid = 1024;3430test_cmd_pasid_attach(pasid, s2_hwpt_id);3431ASSERT_EQ(0,3432test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3433pasid, s2_hwpt_id));34343435/*3436* Replace pasid 1024 with nested_hwpt_id[0], should fail,3437* but have the old valid domain. This is a designed3438* negative case. Normally, this shall succeed.3439*/3440test_err_pasid_replace(ENOMEM, pasid, nested_hwpt_id[0]);3441ASSERT_EQ(0,3442test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3443pasid, s2_hwpt_id));34443445/* cleanup pasid 1024 */3446test_cmd_pasid_detach(pasid);34473448/* Attach to iopf-capable hwpt */34493450/*3451* Attach an iopf hwpt to pasid 2048, should succeed, domain should3452* be valid.3453*/3454pasid = 2048;3455test_cmd_pasid_attach(pasid, iopf_hwpt_id);3456ASSERT_EQ(0,3457test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3458pasid, iopf_hwpt_id));34593460test_cmd_trigger_iopf_pasid(self->device_id, pasid, fault_fd);34613462/*3463* Replace with s2_hwpt_id for pasid 2048, should3464* succeed, and have valid domain.3465*/3466test_cmd_pasid_replace(pasid, s2_hwpt_id);3467ASSERT_EQ(0,3468test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3469pasid, s2_hwpt_id));34703471/* cleanup pasid 2048 */3472test_cmd_pasid_detach(pasid);34733474test_ioctl_destroy(iopf_hwpt_id);3475close(fault_fd);3476test_ioctl_destroy(fault_id);34773478/* Detach the s2_hwpt_id from RID */3479test_cmd_mock_domain_replace(self->stdev_id, self->ioas_id);3480}34813482TEST_HARNESS_MAIN348334843485