Path: blob/master/tools/testing/selftests/iommu/iommufd.c
50905 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;1415static unsigned long get_huge_page_size(void)16{17char buf[80];18int ret;19int fd;2021fd = open("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size",22O_RDONLY);23if (fd < 0)24return 2 * 1024 * 1024;2526ret = read(fd, buf, sizeof(buf));27close(fd);28if (ret <= 0 || ret == sizeof(buf))29return 2 * 1024 * 1024;30buf[ret] = 0;31return strtoul(buf, NULL, 10);32}3334static __attribute__((constructor)) void setup_sizes(void)35{36void *vrc;37int rc;3839PAGE_SIZE = sysconf(_SC_PAGE_SIZE);40HUGEPAGE_SIZE = get_huge_page_size();4142BUFFER_SIZE = PAGE_SIZE * 16;43rc = posix_memalign(&buffer, HUGEPAGE_SIZE, BUFFER_SIZE);44assert(!rc);45assert(buffer);46assert((uintptr_t)buffer % HUGEPAGE_SIZE == 0);47vrc = mmap(buffer, BUFFER_SIZE, PROT_READ | PROT_WRITE,48MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0);49assert(vrc == buffer);5051mfd_buffer = memfd_mmap(BUFFER_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,52&mfd);53assert(mfd_buffer != MAP_FAILED);54assert(mfd > 0);55}5657FIXTURE(iommufd)58{59int fd;60};6162FIXTURE_SETUP(iommufd)63{64self->fd = open("/dev/iommu", O_RDWR);65ASSERT_NE(-1, self->fd);66}6768FIXTURE_TEARDOWN(iommufd)69{70teardown_iommufd(self->fd, _metadata);71}7273TEST_F(iommufd, simple_close)74{75}7677TEST_F(iommufd, cmd_fail)78{79struct iommu_destroy cmd = { .size = sizeof(cmd), .id = 0 };8081/* object id is invalid */82EXPECT_ERRNO(ENOENT, _test_ioctl_destroy(self->fd, 0));83/* Bad pointer */84EXPECT_ERRNO(EFAULT, ioctl(self->fd, IOMMU_DESTROY, NULL));85/* Unknown ioctl */86EXPECT_ERRNO(ENOTTY,87ioctl(self->fd, _IO(IOMMUFD_TYPE, IOMMUFD_CMD_BASE - 1),88&cmd));89}9091TEST_F(iommufd, cmd_length)92{93#define TEST_LENGTH(_struct, _ioctl, _last) \94{ \95size_t min_size = offsetofend(struct _struct, _last); \96struct { \97struct _struct cmd; \98uint8_t extra; \99} cmd = { .cmd = { .size = min_size - 1 }, \100.extra = UINT8_MAX }; \101int old_errno; \102int rc; \103\104EXPECT_ERRNO(EINVAL, ioctl(self->fd, _ioctl, &cmd)); \105cmd.cmd.size = sizeof(struct _struct) + 1; \106EXPECT_ERRNO(E2BIG, ioctl(self->fd, _ioctl, &cmd)); \107cmd.cmd.size = sizeof(struct _struct); \108rc = ioctl(self->fd, _ioctl, &cmd); \109old_errno = errno; \110cmd.cmd.size = sizeof(struct _struct) + 1; \111cmd.extra = 0; \112if (rc) { \113EXPECT_ERRNO(old_errno, \114ioctl(self->fd, _ioctl, &cmd)); \115} else { \116ASSERT_EQ(0, ioctl(self->fd, _ioctl, &cmd)); \117} \118}119120TEST_LENGTH(iommu_destroy, IOMMU_DESTROY, id);121TEST_LENGTH(iommu_hw_info, IOMMU_GET_HW_INFO, __reserved);122TEST_LENGTH(iommu_hwpt_alloc, IOMMU_HWPT_ALLOC, __reserved);123TEST_LENGTH(iommu_hwpt_invalidate, IOMMU_HWPT_INVALIDATE, __reserved);124TEST_LENGTH(iommu_ioas_alloc, IOMMU_IOAS_ALLOC, out_ioas_id);125TEST_LENGTH(iommu_ioas_iova_ranges, IOMMU_IOAS_IOVA_RANGES,126out_iova_alignment);127TEST_LENGTH(iommu_ioas_allow_iovas, IOMMU_IOAS_ALLOW_IOVAS,128allowed_iovas);129TEST_LENGTH(iommu_ioas_map, IOMMU_IOAS_MAP, iova);130TEST_LENGTH(iommu_ioas_copy, IOMMU_IOAS_COPY, src_iova);131TEST_LENGTH(iommu_ioas_unmap, IOMMU_IOAS_UNMAP, length);132TEST_LENGTH(iommu_option, IOMMU_OPTION, val64);133TEST_LENGTH(iommu_vfio_ioas, IOMMU_VFIO_IOAS, __reserved);134TEST_LENGTH(iommu_ioas_map_file, IOMMU_IOAS_MAP_FILE, iova);135TEST_LENGTH(iommu_viommu_alloc, IOMMU_VIOMMU_ALLOC, out_viommu_id);136TEST_LENGTH(iommu_vdevice_alloc, IOMMU_VDEVICE_ALLOC, virt_id);137TEST_LENGTH(iommu_ioas_change_process, IOMMU_IOAS_CHANGE_PROCESS,138__reserved);139#undef TEST_LENGTH140}141142TEST_F(iommufd, cmd_ex_fail)143{144struct {145struct iommu_destroy cmd;146__u64 future;147} cmd = { .cmd = { .size = sizeof(cmd), .id = 0 } };148149/* object id is invalid and command is longer */150EXPECT_ERRNO(ENOENT, ioctl(self->fd, IOMMU_DESTROY, &cmd));151/* future area is non-zero */152cmd.future = 1;153EXPECT_ERRNO(E2BIG, ioctl(self->fd, IOMMU_DESTROY, &cmd));154/* Original command "works" */155cmd.cmd.size = sizeof(cmd.cmd);156EXPECT_ERRNO(ENOENT, ioctl(self->fd, IOMMU_DESTROY, &cmd));157/* Short command fails */158cmd.cmd.size = sizeof(cmd.cmd) - 1;159EXPECT_ERRNO(EINVAL, ioctl(self->fd, IOMMU_DESTROY, &cmd));160}161162TEST_F(iommufd, global_options)163{164struct iommu_option cmd = {165.size = sizeof(cmd),166.option_id = IOMMU_OPTION_RLIMIT_MODE,167.op = IOMMU_OPTION_OP_GET,168.val64 = 1,169};170171cmd.option_id = IOMMU_OPTION_RLIMIT_MODE;172ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));173ASSERT_EQ(0, cmd.val64);174175/* This requires root */176cmd.op = IOMMU_OPTION_OP_SET;177cmd.val64 = 1;178ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));179cmd.val64 = 2;180EXPECT_ERRNO(EINVAL, ioctl(self->fd, IOMMU_OPTION, &cmd));181182cmd.op = IOMMU_OPTION_OP_GET;183ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));184ASSERT_EQ(1, cmd.val64);185186cmd.op = IOMMU_OPTION_OP_SET;187cmd.val64 = 0;188ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));189190cmd.op = IOMMU_OPTION_OP_GET;191cmd.option_id = IOMMU_OPTION_HUGE_PAGES;192EXPECT_ERRNO(ENOENT, ioctl(self->fd, IOMMU_OPTION, &cmd));193cmd.op = IOMMU_OPTION_OP_SET;194EXPECT_ERRNO(ENOENT, ioctl(self->fd, IOMMU_OPTION, &cmd));195}196197static void drop_cap_ipc_lock(struct __test_metadata *_metadata)198{199cap_t caps;200cap_value_t cap_list[1] = { CAP_IPC_LOCK };201202caps = cap_get_proc();203ASSERT_NE(caps, NULL);204ASSERT_NE(-1,205cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_CLEAR));206ASSERT_NE(-1, cap_set_proc(caps));207cap_free(caps);208}209210static long get_proc_status_value(pid_t pid, const char *var)211{212FILE *fp;213char buf[80], tag[80];214long val = -1;215216snprintf(buf, sizeof(buf), "/proc/%d/status", pid);217fp = fopen(buf, "r");218if (!fp)219return val;220221while (fgets(buf, sizeof(buf), fp))222if (fscanf(fp, "%s %ld\n", tag, &val) == 2 && !strcmp(tag, var))223break;224225fclose(fp);226return val;227}228229static long get_vm_pinned(pid_t pid)230{231return get_proc_status_value(pid, "VmPin:");232}233234static long get_vm_locked(pid_t pid)235{236return get_proc_status_value(pid, "VmLck:");237}238239FIXTURE(change_process)240{241int fd;242uint32_t ioas_id;243};244245FIXTURE_VARIANT(change_process)246{247int accounting;248};249250FIXTURE_SETUP(change_process)251{252self->fd = open("/dev/iommu", O_RDWR);253ASSERT_NE(-1, self->fd);254255drop_cap_ipc_lock(_metadata);256if (variant->accounting != IOPT_PAGES_ACCOUNT_NONE) {257struct iommu_option set_limit_cmd = {258.size = sizeof(set_limit_cmd),259.option_id = IOMMU_OPTION_RLIMIT_MODE,260.op = IOMMU_OPTION_OP_SET,261.val64 = (variant->accounting == IOPT_PAGES_ACCOUNT_MM),262};263ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &set_limit_cmd));264}265266test_ioctl_ioas_alloc(&self->ioas_id);267test_cmd_mock_domain(self->ioas_id, NULL, NULL, NULL);268}269270FIXTURE_TEARDOWN(change_process)271{272teardown_iommufd(self->fd, _metadata);273}274275FIXTURE_VARIANT_ADD(change_process, account_none)276{277.accounting = IOPT_PAGES_ACCOUNT_NONE,278};279280FIXTURE_VARIANT_ADD(change_process, account_user)281{282.accounting = IOPT_PAGES_ACCOUNT_USER,283};284285FIXTURE_VARIANT_ADD(change_process, account_mm)286{287.accounting = IOPT_PAGES_ACCOUNT_MM,288};289290TEST_F(change_process, basic)291{292pid_t parent = getpid();293pid_t child;294__u64 iova;295struct iommu_ioas_change_process cmd = {296.size = sizeof(cmd),297};298299/* Expect failure if non-file maps exist */300test_ioctl_ioas_map(buffer, PAGE_SIZE, &iova);301EXPECT_ERRNO(EINVAL, ioctl(self->fd, IOMMU_IOAS_CHANGE_PROCESS, &cmd));302test_ioctl_ioas_unmap(iova, PAGE_SIZE);303304/* Change process works in current process. */305test_ioctl_ioas_map_file(mfd, 0, PAGE_SIZE, &iova);306ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_CHANGE_PROCESS, &cmd));307308/* Change process works in another process */309child = fork();310if (!child) {311int nlock = PAGE_SIZE / 1024;312313/* Parent accounts for locked memory before */314ASSERT_EQ(nlock, get_vm_pinned(parent));315if (variant->accounting == IOPT_PAGES_ACCOUNT_MM)316ASSERT_EQ(nlock, get_vm_locked(parent));317ASSERT_EQ(0, get_vm_pinned(getpid()));318ASSERT_EQ(0, get_vm_locked(getpid()));319320ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_CHANGE_PROCESS, &cmd));321322/* Child accounts for locked memory after */323ASSERT_EQ(0, get_vm_pinned(parent));324ASSERT_EQ(0, get_vm_locked(parent));325ASSERT_EQ(nlock, get_vm_pinned(getpid()));326if (variant->accounting == IOPT_PAGES_ACCOUNT_MM)327ASSERT_EQ(nlock, get_vm_locked(getpid()));328329exit(0);330}331ASSERT_NE(-1, child);332ASSERT_EQ(child, waitpid(child, NULL, 0));333}334335FIXTURE(iommufd_ioas)336{337int fd;338uint32_t ioas_id;339uint32_t stdev_id;340uint32_t hwpt_id;341uint32_t device_id;342uint64_t base_iova;343uint32_t device_pasid_id;344};345346FIXTURE_VARIANT(iommufd_ioas)347{348unsigned int mock_domains;349unsigned int memory_limit;350bool pasid_capable;351};352353FIXTURE_SETUP(iommufd_ioas)354{355unsigned int i;356357358self->fd = open("/dev/iommu", O_RDWR);359ASSERT_NE(-1, self->fd);360test_ioctl_ioas_alloc(&self->ioas_id);361362if (!variant->memory_limit) {363test_ioctl_set_default_memory_limit();364} else {365test_ioctl_set_temp_memory_limit(variant->memory_limit);366}367368for (i = 0; i != variant->mock_domains; i++) {369test_cmd_mock_domain(self->ioas_id, &self->stdev_id,370&self->hwpt_id, &self->device_id);371test_cmd_dev_check_cache_all(self->device_id,372IOMMU_TEST_DEV_CACHE_DEFAULT);373self->base_iova = MOCK_APERTURE_START;374}375376if (variant->pasid_capable)377test_cmd_mock_domain_flags(self->ioas_id,378MOCK_FLAGS_DEVICE_PASID,379NULL, NULL,380&self->device_pasid_id);381}382383FIXTURE_TEARDOWN(iommufd_ioas)384{385test_ioctl_set_default_memory_limit();386teardown_iommufd(self->fd, _metadata);387}388389FIXTURE_VARIANT_ADD(iommufd_ioas, no_domain)390{391};392393FIXTURE_VARIANT_ADD(iommufd_ioas, mock_domain)394{395.mock_domains = 1,396.pasid_capable = true,397};398399FIXTURE_VARIANT_ADD(iommufd_ioas, two_mock_domain)400{401.mock_domains = 2,402};403404FIXTURE_VARIANT_ADD(iommufd_ioas, mock_domain_limit)405{406.mock_domains = 1,407.memory_limit = 16,408};409410TEST_F(iommufd_ioas, ioas_auto_destroy)411{412}413414TEST_F(iommufd_ioas, ioas_destroy)415{416if (self->stdev_id) {417/* IOAS cannot be freed while a device has a HWPT using it */418EXPECT_ERRNO(EBUSY,419_test_ioctl_destroy(self->fd, self->ioas_id));420} else {421/* Can allocate and manually free an IOAS table */422test_ioctl_destroy(self->ioas_id);423}424}425426TEST_F(iommufd_ioas, alloc_hwpt_nested)427{428const uint32_t min_data_len =429offsetofend(struct iommu_hwpt_selftest, iotlb);430struct iommu_hwpt_selftest data = {431.iotlb = IOMMU_TEST_IOTLB_DEFAULT,432};433struct iommu_hwpt_invalidate_selftest inv_reqs[2] = {};434uint32_t nested_hwpt_id[2] = {};435uint32_t num_inv;436uint32_t parent_hwpt_id = 0;437uint32_t parent_hwpt_id_not_work = 0;438uint32_t test_hwpt_id = 0;439uint32_t iopf_hwpt_id;440uint32_t fault_id;441uint32_t fault_fd;442443if (self->device_id) {444/* Negative tests */445test_err_hwpt_alloc(ENOENT, self->ioas_id, self->device_id, 0,446&test_hwpt_id);447test_err_hwpt_alloc(EINVAL, self->device_id, self->device_id, 0,448&test_hwpt_id);449test_err_hwpt_alloc(EOPNOTSUPP, self->device_id, self->ioas_id,450IOMMU_HWPT_ALLOC_NEST_PARENT |451IOMMU_HWPT_FAULT_ID_VALID,452&test_hwpt_id);453454test_cmd_hwpt_alloc(self->device_id, self->ioas_id,455IOMMU_HWPT_ALLOC_NEST_PARENT,456&parent_hwpt_id);457458test_cmd_hwpt_alloc(self->device_id, self->ioas_id, 0,459&parent_hwpt_id_not_work);460461/* Negative nested tests */462test_err_hwpt_alloc_nested(EINVAL, self->device_id,463parent_hwpt_id, 0,464&nested_hwpt_id[0],465IOMMU_HWPT_DATA_NONE, &data,466sizeof(data));467test_err_hwpt_alloc_nested(EOPNOTSUPP, self->device_id,468parent_hwpt_id, 0,469&nested_hwpt_id[0],470IOMMU_HWPT_DATA_SELFTEST + 1, &data,471sizeof(data));472test_err_hwpt_alloc_nested(EINVAL, self->device_id,473parent_hwpt_id, 0,474&nested_hwpt_id[0],475IOMMU_HWPT_DATA_SELFTEST, &data,476min_data_len - 1);477test_err_hwpt_alloc_nested(EFAULT, self->device_id,478parent_hwpt_id, 0,479&nested_hwpt_id[0],480IOMMU_HWPT_DATA_SELFTEST, NULL,481sizeof(data));482test_err_hwpt_alloc_nested(483EOPNOTSUPP, self->device_id, parent_hwpt_id,484IOMMU_HWPT_ALLOC_NEST_PARENT, &nested_hwpt_id[0],485IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data));486test_err_hwpt_alloc_nested(EINVAL, self->device_id,487parent_hwpt_id_not_work, 0,488&nested_hwpt_id[0],489IOMMU_HWPT_DATA_SELFTEST, &data,490sizeof(data));491492/* Allocate two nested hwpts sharing one common parent hwpt */493test_ioctl_fault_alloc(&fault_id, &fault_fd);494test_cmd_hwpt_alloc_nested(self->device_id, parent_hwpt_id, 0,495&nested_hwpt_id[0],496IOMMU_HWPT_DATA_SELFTEST, &data,497sizeof(data));498test_cmd_hwpt_alloc_nested(self->device_id, parent_hwpt_id, 0,499&nested_hwpt_id[1],500IOMMU_HWPT_DATA_SELFTEST, &data,501sizeof(data));502test_err_hwpt_alloc_iopf(ENOENT, self->device_id, parent_hwpt_id,503UINT32_MAX, IOMMU_HWPT_FAULT_ID_VALID,504&iopf_hwpt_id, IOMMU_HWPT_DATA_SELFTEST,505&data, sizeof(data));506test_cmd_hwpt_alloc_iopf(self->device_id, parent_hwpt_id, fault_id,507IOMMU_HWPT_FAULT_ID_VALID, &iopf_hwpt_id,508IOMMU_HWPT_DATA_SELFTEST, &data,509sizeof(data));510test_cmd_hwpt_check_iotlb_all(nested_hwpt_id[0],511IOMMU_TEST_IOTLB_DEFAULT);512test_cmd_hwpt_check_iotlb_all(nested_hwpt_id[1],513IOMMU_TEST_IOTLB_DEFAULT);514515/* Negative test: a nested hwpt on top of a nested hwpt */516test_err_hwpt_alloc_nested(EINVAL, self->device_id,517nested_hwpt_id[0], 0, &test_hwpt_id,518IOMMU_HWPT_DATA_SELFTEST, &data,519sizeof(data));520/* Negative test: parent hwpt now cannot be freed */521EXPECT_ERRNO(EBUSY,522_test_ioctl_destroy(self->fd, parent_hwpt_id));523524/* hwpt_invalidate does not support a parent hwpt */525num_inv = 1;526test_err_hwpt_invalidate(EINVAL, parent_hwpt_id, inv_reqs,527IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,528sizeof(*inv_reqs), &num_inv);529assert(!num_inv);530531/* Check data_type by passing zero-length array */532num_inv = 0;533test_cmd_hwpt_invalidate(nested_hwpt_id[0], inv_reqs,534IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,535sizeof(*inv_reqs), &num_inv);536assert(!num_inv);537538/* Negative test: Invalid data_type */539num_inv = 1;540test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs,541IOMMU_HWPT_INVALIDATE_DATA_SELFTEST_INVALID,542sizeof(*inv_reqs), &num_inv);543assert(!num_inv);544545/* Negative test: structure size sanity */546num_inv = 1;547test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs,548IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,549sizeof(*inv_reqs) + 1, &num_inv);550assert(!num_inv);551552num_inv = 1;553test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs,554IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,5551, &num_inv);556assert(!num_inv);557558/* Negative test: invalid flag is passed */559num_inv = 1;560inv_reqs[0].flags = 0xffffffff;561test_err_hwpt_invalidate(EOPNOTSUPP, nested_hwpt_id[0], inv_reqs,562IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,563sizeof(*inv_reqs), &num_inv);564assert(!num_inv);565566/* Negative test: invalid data_uptr when array is not empty */567num_inv = 1;568inv_reqs[0].flags = 0;569test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], NULL,570IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,571sizeof(*inv_reqs), &num_inv);572assert(!num_inv);573574/* Negative test: invalid entry_len when array is not empty */575num_inv = 1;576inv_reqs[0].flags = 0;577test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs,578IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,5790, &num_inv);580assert(!num_inv);581582/* Negative test: invalid iotlb_id */583num_inv = 1;584inv_reqs[0].flags = 0;585inv_reqs[0].iotlb_id = MOCK_NESTED_DOMAIN_IOTLB_ID_MAX + 1;586test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs,587IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,588sizeof(*inv_reqs), &num_inv);589assert(!num_inv);590591/*592* Invalidate the 1st iotlb entry but fail the 2nd request593* due to invalid flags configuration in the 2nd request.594*/595num_inv = 2;596inv_reqs[0].flags = 0;597inv_reqs[0].iotlb_id = 0;598inv_reqs[1].flags = 0xffffffff;599inv_reqs[1].iotlb_id = 1;600test_err_hwpt_invalidate(EOPNOTSUPP, nested_hwpt_id[0], inv_reqs,601IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,602sizeof(*inv_reqs), &num_inv);603assert(num_inv == 1);604test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 0, 0);605test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 1,606IOMMU_TEST_IOTLB_DEFAULT);607test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 2,608IOMMU_TEST_IOTLB_DEFAULT);609test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 3,610IOMMU_TEST_IOTLB_DEFAULT);611612/*613* Invalidate the 1st iotlb entry but fail the 2nd request614* due to invalid iotlb_id configuration in the 2nd request.615*/616num_inv = 2;617inv_reqs[0].flags = 0;618inv_reqs[0].iotlb_id = 0;619inv_reqs[1].flags = 0;620inv_reqs[1].iotlb_id = MOCK_NESTED_DOMAIN_IOTLB_ID_MAX + 1;621test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs,622IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,623sizeof(*inv_reqs), &num_inv);624assert(num_inv == 1);625test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 0, 0);626test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 1,627IOMMU_TEST_IOTLB_DEFAULT);628test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 2,629IOMMU_TEST_IOTLB_DEFAULT);630test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 3,631IOMMU_TEST_IOTLB_DEFAULT);632633/* Invalidate the 2nd iotlb entry and verify */634num_inv = 1;635inv_reqs[0].flags = 0;636inv_reqs[0].iotlb_id = 1;637test_cmd_hwpt_invalidate(nested_hwpt_id[0], inv_reqs,638IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,639sizeof(*inv_reqs), &num_inv);640assert(num_inv == 1);641test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 0, 0);642test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 1, 0);643test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 2,644IOMMU_TEST_IOTLB_DEFAULT);645test_cmd_hwpt_check_iotlb(nested_hwpt_id[0], 3,646IOMMU_TEST_IOTLB_DEFAULT);647648/* Invalidate the 3rd and 4th iotlb entries and verify */649num_inv = 2;650inv_reqs[0].flags = 0;651inv_reqs[0].iotlb_id = 2;652inv_reqs[1].flags = 0;653inv_reqs[1].iotlb_id = 3;654test_cmd_hwpt_invalidate(nested_hwpt_id[0], inv_reqs,655IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,656sizeof(*inv_reqs), &num_inv);657assert(num_inv == 2);658test_cmd_hwpt_check_iotlb_all(nested_hwpt_id[0], 0);659660/* Invalidate all iotlb entries for nested_hwpt_id[1] and verify */661num_inv = 1;662inv_reqs[0].flags = IOMMU_TEST_INVALIDATE_FLAG_ALL;663test_cmd_hwpt_invalidate(nested_hwpt_id[1], inv_reqs,664IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,665sizeof(*inv_reqs), &num_inv);666assert(num_inv == 1);667test_cmd_hwpt_check_iotlb_all(nested_hwpt_id[1], 0);668669/* Attach device to nested_hwpt_id[0] that then will be busy */670test_cmd_mock_domain_replace(self->stdev_id, nested_hwpt_id[0]);671EXPECT_ERRNO(EBUSY,672_test_ioctl_destroy(self->fd, nested_hwpt_id[0]));673674/* Switch from nested_hwpt_id[0] to nested_hwpt_id[1] */675test_cmd_mock_domain_replace(self->stdev_id, nested_hwpt_id[1]);676EXPECT_ERRNO(EBUSY,677_test_ioctl_destroy(self->fd, nested_hwpt_id[1]));678test_ioctl_destroy(nested_hwpt_id[0]);679680/* Switch from nested_hwpt_id[1] to iopf_hwpt_id */681test_cmd_mock_domain_replace(self->stdev_id, iopf_hwpt_id);682EXPECT_ERRNO(EBUSY,683_test_ioctl_destroy(self->fd, iopf_hwpt_id));684/* Trigger an IOPF on the device */685test_cmd_trigger_iopf(self->device_id, fault_fd);686687/* Detach from nested_hwpt_id[1] and destroy it */688test_cmd_mock_domain_replace(self->stdev_id, parent_hwpt_id);689test_ioctl_destroy(nested_hwpt_id[1]);690test_ioctl_destroy(iopf_hwpt_id);691692/* Detach from the parent hw_pagetable and destroy it */693test_cmd_mock_domain_replace(self->stdev_id, self->ioas_id);694test_ioctl_destroy(parent_hwpt_id);695test_ioctl_destroy(parent_hwpt_id_not_work);696close(fault_fd);697test_ioctl_destroy(fault_id);698} else {699test_err_hwpt_alloc(ENOENT, self->device_id, self->ioas_id, 0,700&parent_hwpt_id);701test_err_hwpt_alloc_nested(ENOENT, self->device_id,702parent_hwpt_id, 0,703&nested_hwpt_id[0],704IOMMU_HWPT_DATA_SELFTEST, &data,705sizeof(data));706test_err_hwpt_alloc_nested(ENOENT, self->device_id,707parent_hwpt_id, 0,708&nested_hwpt_id[1],709IOMMU_HWPT_DATA_SELFTEST, &data,710sizeof(data));711test_err_mock_domain_replace(ENOENT, self->stdev_id,712nested_hwpt_id[0]);713test_err_mock_domain_replace(ENOENT, self->stdev_id,714nested_hwpt_id[1]);715}716}717718TEST_F(iommufd_ioas, hwpt_attach)719{720/* Create a device attached directly to a hwpt */721if (self->stdev_id) {722test_cmd_mock_domain(self->hwpt_id, NULL, NULL, NULL);723} else {724test_err_mock_domain(ENOENT, self->hwpt_id, NULL, NULL);725}726}727728TEST_F(iommufd_ioas, ioas_area_destroy)729{730/* Adding an area does not change ability to destroy */731test_ioctl_ioas_map_fixed(buffer, PAGE_SIZE, self->base_iova);732if (self->stdev_id)733EXPECT_ERRNO(EBUSY,734_test_ioctl_destroy(self->fd, self->ioas_id));735else736test_ioctl_destroy(self->ioas_id);737}738739TEST_F(iommufd_ioas, ioas_area_auto_destroy)740{741int i;742743/* Can allocate and automatically free an IOAS table with many areas */744for (i = 0; i != 10; i++) {745test_ioctl_ioas_map_fixed(buffer, PAGE_SIZE,746self->base_iova + i * PAGE_SIZE);747}748}749750TEST_F(iommufd_ioas, get_hw_info)751{752struct iommu_test_hw_info buffer_exact;753struct iommu_test_hw_info_buffer_larger {754struct iommu_test_hw_info info;755uint64_t trailing_bytes;756} buffer_larger;757758if (self->device_id) {759uint8_t max_pasid = 0;760761/* Provide a zero-size user_buffer */762test_cmd_get_hw_info(self->device_id,763IOMMU_HW_INFO_TYPE_DEFAULT, NULL, 0);764/* Provide a user_buffer with exact size */765test_cmd_get_hw_info(self->device_id,766IOMMU_HW_INFO_TYPE_DEFAULT, &buffer_exact,767sizeof(buffer_exact));768769/* Request for a wrong data_type, and a correct one */770test_err_get_hw_info(EOPNOTSUPP, self->device_id,771IOMMU_HW_INFO_TYPE_SELFTEST + 1,772&buffer_exact, sizeof(buffer_exact));773test_cmd_get_hw_info(self->device_id,774IOMMU_HW_INFO_TYPE_SELFTEST, &buffer_exact,775sizeof(buffer_exact));776/*777* Provide a user_buffer with size larger than the exact size to check if778* kernel zero the trailing bytes.779*/780test_cmd_get_hw_info(self->device_id,781IOMMU_HW_INFO_TYPE_DEFAULT, &buffer_larger,782sizeof(buffer_larger));783/*784* Provide a user_buffer with size smaller than the exact size to check if785* the fields within the size range still gets updated.786*/787test_cmd_get_hw_info(self->device_id,788IOMMU_HW_INFO_TYPE_DEFAULT, &buffer_exact,789offsetofend(struct iommu_test_hw_info,790flags));791test_cmd_get_hw_info_pasid(self->device_id, &max_pasid);792ASSERT_EQ(0, max_pasid);793if (variant->pasid_capable) {794test_cmd_get_hw_info_pasid(self->device_pasid_id,795&max_pasid);796ASSERT_EQ(MOCK_PASID_WIDTH, max_pasid);797}798} else {799test_err_get_hw_info(ENOENT, self->device_id,800IOMMU_HW_INFO_TYPE_DEFAULT, &buffer_exact,801sizeof(buffer_exact));802test_err_get_hw_info(ENOENT, self->device_id,803IOMMU_HW_INFO_TYPE_DEFAULT, &buffer_larger,804sizeof(buffer_larger));805}806}807808TEST_F(iommufd_ioas, area)809{810int i;811812/* Unmap fails if nothing is mapped */813for (i = 0; i != 10; i++)814test_err_ioctl_ioas_unmap(ENOENT, i * PAGE_SIZE, PAGE_SIZE);815816/* Unmap works */817for (i = 0; i != 10; i++)818test_ioctl_ioas_map_fixed(buffer, PAGE_SIZE,819self->base_iova + i * PAGE_SIZE);820for (i = 0; i != 10; i++)821test_ioctl_ioas_unmap(self->base_iova + i * PAGE_SIZE,822PAGE_SIZE);823824/* Split fails */825test_ioctl_ioas_map_fixed(buffer, PAGE_SIZE * 2,826self->base_iova + 16 * PAGE_SIZE);827test_err_ioctl_ioas_unmap(ENOENT, self->base_iova + 16 * PAGE_SIZE,828PAGE_SIZE);829test_err_ioctl_ioas_unmap(ENOENT, self->base_iova + 17 * PAGE_SIZE,830PAGE_SIZE);831832/* Over map fails */833test_err_ioctl_ioas_map_fixed(EEXIST, buffer, PAGE_SIZE * 2,834self->base_iova + 16 * PAGE_SIZE);835test_err_ioctl_ioas_map_fixed(EEXIST, buffer, PAGE_SIZE,836self->base_iova + 16 * PAGE_SIZE);837test_err_ioctl_ioas_map_fixed(EEXIST, buffer, PAGE_SIZE,838self->base_iova + 17 * PAGE_SIZE);839test_err_ioctl_ioas_map_fixed(EEXIST, buffer, PAGE_SIZE * 2,840self->base_iova + 15 * PAGE_SIZE);841test_err_ioctl_ioas_map_fixed(EEXIST, buffer, PAGE_SIZE * 3,842self->base_iova + 15 * PAGE_SIZE);843844/* unmap all works */845test_ioctl_ioas_unmap(0, UINT64_MAX);846847/* Unmap all succeeds on an empty IOAS */848test_ioctl_ioas_unmap(0, UINT64_MAX);849}850851TEST_F(iommufd_ioas, unmap_fully_contained_areas)852{853uint64_t unmap_len;854int i;855856/* Give no_domain some space to rewind base_iova */857self->base_iova += 4 * PAGE_SIZE;858859for (i = 0; i != 4; i++)860test_ioctl_ioas_map_fixed(buffer, 8 * PAGE_SIZE,861self->base_iova + i * 16 * PAGE_SIZE);862863/* Unmap not fully contained area doesn't work */864test_err_ioctl_ioas_unmap(ENOENT, self->base_iova - 4 * PAGE_SIZE,8658 * PAGE_SIZE);866test_err_ioctl_ioas_unmap(ENOENT,867self->base_iova + 3 * 16 * PAGE_SIZE +8688 * PAGE_SIZE - 4 * PAGE_SIZE,8698 * PAGE_SIZE);870871/* Unmap fully contained areas works */872ASSERT_EQ(0, _test_ioctl_ioas_unmap(self->fd, self->ioas_id,873self->base_iova - 4 * PAGE_SIZE,8743 * 16 * PAGE_SIZE + 8 * PAGE_SIZE +8754 * PAGE_SIZE,876&unmap_len));877ASSERT_EQ(32 * PAGE_SIZE, unmap_len);878}879880TEST_F(iommufd_ioas, area_auto_iova)881{882struct iommu_test_cmd test_cmd = {883.size = sizeof(test_cmd),884.op = IOMMU_TEST_OP_ADD_RESERVED,885.id = self->ioas_id,886.add_reserved = { .start = PAGE_SIZE * 4,887.length = PAGE_SIZE * 100 },888};889struct iommu_iova_range ranges[1] = {};890struct iommu_ioas_allow_iovas allow_cmd = {891.size = sizeof(allow_cmd),892.ioas_id = self->ioas_id,893.num_iovas = 1,894.allowed_iovas = (uintptr_t)ranges,895};896__u64 iovas[10];897int i;898899/* Simple 4k pages */900for (i = 0; i != 10; i++)901test_ioctl_ioas_map(buffer, PAGE_SIZE, &iovas[i]);902for (i = 0; i != 10; i++)903test_ioctl_ioas_unmap(iovas[i], PAGE_SIZE);904905/* Kernel automatically aligns IOVAs properly */906for (i = 0; i != 10; i++) {907size_t length = PAGE_SIZE * (i + 1);908909if (self->stdev_id) {910test_ioctl_ioas_map(buffer, length, &iovas[i]);911} else {912test_ioctl_ioas_map((void *)(1UL << 31), length,913&iovas[i]);914}915EXPECT_EQ(0, iovas[i] % (1UL << (ffs(length) - 1)));916}917for (i = 0; i != 10; i++)918test_ioctl_ioas_unmap(iovas[i], PAGE_SIZE * (i + 1));919920/* Avoids a reserved region */921ASSERT_EQ(0,922ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ADD_RESERVED),923&test_cmd));924for (i = 0; i != 10; i++) {925size_t length = PAGE_SIZE * (i + 1);926927test_ioctl_ioas_map(buffer, length, &iovas[i]);928EXPECT_EQ(0, iovas[i] % (1UL << (ffs(length) - 1)));929EXPECT_EQ(false,930iovas[i] > test_cmd.add_reserved.start &&931iovas[i] <932test_cmd.add_reserved.start +933test_cmd.add_reserved.length);934}935for (i = 0; i != 10; i++)936test_ioctl_ioas_unmap(iovas[i], PAGE_SIZE * (i + 1));937938/* Allowed region intersects with a reserved region */939ranges[0].start = PAGE_SIZE;940ranges[0].last = PAGE_SIZE * 600;941EXPECT_ERRNO(EADDRINUSE,942ioctl(self->fd, IOMMU_IOAS_ALLOW_IOVAS, &allow_cmd));943944/* Allocate from an allowed region */945if (self->stdev_id) {946ranges[0].start = MOCK_APERTURE_START + PAGE_SIZE;947ranges[0].last = MOCK_APERTURE_START + PAGE_SIZE * 600 - 1;948} else {949ranges[0].start = PAGE_SIZE * 200;950ranges[0].last = PAGE_SIZE * 600 - 1;951}952ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_ALLOW_IOVAS, &allow_cmd));953for (i = 0; i != 10; i++) {954size_t length = PAGE_SIZE * (i + 1);955956test_ioctl_ioas_map(buffer, length, &iovas[i]);957EXPECT_EQ(0, iovas[i] % (1UL << (ffs(length) - 1)));958EXPECT_EQ(true, iovas[i] >= ranges[0].start);959EXPECT_EQ(true, iovas[i] <= ranges[0].last);960EXPECT_EQ(true, iovas[i] + length > ranges[0].start);961EXPECT_EQ(true, iovas[i] + length <= ranges[0].last + 1);962}963for (i = 0; i != 10; i++)964test_ioctl_ioas_unmap(iovas[i], PAGE_SIZE * (i + 1));965}966967/* https://lore.kernel.org/r/[email protected] */968TEST_F(iommufd_ioas, reserved_overflow)969{970struct iommu_test_cmd test_cmd = {971.size = sizeof(test_cmd),972.op = IOMMU_TEST_OP_ADD_RESERVED,973.id = self->ioas_id,974.add_reserved.start = 6,975};976unsigned int map_len;977__u64 iova;978979if (PAGE_SIZE == 4096) {980test_cmd.add_reserved.length = 0xffffffffffff8001;981map_len = 0x5000;982} else {983test_cmd.add_reserved.length =9840xffffffffffffffff - MOCK_PAGE_SIZE * 16;985map_len = MOCK_PAGE_SIZE * 10;986}987988ASSERT_EQ(0,989ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ADD_RESERVED),990&test_cmd));991test_err_ioctl_ioas_map(ENOSPC, buffer, map_len, &iova);992}993994TEST_F(iommufd_ioas, area_allowed)995{996struct iommu_test_cmd test_cmd = {997.size = sizeof(test_cmd),998.op = IOMMU_TEST_OP_ADD_RESERVED,999.id = self->ioas_id,1000.add_reserved = { .start = PAGE_SIZE * 4,1001.length = PAGE_SIZE * 100 },1002};1003struct iommu_iova_range ranges[1] = {};1004struct iommu_ioas_allow_iovas allow_cmd = {1005.size = sizeof(allow_cmd),1006.ioas_id = self->ioas_id,1007.num_iovas = 1,1008.allowed_iovas = (uintptr_t)ranges,1009};10101011/* Reserved intersects an allowed */1012allow_cmd.num_iovas = 1;1013ranges[0].start = self->base_iova;1014ranges[0].last = ranges[0].start + PAGE_SIZE * 600;1015ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_ALLOW_IOVAS, &allow_cmd));1016test_cmd.add_reserved.start = ranges[0].start + PAGE_SIZE;1017test_cmd.add_reserved.length = PAGE_SIZE;1018EXPECT_ERRNO(EADDRINUSE,1019ioctl(self->fd,1020_IOMMU_TEST_CMD(IOMMU_TEST_OP_ADD_RESERVED),1021&test_cmd));1022allow_cmd.num_iovas = 0;1023ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_ALLOW_IOVAS, &allow_cmd));10241025/* Allowed intersects a reserved */1026ASSERT_EQ(0,1027ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ADD_RESERVED),1028&test_cmd));1029allow_cmd.num_iovas = 1;1030ranges[0].start = self->base_iova;1031ranges[0].last = ranges[0].start + PAGE_SIZE * 600;1032EXPECT_ERRNO(EADDRINUSE,1033ioctl(self->fd, IOMMU_IOAS_ALLOW_IOVAS, &allow_cmd));1034}10351036TEST_F(iommufd_ioas, copy_area)1037{1038struct iommu_ioas_copy copy_cmd = {1039.size = sizeof(copy_cmd),1040.flags = IOMMU_IOAS_MAP_FIXED_IOVA | IOMMU_IOAS_MAP_WRITEABLE,1041.dst_ioas_id = self->ioas_id,1042.src_ioas_id = self->ioas_id,1043.length = PAGE_SIZE,1044};10451046test_ioctl_ioas_map_fixed(buffer, PAGE_SIZE, self->base_iova);10471048/* Copy inside a single IOAS */1049copy_cmd.src_iova = self->base_iova;1050copy_cmd.dst_iova = self->base_iova + PAGE_SIZE;1051ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_COPY, ©_cmd));10521053/* Copy between IOAS's */1054copy_cmd.src_iova = self->base_iova;1055copy_cmd.dst_iova = 0;1056test_ioctl_ioas_alloc(©_cmd.dst_ioas_id);1057ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_COPY, ©_cmd));1058}10591060TEST_F(iommufd_ioas, iova_ranges)1061{1062struct iommu_test_cmd test_cmd = {1063.size = sizeof(test_cmd),1064.op = IOMMU_TEST_OP_ADD_RESERVED,1065.id = self->ioas_id,1066.add_reserved = { .start = PAGE_SIZE, .length = PAGE_SIZE },1067};1068struct iommu_iova_range *ranges = buffer;1069struct iommu_ioas_iova_ranges ranges_cmd = {1070.size = sizeof(ranges_cmd),1071.ioas_id = self->ioas_id,1072.num_iovas = BUFFER_SIZE / sizeof(*ranges),1073.allowed_iovas = (uintptr_t)ranges,1074};10751076/* Range can be read */1077ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_IOVA_RANGES, &ranges_cmd));1078EXPECT_EQ(1, ranges_cmd.num_iovas);1079if (!self->stdev_id) {1080EXPECT_EQ(0, ranges[0].start);1081EXPECT_EQ(SIZE_MAX, ranges[0].last);1082EXPECT_EQ(1, ranges_cmd.out_iova_alignment);1083} else {1084EXPECT_EQ(MOCK_APERTURE_START, ranges[0].start);1085EXPECT_EQ(MOCK_APERTURE_LAST, ranges[0].last);1086EXPECT_EQ(MOCK_PAGE_SIZE, ranges_cmd.out_iova_alignment);1087}10881089/* Buffer too small */1090memset(ranges, 0, BUFFER_SIZE);1091ranges_cmd.num_iovas = 0;1092EXPECT_ERRNO(EMSGSIZE,1093ioctl(self->fd, IOMMU_IOAS_IOVA_RANGES, &ranges_cmd));1094EXPECT_EQ(1, ranges_cmd.num_iovas);1095EXPECT_EQ(0, ranges[0].start);1096EXPECT_EQ(0, ranges[0].last);10971098/* 2 ranges */1099ASSERT_EQ(0,1100ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ADD_RESERVED),1101&test_cmd));1102ranges_cmd.num_iovas = BUFFER_SIZE / sizeof(*ranges);1103ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_IOVA_RANGES, &ranges_cmd));1104if (!self->stdev_id) {1105EXPECT_EQ(2, ranges_cmd.num_iovas);1106EXPECT_EQ(0, ranges[0].start);1107EXPECT_EQ(PAGE_SIZE - 1, ranges[0].last);1108EXPECT_EQ(PAGE_SIZE * 2, ranges[1].start);1109EXPECT_EQ(SIZE_MAX, ranges[1].last);1110} else {1111EXPECT_EQ(1, ranges_cmd.num_iovas);1112EXPECT_EQ(MOCK_APERTURE_START, ranges[0].start);1113EXPECT_EQ(MOCK_APERTURE_LAST, ranges[0].last);1114}11151116/* Buffer too small */1117memset(ranges, 0, BUFFER_SIZE);1118ranges_cmd.num_iovas = 1;1119if (!self->stdev_id) {1120EXPECT_ERRNO(EMSGSIZE, ioctl(self->fd, IOMMU_IOAS_IOVA_RANGES,1121&ranges_cmd));1122EXPECT_EQ(2, ranges_cmd.num_iovas);1123EXPECT_EQ(0, ranges[0].start);1124EXPECT_EQ(PAGE_SIZE - 1, ranges[0].last);1125} else {1126ASSERT_EQ(0,1127ioctl(self->fd, IOMMU_IOAS_IOVA_RANGES, &ranges_cmd));1128EXPECT_EQ(1, ranges_cmd.num_iovas);1129EXPECT_EQ(MOCK_APERTURE_START, ranges[0].start);1130EXPECT_EQ(MOCK_APERTURE_LAST, ranges[0].last);1131}1132EXPECT_EQ(0, ranges[1].start);1133EXPECT_EQ(0, ranges[1].last);1134}11351136TEST_F(iommufd_ioas, access_domain_destory)1137{1138struct iommu_test_cmd access_cmd = {1139.size = sizeof(access_cmd),1140.op = IOMMU_TEST_OP_ACCESS_PAGES,1141.access_pages = { .iova = self->base_iova + PAGE_SIZE,1142.length = PAGE_SIZE},1143};1144size_t buf_size = 2 * HUGEPAGE_SIZE;1145uint8_t *buf;11461147buf = mmap(0, buf_size, PROT_READ | PROT_WRITE,1148MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, -1,11490);1150ASSERT_NE(MAP_FAILED, buf);1151test_ioctl_ioas_map_fixed(buf, buf_size, self->base_iova);11521153test_cmd_create_access(self->ioas_id, &access_cmd.id,1154MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES);1155access_cmd.access_pages.uptr = (uintptr_t)buf + PAGE_SIZE;1156ASSERT_EQ(0,1157ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),1158&access_cmd));11591160/* Causes a complicated unpin across a huge page boundary */1161if (self->stdev_id)1162test_ioctl_destroy(self->stdev_id);11631164test_cmd_destroy_access_pages(1165access_cmd.id, access_cmd.access_pages.out_access_pages_id);1166test_cmd_destroy_access(access_cmd.id);1167ASSERT_EQ(0, munmap(buf, buf_size));1168}11691170TEST_F(iommufd_ioas, access_pin)1171{1172struct iommu_test_cmd access_cmd = {1173.size = sizeof(access_cmd),1174.op = IOMMU_TEST_OP_ACCESS_PAGES,1175.access_pages = { .iova = MOCK_APERTURE_START,1176.length = BUFFER_SIZE,1177.uptr = (uintptr_t)buffer },1178};1179struct iommu_test_cmd check_map_cmd = {1180.size = sizeof(check_map_cmd),1181.op = IOMMU_TEST_OP_MD_CHECK_MAP,1182.check_map = { .iova = MOCK_APERTURE_START,1183.length = BUFFER_SIZE,1184.uptr = (uintptr_t)buffer },1185};1186uint32_t access_pages_id;1187unsigned int npages;11881189test_cmd_create_access(self->ioas_id, &access_cmd.id,1190MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES);11911192for (npages = 1; npages < BUFFER_SIZE / PAGE_SIZE; npages++) {1193uint32_t mock_stdev_id;1194uint32_t mock_hwpt_id;11951196access_cmd.access_pages.length = npages * PAGE_SIZE;11971198/* Single map/unmap */1199test_ioctl_ioas_map_fixed(buffer, BUFFER_SIZE,1200MOCK_APERTURE_START);1201ASSERT_EQ(0, ioctl(self->fd,1202_IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),1203&access_cmd));1204test_cmd_destroy_access_pages(1205access_cmd.id,1206access_cmd.access_pages.out_access_pages_id);12071208/* Double user */1209ASSERT_EQ(0, ioctl(self->fd,1210_IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),1211&access_cmd));1212access_pages_id = access_cmd.access_pages.out_access_pages_id;1213ASSERT_EQ(0, ioctl(self->fd,1214_IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),1215&access_cmd));1216test_cmd_destroy_access_pages(1217access_cmd.id,1218access_cmd.access_pages.out_access_pages_id);1219test_cmd_destroy_access_pages(access_cmd.id, access_pages_id);12201221/* Add/remove a domain with a user */1222ASSERT_EQ(0, ioctl(self->fd,1223_IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),1224&access_cmd));1225test_cmd_mock_domain(self->ioas_id, &mock_stdev_id,1226&mock_hwpt_id, NULL);1227check_map_cmd.id = mock_hwpt_id;1228ASSERT_EQ(0, ioctl(self->fd,1229_IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_MAP),1230&check_map_cmd));12311232test_ioctl_destroy(mock_stdev_id);1233test_cmd_destroy_access_pages(1234access_cmd.id,1235access_cmd.access_pages.out_access_pages_id);12361237test_ioctl_ioas_unmap(MOCK_APERTURE_START, BUFFER_SIZE);1238}1239test_cmd_destroy_access(access_cmd.id);1240}12411242TEST_F(iommufd_ioas, access_pin_unmap)1243{1244struct iommu_test_cmd access_pages_cmd = {1245.size = sizeof(access_pages_cmd),1246.op = IOMMU_TEST_OP_ACCESS_PAGES,1247.access_pages = { .iova = MOCK_APERTURE_START,1248.length = BUFFER_SIZE,1249.uptr = (uintptr_t)buffer },1250};12511252test_cmd_create_access(self->ioas_id, &access_pages_cmd.id,1253MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES);1254test_ioctl_ioas_map_fixed(buffer, BUFFER_SIZE, MOCK_APERTURE_START);1255ASSERT_EQ(0,1256ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),1257&access_pages_cmd));12581259/* Trigger the unmap op */1260test_ioctl_ioas_unmap(MOCK_APERTURE_START, BUFFER_SIZE);12611262/* kernel removed the item for us */1263test_err_destroy_access_pages(1264ENOENT, access_pages_cmd.id,1265access_pages_cmd.access_pages.out_access_pages_id);1266}12671268static void check_access_rw(struct __test_metadata *_metadata, int fd,1269unsigned int access_id, uint64_t iova,1270unsigned int def_flags)1271{1272uint16_t tmp[32];1273struct iommu_test_cmd access_cmd = {1274.size = sizeof(access_cmd),1275.op = IOMMU_TEST_OP_ACCESS_RW,1276.id = access_id,1277.access_rw = { .uptr = (uintptr_t)tmp },1278};1279uint16_t *buffer16 = buffer;1280unsigned int i;1281void *tmp2;12821283for (i = 0; i != BUFFER_SIZE / sizeof(*buffer16); i++)1284buffer16[i] = rand();12851286for (access_cmd.access_rw.iova = iova + PAGE_SIZE - 50;1287access_cmd.access_rw.iova < iova + PAGE_SIZE + 50;1288access_cmd.access_rw.iova++) {1289for (access_cmd.access_rw.length = 1;1290access_cmd.access_rw.length < sizeof(tmp);1291access_cmd.access_rw.length++) {1292access_cmd.access_rw.flags = def_flags;1293ASSERT_EQ(0, ioctl(fd,1294_IOMMU_TEST_CMD(1295IOMMU_TEST_OP_ACCESS_RW),1296&access_cmd));1297ASSERT_EQ(0,1298memcmp(buffer + (access_cmd.access_rw.iova -1299iova),1300tmp, access_cmd.access_rw.length));13011302for (i = 0; i != ARRAY_SIZE(tmp); i++)1303tmp[i] = rand();1304access_cmd.access_rw.flags = def_flags |1305MOCK_ACCESS_RW_WRITE;1306ASSERT_EQ(0, ioctl(fd,1307_IOMMU_TEST_CMD(1308IOMMU_TEST_OP_ACCESS_RW),1309&access_cmd));1310ASSERT_EQ(0,1311memcmp(buffer + (access_cmd.access_rw.iova -1312iova),1313tmp, access_cmd.access_rw.length));1314}1315}13161317/* Multi-page test */1318tmp2 = malloc(BUFFER_SIZE);1319ASSERT_NE(NULL, tmp2);1320access_cmd.access_rw.iova = iova;1321access_cmd.access_rw.length = BUFFER_SIZE;1322access_cmd.access_rw.flags = def_flags;1323access_cmd.access_rw.uptr = (uintptr_t)tmp2;1324ASSERT_EQ(0, ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_RW),1325&access_cmd));1326ASSERT_EQ(0, memcmp(buffer, tmp2, access_cmd.access_rw.length));1327free(tmp2);1328}13291330TEST_F(iommufd_ioas, access_rw)1331{1332__u32 access_id;1333__u64 iova;13341335test_cmd_create_access(self->ioas_id, &access_id, 0);1336test_ioctl_ioas_map(buffer, BUFFER_SIZE, &iova);1337check_access_rw(_metadata, self->fd, access_id, iova, 0);1338check_access_rw(_metadata, self->fd, access_id, iova,1339MOCK_ACCESS_RW_SLOW_PATH);1340test_ioctl_ioas_unmap(iova, BUFFER_SIZE);1341test_cmd_destroy_access(access_id);1342}13431344TEST_F(iommufd_ioas, access_rw_unaligned)1345{1346__u32 access_id;1347__u64 iova;13481349test_cmd_create_access(self->ioas_id, &access_id, 0);13501351/* Unaligned pages */1352iova = self->base_iova + MOCK_PAGE_SIZE;1353test_ioctl_ioas_map_fixed(buffer, BUFFER_SIZE, iova);1354check_access_rw(_metadata, self->fd, access_id, iova, 0);1355test_ioctl_ioas_unmap(iova, BUFFER_SIZE);1356test_cmd_destroy_access(access_id);1357}13581359TEST_F(iommufd_ioas, fork_gone)1360{1361__u32 access_id;1362pid_t child;13631364test_cmd_create_access(self->ioas_id, &access_id, 0);13651366/* Create a mapping with a different mm */1367child = fork();1368if (!child) {1369test_ioctl_ioas_map_fixed(buffer, BUFFER_SIZE,1370MOCK_APERTURE_START);1371exit(0);1372}1373ASSERT_NE(-1, child);1374ASSERT_EQ(child, waitpid(child, NULL, 0));13751376if (self->stdev_id) {1377/*1378* If a domain already existed then everything was pinned within1379* the fork, so this copies from one domain to another.1380*/1381test_cmd_mock_domain(self->ioas_id, NULL, NULL, NULL);1382check_access_rw(_metadata, self->fd, access_id,1383MOCK_APERTURE_START, 0);13841385} else {1386/*1387* Otherwise we need to actually pin pages which can't happen1388* since the fork is gone.1389*/1390test_err_mock_domain(EFAULT, self->ioas_id, NULL, NULL);1391}13921393test_cmd_destroy_access(access_id);1394}13951396TEST_F(iommufd_ioas, fork_present)1397{1398__u32 access_id;1399int pipefds[2];1400uint64_t tmp;1401pid_t child;1402int efd;14031404test_cmd_create_access(self->ioas_id, &access_id, 0);14051406ASSERT_EQ(0, pipe2(pipefds, O_CLOEXEC));1407efd = eventfd(0, EFD_CLOEXEC);1408ASSERT_NE(-1, efd);14091410/* Create a mapping with a different mm */1411child = fork();1412if (!child) {1413__u64 iova;1414uint64_t one = 1;14151416close(pipefds[1]);1417test_ioctl_ioas_map_fixed(buffer, BUFFER_SIZE,1418MOCK_APERTURE_START);1419if (write(efd, &one, sizeof(one)) != sizeof(one))1420exit(100);1421if (read(pipefds[0], &iova, 1) != 1)1422exit(100);1423exit(0);1424}1425close(pipefds[0]);1426ASSERT_NE(-1, child);1427ASSERT_EQ(8, read(efd, &tmp, sizeof(tmp)));14281429/* Read pages from the remote process */1430test_cmd_mock_domain(self->ioas_id, NULL, NULL, NULL);1431check_access_rw(_metadata, self->fd, access_id, MOCK_APERTURE_START, 0);14321433ASSERT_EQ(0, close(pipefds[1]));1434ASSERT_EQ(child, waitpid(child, NULL, 0));14351436test_cmd_destroy_access(access_id);1437}14381439TEST_F(iommufd_ioas, ioas_option_huge_pages)1440{1441struct iommu_option cmd = {1442.size = sizeof(cmd),1443.option_id = IOMMU_OPTION_HUGE_PAGES,1444.op = IOMMU_OPTION_OP_GET,1445.val64 = 3,1446.object_id = self->ioas_id,1447};14481449ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));1450ASSERT_EQ(1, cmd.val64);14511452cmd.op = IOMMU_OPTION_OP_SET;1453cmd.val64 = 0;1454ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));14551456cmd.op = IOMMU_OPTION_OP_GET;1457cmd.val64 = 3;1458ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));1459ASSERT_EQ(0, cmd.val64);14601461cmd.op = IOMMU_OPTION_OP_SET;1462cmd.val64 = 2;1463EXPECT_ERRNO(EINVAL, ioctl(self->fd, IOMMU_OPTION, &cmd));14641465cmd.op = IOMMU_OPTION_OP_SET;1466cmd.val64 = 1;1467ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));1468}14691470TEST_F(iommufd_ioas, ioas_iova_alloc)1471{1472unsigned int length;1473__u64 iova;14741475for (length = 1; length != PAGE_SIZE * 2; length++) {1476if (variant->mock_domains && (length % MOCK_PAGE_SIZE)) {1477test_err_ioctl_ioas_map(EINVAL, buffer, length, &iova);1478} else {1479test_ioctl_ioas_map(buffer, length, &iova);1480test_ioctl_ioas_unmap(iova, length);1481}1482}1483}14841485TEST_F(iommufd_ioas, ioas_align_change)1486{1487struct iommu_option cmd = {1488.size = sizeof(cmd),1489.option_id = IOMMU_OPTION_HUGE_PAGES,1490.op = IOMMU_OPTION_OP_SET,1491.object_id = self->ioas_id,1492/* 0 means everything must be aligned to PAGE_SIZE */1493.val64 = 0,1494};14951496/*1497* We cannot upgrade the alignment using OPTION_HUGE_PAGES when a domain1498* and map are present.1499*/1500if (variant->mock_domains)1501return;15021503/*1504* We can upgrade to PAGE_SIZE alignment when things are aligned right1505*/1506test_ioctl_ioas_map_fixed(buffer, PAGE_SIZE, MOCK_APERTURE_START);1507ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));15081509/* Misalignment is rejected at map time */1510test_err_ioctl_ioas_map_fixed(EINVAL, buffer + MOCK_PAGE_SIZE,1511PAGE_SIZE,1512MOCK_APERTURE_START + PAGE_SIZE);1513ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));15141515/* Reduce alignment */1516cmd.val64 = 1;1517ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));15181519/* Confirm misalignment is rejected during alignment upgrade */1520test_ioctl_ioas_map_fixed(buffer + MOCK_PAGE_SIZE, PAGE_SIZE,1521MOCK_APERTURE_START + PAGE_SIZE);1522cmd.val64 = 0;1523EXPECT_ERRNO(EADDRINUSE, ioctl(self->fd, IOMMU_OPTION, &cmd));15241525test_ioctl_ioas_unmap(MOCK_APERTURE_START + PAGE_SIZE, PAGE_SIZE);1526test_ioctl_ioas_unmap(MOCK_APERTURE_START, PAGE_SIZE);1527}15281529TEST_F(iommufd_ioas, copy_sweep)1530{1531struct iommu_ioas_copy copy_cmd = {1532.size = sizeof(copy_cmd),1533.flags = IOMMU_IOAS_MAP_FIXED_IOVA | IOMMU_IOAS_MAP_WRITEABLE,1534.src_ioas_id = self->ioas_id,1535.dst_iova = MOCK_APERTURE_START,1536.length = MOCK_PAGE_SIZE,1537};1538unsigned int dst_ioas_id;1539uint64_t last_iova;1540uint64_t iova;15411542test_ioctl_ioas_alloc(&dst_ioas_id);1543copy_cmd.dst_ioas_id = dst_ioas_id;15441545if (variant->mock_domains)1546last_iova = MOCK_APERTURE_START + BUFFER_SIZE - 1;1547else1548last_iova = MOCK_APERTURE_START + BUFFER_SIZE - 2;15491550test_ioctl_ioas_map_fixed(buffer, last_iova - MOCK_APERTURE_START + 1,1551MOCK_APERTURE_START);15521553for (iova = MOCK_APERTURE_START - PAGE_SIZE; iova <= last_iova;1554iova += 511) {1555copy_cmd.src_iova = iova;1556if (iova < MOCK_APERTURE_START ||1557iova + copy_cmd.length - 1 > last_iova) {1558EXPECT_ERRNO(ENOENT, ioctl(self->fd, IOMMU_IOAS_COPY,1559©_cmd));1560} else {1561ASSERT_EQ(0,1562ioctl(self->fd, IOMMU_IOAS_COPY, ©_cmd));1563test_ioctl_ioas_unmap_id(dst_ioas_id, copy_cmd.dst_iova,1564copy_cmd.length);1565}1566}15671568test_ioctl_destroy(dst_ioas_id);1569}15701571TEST_F(iommufd_ioas, dmabuf_simple)1572{1573size_t buf_size = PAGE_SIZE*4;1574__u64 iova;1575int dfd;15761577test_cmd_get_dmabuf(buf_size, &dfd);1578test_err_ioctl_ioas_map_file(EINVAL, dfd, 0, 0, &iova);1579test_err_ioctl_ioas_map_file(EINVAL, dfd, buf_size, buf_size, &iova);1580test_err_ioctl_ioas_map_file(EINVAL, dfd, 0, buf_size + 1, &iova);1581test_ioctl_ioas_map_file(dfd, 0, buf_size, &iova);15821583close(dfd);1584}15851586TEST_F(iommufd_ioas, dmabuf_revoke)1587{1588size_t buf_size = PAGE_SIZE*4;1589__u32 hwpt_id;1590__u64 iova;1591__u64 iova2;1592int dfd;15931594test_cmd_get_dmabuf(buf_size, &dfd);1595test_ioctl_ioas_map_file(dfd, 0, buf_size, &iova);1596test_cmd_revoke_dmabuf(dfd, true);15971598if (variant->mock_domains)1599test_cmd_hwpt_alloc(self->device_id, self->ioas_id, 0,1600&hwpt_id);16011602test_err_ioctl_ioas_map_file(ENODEV, dfd, 0, buf_size, &iova2);16031604test_cmd_revoke_dmabuf(dfd, false);1605test_ioctl_ioas_map_file(dfd, 0, buf_size, &iova2);16061607/* Restore the iova back */1608test_ioctl_ioas_unmap(iova, buf_size);1609test_ioctl_ioas_map_fixed_file(dfd, 0, buf_size, iova);16101611close(dfd);1612}16131614FIXTURE(iommufd_mock_domain)1615{1616int fd;1617uint32_t ioas_id;1618uint32_t hwpt_id;1619uint32_t hwpt_ids[2];1620uint32_t stdev_ids[2];1621uint32_t idev_ids[2];1622int mmap_flags;1623size_t mmap_buf_size;1624};16251626FIXTURE_VARIANT(iommufd_mock_domain)1627{1628unsigned int mock_domains;1629bool hugepages;1630bool file;1631};16321633FIXTURE_SETUP(iommufd_mock_domain)1634{1635unsigned int i;16361637self->fd = open("/dev/iommu", O_RDWR);1638ASSERT_NE(-1, self->fd);1639test_ioctl_ioas_alloc(&self->ioas_id);16401641ASSERT_GE(ARRAY_SIZE(self->hwpt_ids), variant->mock_domains);16421643for (i = 0; i != variant->mock_domains; i++) {1644test_cmd_mock_domain(self->ioas_id, &self->stdev_ids[i],1645&self->hwpt_ids[i], &self->idev_ids[i]);1646test_cmd_dev_check_cache_all(self->idev_ids[0],1647IOMMU_TEST_DEV_CACHE_DEFAULT);1648}1649self->hwpt_id = self->hwpt_ids[0];16501651self->mmap_flags = MAP_SHARED | MAP_ANONYMOUS;1652self->mmap_buf_size = PAGE_SIZE * 8;1653if (variant->hugepages) {1654/*1655* MAP_POPULATE will cause the kernel to fail mmap if THPs are1656* not available.1657*/1658self->mmap_flags |= MAP_HUGETLB | MAP_POPULATE;1659self->mmap_buf_size = HUGEPAGE_SIZE * 2;1660}1661}16621663FIXTURE_TEARDOWN(iommufd_mock_domain)1664{1665teardown_iommufd(self->fd, _metadata);1666}16671668FIXTURE_VARIANT_ADD(iommufd_mock_domain, one_domain)1669{1670.mock_domains = 1,1671.hugepages = false,1672.file = false,1673};16741675FIXTURE_VARIANT_ADD(iommufd_mock_domain, two_domains)1676{1677.mock_domains = 2,1678.hugepages = false,1679.file = false,1680};16811682FIXTURE_VARIANT_ADD(iommufd_mock_domain, one_domain_hugepage)1683{1684.mock_domains = 1,1685.hugepages = true,1686.file = false,1687};16881689FIXTURE_VARIANT_ADD(iommufd_mock_domain, two_domains_hugepage)1690{1691.mock_domains = 2,1692.hugepages = true,1693.file = false,1694};16951696FIXTURE_VARIANT_ADD(iommufd_mock_domain, one_domain_file)1697{1698.mock_domains = 1,1699.hugepages = false,1700.file = true,1701};17021703FIXTURE_VARIANT_ADD(iommufd_mock_domain, one_domain_file_hugepage)1704{1705.mock_domains = 1,1706.hugepages = true,1707.file = true,1708};170917101711/* Have the kernel check that the user pages made it to the iommu_domain */1712#define check_mock_iova(_ptr, _iova, _length) \1713({ \1714struct iommu_test_cmd check_map_cmd = { \1715.size = sizeof(check_map_cmd), \1716.op = IOMMU_TEST_OP_MD_CHECK_MAP, \1717.id = self->hwpt_id, \1718.check_map = { .iova = _iova, \1719.length = _length, \1720.uptr = (uintptr_t)(_ptr) }, \1721}; \1722ASSERT_EQ(0, \1723ioctl(self->fd, \1724_IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_MAP), \1725&check_map_cmd)); \1726if (self->hwpt_ids[1]) { \1727check_map_cmd.id = self->hwpt_ids[1]; \1728ASSERT_EQ(0, \1729ioctl(self->fd, \1730_IOMMU_TEST_CMD( \1731IOMMU_TEST_OP_MD_CHECK_MAP), \1732&check_map_cmd)); \1733} \1734})17351736static void1737test_basic_mmap(struct __test_metadata *_metadata,1738struct _test_data_iommufd_mock_domain *self,1739const struct _fixture_variant_iommufd_mock_domain *variant)1740{1741size_t buf_size = self->mmap_buf_size;1742uint8_t *buf;1743__u64 iova;17441745/* Simple one page map */1746test_ioctl_ioas_map(buffer, PAGE_SIZE, &iova);1747check_mock_iova(buffer, iova, PAGE_SIZE);17481749buf = mmap(0, buf_size, PROT_READ | PROT_WRITE, self->mmap_flags, -1,17500);1751ASSERT_NE(MAP_FAILED, buf);17521753/* EFAULT half way through mapping */1754ASSERT_EQ(0, munmap(buf + buf_size / 2, buf_size / 2));1755test_err_ioctl_ioas_map(EFAULT, buf, buf_size, &iova);17561757/* EFAULT on first page */1758ASSERT_EQ(0, munmap(buf, buf_size / 2));1759test_err_ioctl_ioas_map(EFAULT, buf, buf_size, &iova);1760}17611762static void1763test_basic_file(struct __test_metadata *_metadata,1764struct _test_data_iommufd_mock_domain *self,1765const struct _fixture_variant_iommufd_mock_domain *variant)1766{1767size_t buf_size = self->mmap_buf_size;1768uint8_t *buf;1769__u64 iova;1770int mfd_tmp;1771int prot = PROT_READ | PROT_WRITE;17721773/* Simple one page map */1774test_ioctl_ioas_map_file(mfd, 0, PAGE_SIZE, &iova);1775check_mock_iova(mfd_buffer, iova, PAGE_SIZE);17761777buf = memfd_mmap(buf_size, prot, MAP_SHARED, &mfd_tmp);1778ASSERT_NE(MAP_FAILED, buf);17791780test_err_ioctl_ioas_map_file(EINVAL, mfd_tmp, 0, buf_size + 1, &iova);17811782ASSERT_EQ(0, ftruncate(mfd_tmp, 0));1783test_err_ioctl_ioas_map_file(EINVAL, mfd_tmp, 0, buf_size, &iova);17841785close(mfd_tmp);1786}17871788TEST_F(iommufd_mock_domain, basic)1789{1790if (variant->file)1791test_basic_file(_metadata, self, variant);1792else1793test_basic_mmap(_metadata, self, variant);1794}17951796TEST_F(iommufd_mock_domain, ro_unshare)1797{1798uint8_t *buf;1799__u64 iova;1800int fd;18011802fd = open("/proc/self/exe", O_RDONLY);1803ASSERT_NE(-1, fd);18041805buf = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);1806ASSERT_NE(MAP_FAILED, buf);1807close(fd);18081809/*1810* There have been lots of changes to the "unshare" mechanism in1811* get_user_pages(), make sure it works right. The write to the page1812* after we map it for reading should not change the assigned PFN.1813*/1814ASSERT_EQ(0,1815_test_ioctl_ioas_map(self->fd, self->ioas_id, buf, PAGE_SIZE,1816&iova, IOMMU_IOAS_MAP_READABLE));1817check_mock_iova(buf, iova, PAGE_SIZE);1818memset(buf, 1, PAGE_SIZE);1819check_mock_iova(buf, iova, PAGE_SIZE);1820ASSERT_EQ(0, munmap(buf, PAGE_SIZE));1821}18221823TEST_F(iommufd_mock_domain, all_aligns)1824{1825size_t test_step = variant->hugepages ? (self->mmap_buf_size / 16) :1826MOCK_PAGE_SIZE;1827size_t buf_size = self->mmap_buf_size;1828unsigned int start;1829unsigned int end;1830uint8_t *buf;1831int prot = PROT_READ | PROT_WRITE;1832int mfd = -1;18331834if (variant->file)1835buf = memfd_mmap(buf_size, prot, MAP_SHARED, &mfd);1836else1837buf = mmap(0, buf_size, prot, self->mmap_flags, -1, 0);1838ASSERT_NE(MAP_FAILED, buf);1839if (variant->file)1840ASSERT_GT(mfd, 0);1841check_refs(buf, buf_size, 0);18421843/*1844* Map every combination of page size and alignment within a big region,1845* less for hugepage case as it takes so long to finish.1846*/1847for (start = 0; start < buf_size; start += test_step) {1848if (variant->hugepages)1849end = buf_size;1850else1851end = start + MOCK_PAGE_SIZE;1852for (; end < buf_size; end += MOCK_PAGE_SIZE) {1853size_t length = end - start;1854__u64 iova;18551856if (variant->file) {1857test_ioctl_ioas_map_file(mfd, start, length,1858&iova);1859} else {1860test_ioctl_ioas_map(buf + start, length, &iova);1861}1862check_mock_iova(buf + start, iova, length);1863check_refs(buf + start / PAGE_SIZE * PAGE_SIZE,1864end / PAGE_SIZE * PAGE_SIZE -1865start / PAGE_SIZE * PAGE_SIZE,18661);18671868test_ioctl_ioas_unmap(iova, length);1869}1870}1871check_refs(buf, buf_size, 0);1872ASSERT_EQ(0, munmap(buf, buf_size));1873if (variant->file)1874close(mfd);1875}18761877TEST_F(iommufd_mock_domain, all_aligns_copy)1878{1879size_t test_step = variant->hugepages ? self->mmap_buf_size / 16 :1880MOCK_PAGE_SIZE;1881size_t buf_size = self->mmap_buf_size;1882unsigned int start;1883unsigned int end;1884uint8_t *buf;1885int prot = PROT_READ | PROT_WRITE;1886int mfd = -1;18871888if (variant->file)1889buf = memfd_mmap(buf_size, prot, MAP_SHARED, &mfd);1890else1891buf = mmap(0, buf_size, prot, self->mmap_flags, -1, 0);1892ASSERT_NE(MAP_FAILED, buf);1893if (variant->file)1894ASSERT_GT(mfd, 0);1895check_refs(buf, buf_size, 0);18961897/*1898* Map every combination of page size and alignment within a big region,1899* less for hugepage case as it takes so long to finish.1900*/1901for (start = 0; start < buf_size; start += test_step) {1902if (variant->hugepages)1903end = buf_size;1904else1905end = start + MOCK_PAGE_SIZE;1906for (; end < buf_size; end += MOCK_PAGE_SIZE) {1907size_t length = end - start;1908unsigned int old_id;1909uint32_t mock_stdev_id;1910__u64 iova;19111912if (variant->file) {1913test_ioctl_ioas_map_file(mfd, start, length,1914&iova);1915} else {1916test_ioctl_ioas_map(buf + start, length, &iova);1917}19181919/* Add and destroy a domain while the area exists */1920old_id = self->hwpt_ids[1];1921test_cmd_mock_domain(self->ioas_id, &mock_stdev_id,1922&self->hwpt_ids[1], NULL);19231924check_mock_iova(buf + start, iova, length);1925check_refs(buf + start / PAGE_SIZE * PAGE_SIZE,1926end / PAGE_SIZE * PAGE_SIZE -1927start / PAGE_SIZE * PAGE_SIZE,19281);19291930test_ioctl_destroy(mock_stdev_id);1931self->hwpt_ids[1] = old_id;19321933test_ioctl_ioas_unmap(iova, length);1934}1935}1936check_refs(buf, buf_size, 0);1937ASSERT_EQ(0, munmap(buf, buf_size));1938if (variant->file)1939close(mfd);1940}19411942TEST_F(iommufd_mock_domain, user_copy)1943{1944void *buf = variant->file ? mfd_buffer : buffer;1945struct iommu_test_cmd access_cmd = {1946.size = sizeof(access_cmd),1947.op = IOMMU_TEST_OP_ACCESS_PAGES,1948.access_pages = { .length = BUFFER_SIZE,1949.uptr = (uintptr_t)buf },1950};1951struct iommu_ioas_copy copy_cmd = {1952.size = sizeof(copy_cmd),1953.flags = IOMMU_IOAS_MAP_FIXED_IOVA | IOMMU_IOAS_MAP_WRITEABLE,1954.dst_ioas_id = self->ioas_id,1955.dst_iova = MOCK_APERTURE_START,1956.length = BUFFER_SIZE,1957};1958struct iommu_ioas_unmap unmap_cmd = {1959.size = sizeof(unmap_cmd),1960.ioas_id = self->ioas_id,1961.iova = MOCK_APERTURE_START,1962.length = BUFFER_SIZE,1963};1964unsigned int new_ioas_id, ioas_id;19651966/* Pin the pages in an IOAS with no domains then copy to an IOAS with domains */1967test_ioctl_ioas_alloc(&ioas_id);1968if (variant->file) {1969test_ioctl_ioas_map_id_file(ioas_id, mfd, 0, BUFFER_SIZE,1970©_cmd.src_iova);1971} else {1972test_ioctl_ioas_map_id(ioas_id, buf, BUFFER_SIZE,1973©_cmd.src_iova);1974}1975test_cmd_create_access(ioas_id, &access_cmd.id,1976MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES);19771978access_cmd.access_pages.iova = copy_cmd.src_iova;1979ASSERT_EQ(0,1980ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),1981&access_cmd));1982copy_cmd.src_ioas_id = ioas_id;1983ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_COPY, ©_cmd));1984check_mock_iova(buf, MOCK_APERTURE_START, BUFFER_SIZE);19851986/* Now replace the ioas with a new one */1987test_ioctl_ioas_alloc(&new_ioas_id);1988if (variant->file) {1989test_ioctl_ioas_map_id_file(new_ioas_id, mfd, 0, BUFFER_SIZE,1990©_cmd.src_iova);1991} else {1992test_ioctl_ioas_map_id(new_ioas_id, buf, BUFFER_SIZE,1993©_cmd.src_iova);1994}1995test_cmd_access_replace_ioas(access_cmd.id, new_ioas_id);19961997/* Destroy the old ioas and cleanup copied mapping */1998ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_UNMAP, &unmap_cmd));1999test_ioctl_destroy(ioas_id);20002001/* Then run the same test again with the new ioas */2002access_cmd.access_pages.iova = copy_cmd.src_iova;2003ASSERT_EQ(0,2004ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES),2005&access_cmd));2006copy_cmd.src_ioas_id = new_ioas_id;2007ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_COPY, ©_cmd));2008check_mock_iova(buf, MOCK_APERTURE_START, BUFFER_SIZE);20092010test_cmd_destroy_access_pages(2011access_cmd.id, access_cmd.access_pages.out_access_pages_id);2012test_cmd_destroy_access(access_cmd.id);20132014test_ioctl_destroy(new_ioas_id);2015}20162017TEST_F(iommufd_mock_domain, replace)2018{2019uint32_t ioas_id;20202021test_ioctl_ioas_alloc(&ioas_id);20222023test_cmd_mock_domain_replace(self->stdev_ids[0], ioas_id);20242025/*2026* Replacing the IOAS causes the prior HWPT to be deallocated, thus we2027* should get enoent when we try to use it.2028*/2029if (variant->mock_domains == 1)2030test_err_mock_domain_replace(ENOENT, self->stdev_ids[0],2031self->hwpt_ids[0]);20322033test_cmd_mock_domain_replace(self->stdev_ids[0], ioas_id);2034if (variant->mock_domains >= 2) {2035test_cmd_mock_domain_replace(self->stdev_ids[0],2036self->hwpt_ids[1]);2037test_cmd_mock_domain_replace(self->stdev_ids[0],2038self->hwpt_ids[1]);2039test_cmd_mock_domain_replace(self->stdev_ids[0],2040self->hwpt_ids[0]);2041}20422043test_cmd_mock_domain_replace(self->stdev_ids[0], self->ioas_id);2044test_ioctl_destroy(ioas_id);2045}20462047TEST_F(iommufd_mock_domain, alloc_hwpt)2048{2049int i;20502051for (i = 0; i != variant->mock_domains; i++) {2052uint32_t hwpt_id[2];2053uint32_t stddev_id;20542055test_err_hwpt_alloc(EOPNOTSUPP,2056self->idev_ids[i], self->ioas_id,2057~IOMMU_HWPT_ALLOC_NEST_PARENT, &hwpt_id[0]);2058test_cmd_hwpt_alloc(self->idev_ids[i], self->ioas_id,20590, &hwpt_id[0]);2060test_cmd_hwpt_alloc(self->idev_ids[i], self->ioas_id,2061IOMMU_HWPT_ALLOC_NEST_PARENT, &hwpt_id[1]);20622063/* Do a hw_pagetable rotation test */2064test_cmd_mock_domain_replace(self->stdev_ids[i], hwpt_id[0]);2065EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, hwpt_id[0]));2066test_cmd_mock_domain_replace(self->stdev_ids[i], hwpt_id[1]);2067EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, hwpt_id[1]));2068test_cmd_mock_domain_replace(self->stdev_ids[i], self->ioas_id);2069test_ioctl_destroy(hwpt_id[1]);20702071test_cmd_mock_domain(hwpt_id[0], &stddev_id, NULL, NULL);2072test_ioctl_destroy(stddev_id);2073test_ioctl_destroy(hwpt_id[0]);2074}2075}20762077FIXTURE(iommufd_dirty_tracking)2078{2079int fd;2080uint32_t ioas_id;2081uint32_t hwpt_id;2082uint32_t stdev_id;2083uint32_t idev_id;2084unsigned long page_size;2085unsigned long bitmap_size;2086void *bitmap;2087void *buffer;2088};20892090FIXTURE_VARIANT(iommufd_dirty_tracking)2091{2092unsigned long buffer_size;2093bool hugepages;2094};20952096FIXTURE_SETUP(iommufd_dirty_tracking)2097{2098struct iommu_option cmd = {2099.size = sizeof(cmd),2100.option_id = IOMMU_OPTION_HUGE_PAGES,2101.op = IOMMU_OPTION_OP_SET,2102.val64 = 0,2103};2104size_t mmap_buffer_size;2105unsigned long size;2106int mmap_flags;2107void *vrc;2108int rc;21092110if (variant->buffer_size < MOCK_PAGE_SIZE) {2111SKIP(return,2112"Skipping buffer_size=%lu, less than MOCK_PAGE_SIZE=%u",2113variant->buffer_size, MOCK_PAGE_SIZE);2114}21152116self->fd = open("/dev/iommu", O_RDWR);2117ASSERT_NE(-1, self->fd);21182119mmap_flags = MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED;2120mmap_buffer_size = variant->buffer_size;2121if (variant->hugepages) {2122/*2123* MAP_POPULATE will cause the kernel to fail mmap if THPs are2124* not available.2125*/2126mmap_flags |= MAP_HUGETLB | MAP_POPULATE;21272128/*2129* Allocation must be aligned to the HUGEPAGE_SIZE, because the2130* following mmap() will automatically align the length to be a2131* multiple of the underlying huge page size. Failing to do the2132* same at this allocation will result in a memory overwrite by2133* the mmap().2134*/2135if (mmap_buffer_size < HUGEPAGE_SIZE)2136mmap_buffer_size = HUGEPAGE_SIZE;2137}21382139rc = posix_memalign(&self->buffer, HUGEPAGE_SIZE, mmap_buffer_size);2140if (rc || !self->buffer) {2141SKIP(return, "Skipping buffer_size=%lu due to errno=%d",2142mmap_buffer_size, rc);2143}2144assert((uintptr_t)self->buffer % HUGEPAGE_SIZE == 0);2145vrc = mmap(self->buffer, mmap_buffer_size, PROT_READ | PROT_WRITE,2146mmap_flags, -1, 0);2147assert(vrc == self->buffer);21482149self->page_size = MOCK_PAGE_SIZE;2150self->bitmap_size = variant->buffer_size / self->page_size;21512152/* Provision with an extra (PAGE_SIZE) for the unaligned case */2153size = DIV_ROUND_UP(self->bitmap_size, BITS_PER_BYTE);2154rc = posix_memalign(&self->bitmap, PAGE_SIZE, size + PAGE_SIZE);2155assert(!rc);2156assert(self->bitmap);2157assert((uintptr_t)self->bitmap % PAGE_SIZE == 0);21582159test_ioctl_ioas_alloc(&self->ioas_id);21602161/*2162* For dirty testing it is important that the page size fed into2163* the iommu page tables matches the size the dirty logic2164* expects, or set_dirty can touch too much stuff.2165*/2166cmd.object_id = self->ioas_id;2167if (!variant->hugepages)2168ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));21692170test_cmd_mock_domain(self->ioas_id, &self->stdev_id, &self->hwpt_id,2171&self->idev_id);2172}21732174FIXTURE_TEARDOWN(iommufd_dirty_tracking)2175{2176free(self->buffer);2177free(self->bitmap);2178teardown_iommufd(self->fd, _metadata);2179}21802181FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty8k)2182{2183/* half of an u8 index bitmap */2184.buffer_size = 8UL * 1024UL,2185};21862187FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty16k)2188{2189/* one u8 index bitmap */2190.buffer_size = 16UL * 1024UL,2191};21922193FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty64k)2194{2195/* one u32 index bitmap */2196.buffer_size = 64UL * 1024UL,2197};21982199FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128k)2200{2201/* one u64 index bitmap */2202.buffer_size = 128UL * 1024UL,2203};22042205FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty320k)2206{2207/* two u64 index and trailing end bitmap */2208.buffer_size = 320UL * 1024UL,2209};22102211FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty64M)2212{2213/* 4K bitmap (64M IOVA range) */2214.buffer_size = 64UL * 1024UL * 1024UL,2215};22162217FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty64M_huge)2218{2219/* 4K bitmap (64M IOVA range) */2220.buffer_size = 64UL * 1024UL * 1024UL,2221.hugepages = true,2222};22232224FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128M)2225{2226/* 8K bitmap (128M IOVA range) */2227.buffer_size = 128UL * 1024UL * 1024UL,2228};22292230FIXTURE_VARIANT_ADD(iommufd_dirty_tracking, domain_dirty128M_huge)2231{2232/* 8K bitmap (128M IOVA range) */2233.buffer_size = 128UL * 1024UL * 1024UL,2234.hugepages = true,2235};22362237TEST_F(iommufd_dirty_tracking, enforce_dirty)2238{2239uint32_t ioas_id, stddev_id, idev_id;2240uint32_t hwpt_id, _hwpt_id;2241uint32_t dev_flags;22422243/* Regular case */2244dev_flags = MOCK_FLAGS_DEVICE_NO_DIRTY;2245test_cmd_hwpt_alloc(self->idev_id, self->ioas_id,2246IOMMU_HWPT_ALLOC_DIRTY_TRACKING, &hwpt_id);2247test_cmd_mock_domain(hwpt_id, &stddev_id, NULL, NULL);2248test_err_mock_domain_flags(EINVAL, hwpt_id, dev_flags, &stddev_id,2249NULL);2250test_ioctl_destroy(stddev_id);2251test_ioctl_destroy(hwpt_id);22522253/* IOMMU device does not support dirty tracking */2254test_ioctl_ioas_alloc(&ioas_id);2255test_cmd_mock_domain_flags(ioas_id, dev_flags, &stddev_id, &_hwpt_id,2256&idev_id);2257test_err_hwpt_alloc(EOPNOTSUPP, idev_id, ioas_id,2258IOMMU_HWPT_ALLOC_DIRTY_TRACKING, &hwpt_id);2259test_ioctl_destroy(stddev_id);2260}22612262TEST_F(iommufd_dirty_tracking, set_dirty_tracking)2263{2264uint32_t stddev_id;2265uint32_t hwpt_id;22662267test_cmd_hwpt_alloc(self->idev_id, self->ioas_id,2268IOMMU_HWPT_ALLOC_DIRTY_TRACKING, &hwpt_id);2269test_cmd_mock_domain(hwpt_id, &stddev_id, NULL, NULL);2270test_cmd_set_dirty_tracking(hwpt_id, true);2271test_cmd_set_dirty_tracking(hwpt_id, false);22722273test_ioctl_destroy(stddev_id);2274test_ioctl_destroy(hwpt_id);2275}22762277TEST_F(iommufd_dirty_tracking, device_dirty_capability)2278{2279uint32_t caps = 0;2280uint32_t stddev_id;2281uint32_t hwpt_id;22822283test_cmd_hwpt_alloc(self->idev_id, self->ioas_id, 0, &hwpt_id);2284test_cmd_mock_domain(hwpt_id, &stddev_id, NULL, NULL);2285test_cmd_get_hw_capabilities(self->idev_id, caps);2286ASSERT_EQ(IOMMU_HW_CAP_DIRTY_TRACKING,2287caps & IOMMU_HW_CAP_DIRTY_TRACKING);22882289test_ioctl_destroy(stddev_id);2290test_ioctl_destroy(hwpt_id);2291}22922293TEST_F(iommufd_dirty_tracking, get_dirty_bitmap)2294{2295uint32_t page_size = MOCK_PAGE_SIZE;2296uint32_t ioas_id = self->ioas_id;2297uint32_t hwpt_id;22982299if (variant->hugepages)2300page_size = MOCK_HUGE_PAGE_SIZE;23012302test_ioctl_ioas_map_fixed_id(ioas_id, self->buffer,2303variant->buffer_size, MOCK_APERTURE_START);23042305if (variant->hugepages)2306test_cmd_hwpt_alloc_iommupt(self->idev_id, ioas_id,2307IOMMU_HWPT_ALLOC_DIRTY_TRACKING,2308MOCK_IOMMUPT_HUGE, &hwpt_id);2309else2310test_cmd_hwpt_alloc_iommupt(self->idev_id, ioas_id,2311IOMMU_HWPT_ALLOC_DIRTY_TRACKING,2312MOCK_IOMMUPT_DEFAULT, &hwpt_id);23132314test_cmd_set_dirty_tracking(hwpt_id, true);23152316test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,2317MOCK_APERTURE_START, self->page_size, page_size,2318self->bitmap, self->bitmap_size, 0, _metadata);23192320/* PAGE_SIZE unaligned bitmap */2321test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,2322MOCK_APERTURE_START, self->page_size, page_size,2323self->bitmap + MOCK_PAGE_SIZE,2324self->bitmap_size, 0, _metadata);23252326/* u64 unaligned bitmap */2327test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,2328MOCK_APERTURE_START, self->page_size, page_size,2329self->bitmap + 0xff1, self->bitmap_size, 0,2330_metadata);23312332test_ioctl_destroy(hwpt_id);2333}23342335TEST_F(iommufd_dirty_tracking, get_dirty_bitmap_no_clear)2336{2337uint32_t page_size = MOCK_PAGE_SIZE;2338uint32_t ioas_id = self->ioas_id;2339uint32_t hwpt_id;23402341if (variant->hugepages)2342page_size = MOCK_HUGE_PAGE_SIZE;23432344test_ioctl_ioas_map_fixed_id(ioas_id, self->buffer,2345variant->buffer_size, MOCK_APERTURE_START);234623472348if (variant->hugepages)2349test_cmd_hwpt_alloc_iommupt(self->idev_id, ioas_id,2350IOMMU_HWPT_ALLOC_DIRTY_TRACKING,2351MOCK_IOMMUPT_HUGE, &hwpt_id);2352else2353test_cmd_hwpt_alloc_iommupt(self->idev_id, ioas_id,2354IOMMU_HWPT_ALLOC_DIRTY_TRACKING,2355MOCK_IOMMUPT_DEFAULT, &hwpt_id);23562357test_cmd_set_dirty_tracking(hwpt_id, true);23582359test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,2360MOCK_APERTURE_START, self->page_size, page_size,2361self->bitmap, self->bitmap_size,2362IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR,2363_metadata);23642365/* Unaligned bitmap */2366test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,2367MOCK_APERTURE_START, self->page_size, page_size,2368self->bitmap + MOCK_PAGE_SIZE,2369self->bitmap_size,2370IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR,2371_metadata);23722373/* u64 unaligned bitmap */2374test_mock_dirty_bitmaps(hwpt_id, variant->buffer_size,2375MOCK_APERTURE_START, self->page_size, page_size,2376self->bitmap + 0xff1, self->bitmap_size,2377IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR,2378_metadata);23792380test_ioctl_destroy(hwpt_id);2381}23822383/* VFIO compatibility IOCTLs */23842385TEST_F(iommufd, simple_ioctls)2386{2387ASSERT_EQ(VFIO_API_VERSION, ioctl(self->fd, VFIO_GET_API_VERSION));2388ASSERT_EQ(1, ioctl(self->fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU));2389}23902391TEST_F(iommufd, unmap_cmd)2392{2393struct vfio_iommu_type1_dma_unmap unmap_cmd = {2394.iova = MOCK_APERTURE_START,2395.size = PAGE_SIZE,2396};23972398unmap_cmd.argsz = 1;2399EXPECT_ERRNO(EINVAL, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));24002401unmap_cmd.argsz = sizeof(unmap_cmd);2402unmap_cmd.flags = 1 << 31;2403EXPECT_ERRNO(EINVAL, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));24042405unmap_cmd.flags = 0;2406EXPECT_ERRNO(ENODEV, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));2407}24082409TEST_F(iommufd, map_cmd)2410{2411struct vfio_iommu_type1_dma_map map_cmd = {2412.iova = MOCK_APERTURE_START,2413.size = PAGE_SIZE,2414.vaddr = (__u64)buffer,2415};24162417map_cmd.argsz = 1;2418EXPECT_ERRNO(EINVAL, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));24192420map_cmd.argsz = sizeof(map_cmd);2421map_cmd.flags = 1 << 31;2422EXPECT_ERRNO(EINVAL, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));24232424/* Requires a domain to be attached */2425map_cmd.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE;2426EXPECT_ERRNO(ENODEV, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));2427}24282429TEST_F(iommufd, info_cmd)2430{2431struct vfio_iommu_type1_info info_cmd = {};24322433/* Invalid argsz */2434info_cmd.argsz = 1;2435EXPECT_ERRNO(EINVAL, ioctl(self->fd, VFIO_IOMMU_GET_INFO, &info_cmd));24362437info_cmd.argsz = sizeof(info_cmd);2438EXPECT_ERRNO(ENODEV, ioctl(self->fd, VFIO_IOMMU_GET_INFO, &info_cmd));2439}24402441TEST_F(iommufd, set_iommu_cmd)2442{2443/* Requires a domain to be attached */2444EXPECT_ERRNO(ENODEV,2445ioctl(self->fd, VFIO_SET_IOMMU, VFIO_TYPE1v2_IOMMU));2446EXPECT_ERRNO(ENODEV, ioctl(self->fd, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU));2447}24482449TEST_F(iommufd, vfio_ioas)2450{2451struct iommu_vfio_ioas vfio_ioas_cmd = {2452.size = sizeof(vfio_ioas_cmd),2453.op = IOMMU_VFIO_IOAS_GET,2454};2455__u32 ioas_id;24562457/* ENODEV if there is no compat ioas */2458EXPECT_ERRNO(ENODEV, ioctl(self->fd, IOMMU_VFIO_IOAS, &vfio_ioas_cmd));24592460/* Invalid id for set */2461vfio_ioas_cmd.op = IOMMU_VFIO_IOAS_SET;2462EXPECT_ERRNO(ENOENT, ioctl(self->fd, IOMMU_VFIO_IOAS, &vfio_ioas_cmd));24632464/* Valid id for set*/2465test_ioctl_ioas_alloc(&ioas_id);2466vfio_ioas_cmd.ioas_id = ioas_id;2467ASSERT_EQ(0, ioctl(self->fd, IOMMU_VFIO_IOAS, &vfio_ioas_cmd));24682469/* Same id comes back from get */2470vfio_ioas_cmd.op = IOMMU_VFIO_IOAS_GET;2471ASSERT_EQ(0, ioctl(self->fd, IOMMU_VFIO_IOAS, &vfio_ioas_cmd));2472ASSERT_EQ(ioas_id, vfio_ioas_cmd.ioas_id);24732474/* Clear works */2475vfio_ioas_cmd.op = IOMMU_VFIO_IOAS_CLEAR;2476ASSERT_EQ(0, ioctl(self->fd, IOMMU_VFIO_IOAS, &vfio_ioas_cmd));2477vfio_ioas_cmd.op = IOMMU_VFIO_IOAS_GET;2478EXPECT_ERRNO(ENODEV, ioctl(self->fd, IOMMU_VFIO_IOAS, &vfio_ioas_cmd));2479}24802481FIXTURE(vfio_compat_mock_domain)2482{2483int fd;2484uint32_t ioas_id;2485};24862487FIXTURE_VARIANT(vfio_compat_mock_domain)2488{2489unsigned int version;2490};24912492FIXTURE_SETUP(vfio_compat_mock_domain)2493{2494struct iommu_vfio_ioas vfio_ioas_cmd = {2495.size = sizeof(vfio_ioas_cmd),2496.op = IOMMU_VFIO_IOAS_SET,2497};24982499self->fd = open("/dev/iommu", O_RDWR);2500ASSERT_NE(-1, self->fd);25012502/* Create what VFIO would consider a group */2503test_ioctl_ioas_alloc(&self->ioas_id);2504test_cmd_mock_domain(self->ioas_id, NULL, NULL, NULL);25052506/* Attach it to the vfio compat */2507vfio_ioas_cmd.ioas_id = self->ioas_id;2508ASSERT_EQ(0, ioctl(self->fd, IOMMU_VFIO_IOAS, &vfio_ioas_cmd));2509ASSERT_EQ(0, ioctl(self->fd, VFIO_SET_IOMMU, variant->version));2510}25112512FIXTURE_TEARDOWN(vfio_compat_mock_domain)2513{2514teardown_iommufd(self->fd, _metadata);2515}25162517FIXTURE_VARIANT_ADD(vfio_compat_mock_domain, Ver1v2)2518{2519.version = VFIO_TYPE1v2_IOMMU,2520};25212522FIXTURE_VARIANT_ADD(vfio_compat_mock_domain, Ver1v0)2523{2524.version = VFIO_TYPE1_IOMMU,2525};25262527TEST_F(vfio_compat_mock_domain, simple_close)2528{2529}25302531TEST_F(vfio_compat_mock_domain, option_huge_pages)2532{2533struct iommu_option cmd = {2534.size = sizeof(cmd),2535.option_id = IOMMU_OPTION_HUGE_PAGES,2536.op = IOMMU_OPTION_OP_GET,2537.val64 = 3,2538.object_id = self->ioas_id,2539};25402541ASSERT_EQ(0, ioctl(self->fd, IOMMU_OPTION, &cmd));2542if (variant->version == VFIO_TYPE1_IOMMU) {2543ASSERT_EQ(0, cmd.val64);2544} else {2545ASSERT_EQ(1, cmd.val64);2546}2547}25482549/*2550* Execute an ioctl command stored in buffer and check that the result does not2551* overflow memory.2552*/2553static bool is_filled(const void *buf, uint8_t c, size_t len)2554{2555const uint8_t *cbuf = buf;25562557for (; len; cbuf++, len--)2558if (*cbuf != c)2559return false;2560return true;2561}25622563#define ioctl_check_buf(fd, cmd) \2564({ \2565size_t _cmd_len = *(__u32 *)buffer; \2566\2567memset(buffer + _cmd_len, 0xAA, BUFFER_SIZE - _cmd_len); \2568ASSERT_EQ(0, ioctl(fd, cmd, buffer)); \2569ASSERT_EQ(true, is_filled(buffer + _cmd_len, 0xAA, \2570BUFFER_SIZE - _cmd_len)); \2571})25722573static void check_vfio_info_cap_chain(struct __test_metadata *_metadata,2574struct vfio_iommu_type1_info *info_cmd)2575{2576const struct vfio_info_cap_header *cap;25772578ASSERT_GE(info_cmd->argsz, info_cmd->cap_offset + sizeof(*cap));2579cap = buffer + info_cmd->cap_offset;2580while (true) {2581size_t cap_size;25822583if (cap->next)2584cap_size = (buffer + cap->next) - (void *)cap;2585else2586cap_size = (buffer + info_cmd->argsz) - (void *)cap;25872588switch (cap->id) {2589case VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE: {2590struct vfio_iommu_type1_info_cap_iova_range *data =2591(void *)cap;25922593ASSERT_EQ(1, data->header.version);2594ASSERT_EQ(1, data->nr_iovas);2595EXPECT_EQ(MOCK_APERTURE_START,2596data->iova_ranges[0].start);2597EXPECT_EQ(MOCK_APERTURE_LAST, data->iova_ranges[0].end);2598break;2599}2600case VFIO_IOMMU_TYPE1_INFO_DMA_AVAIL: {2601struct vfio_iommu_type1_info_dma_avail *data =2602(void *)cap;26032604ASSERT_EQ(1, data->header.version);2605ASSERT_EQ(sizeof(*data), cap_size);2606break;2607}2608default:2609ASSERT_EQ(false, true);2610break;2611}2612if (!cap->next)2613break;26142615ASSERT_GE(info_cmd->argsz, cap->next + sizeof(*cap));2616ASSERT_GE(buffer + cap->next, (void *)cap);2617cap = buffer + cap->next;2618}2619}26202621TEST_F(vfio_compat_mock_domain, get_info)2622{2623struct vfio_iommu_type1_info *info_cmd = buffer;2624unsigned int i;2625size_t caplen;26262627/* Pre-cap ABI */2628*info_cmd = (struct vfio_iommu_type1_info){2629.argsz = offsetof(struct vfio_iommu_type1_info, cap_offset),2630};2631ioctl_check_buf(self->fd, VFIO_IOMMU_GET_INFO);2632ASSERT_NE(0, info_cmd->iova_pgsizes);2633ASSERT_EQ(VFIO_IOMMU_INFO_PGSIZES | VFIO_IOMMU_INFO_CAPS,2634info_cmd->flags);26352636/* Read the cap chain size */2637*info_cmd = (struct vfio_iommu_type1_info){2638.argsz = sizeof(*info_cmd),2639};2640ioctl_check_buf(self->fd, VFIO_IOMMU_GET_INFO);2641ASSERT_NE(0, info_cmd->iova_pgsizes);2642ASSERT_EQ(VFIO_IOMMU_INFO_PGSIZES | VFIO_IOMMU_INFO_CAPS,2643info_cmd->flags);2644ASSERT_EQ(0, info_cmd->cap_offset);2645ASSERT_LT(sizeof(*info_cmd), info_cmd->argsz);26462647/* Read the caps, kernel should never create a corrupted caps */2648caplen = info_cmd->argsz;2649for (i = sizeof(*info_cmd); i < caplen; i++) {2650*info_cmd = (struct vfio_iommu_type1_info){2651.argsz = i,2652};2653ioctl_check_buf(self->fd, VFIO_IOMMU_GET_INFO);2654ASSERT_EQ(VFIO_IOMMU_INFO_PGSIZES | VFIO_IOMMU_INFO_CAPS,2655info_cmd->flags);2656if (!info_cmd->cap_offset)2657continue;2658check_vfio_info_cap_chain(_metadata, info_cmd);2659}2660}26612662static void shuffle_array(unsigned long *array, size_t nelms)2663{2664unsigned int i;26652666/* Shuffle */2667for (i = 0; i != nelms; i++) {2668unsigned long tmp = array[i];2669unsigned int other = rand() % (nelms - i);26702671array[i] = array[other];2672array[other] = tmp;2673}2674}26752676TEST_F(vfio_compat_mock_domain, map)2677{2678struct vfio_iommu_type1_dma_map map_cmd = {2679.argsz = sizeof(map_cmd),2680.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE,2681.vaddr = (uintptr_t)buffer,2682.size = BUFFER_SIZE,2683.iova = MOCK_APERTURE_START,2684};2685struct vfio_iommu_type1_dma_unmap unmap_cmd = {2686.argsz = sizeof(unmap_cmd),2687.size = BUFFER_SIZE,2688.iova = MOCK_APERTURE_START,2689};2690unsigned long pages_iova[BUFFER_SIZE / PAGE_SIZE];2691unsigned int i;26922693/* Simple map/unmap */2694ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));2695ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));2696ASSERT_EQ(BUFFER_SIZE, unmap_cmd.size);2697/* Unmap of empty is success */2698ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));26992700/* UNMAP_FLAG_ALL requires 0 iova/size */2701ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));2702unmap_cmd.flags = VFIO_DMA_UNMAP_FLAG_ALL;2703EXPECT_ERRNO(EINVAL, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));27042705unmap_cmd.iova = 0;2706unmap_cmd.size = 0;2707ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));2708ASSERT_EQ(BUFFER_SIZE, unmap_cmd.size);27092710/* Small pages */2711for (i = 0; i != ARRAY_SIZE(pages_iova); i++) {2712map_cmd.iova = pages_iova[i] =2713MOCK_APERTURE_START + i * PAGE_SIZE;2714map_cmd.vaddr = (uintptr_t)buffer + i * PAGE_SIZE;2715map_cmd.size = PAGE_SIZE;2716ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));2717}2718shuffle_array(pages_iova, ARRAY_SIZE(pages_iova));27192720unmap_cmd.flags = 0;2721unmap_cmd.size = PAGE_SIZE;2722for (i = 0; i != ARRAY_SIZE(pages_iova); i++) {2723unmap_cmd.iova = pages_iova[i];2724ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));2725}2726}27272728TEST_F(vfio_compat_mock_domain, huge_map)2729{2730size_t buf_size = HUGEPAGE_SIZE * 2;2731struct vfio_iommu_type1_dma_map map_cmd = {2732.argsz = sizeof(map_cmd),2733.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE,2734.size = buf_size,2735.iova = MOCK_APERTURE_START,2736};2737struct vfio_iommu_type1_dma_unmap unmap_cmd = {2738.argsz = sizeof(unmap_cmd),2739};2740unsigned long pages_iova[16];2741unsigned int i;2742void *buf;27432744/* Test huge pages and splitting */2745buf = mmap(0, buf_size, PROT_READ | PROT_WRITE,2746MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, -1,27470);2748ASSERT_NE(MAP_FAILED, buf);2749map_cmd.vaddr = (uintptr_t)buf;2750ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));27512752unmap_cmd.size = buf_size / ARRAY_SIZE(pages_iova);2753for (i = 0; i != ARRAY_SIZE(pages_iova); i++)2754pages_iova[i] = MOCK_APERTURE_START + (i * unmap_cmd.size);2755shuffle_array(pages_iova, ARRAY_SIZE(pages_iova));27562757/* type1 mode can cut up larger mappings, type1v2 always fails */2758for (i = 0; i != ARRAY_SIZE(pages_iova); i++) {2759unmap_cmd.iova = pages_iova[i];2760unmap_cmd.size = buf_size / ARRAY_SIZE(pages_iova);2761if (variant->version == VFIO_TYPE1_IOMMU) {2762ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA,2763&unmap_cmd));2764} else {2765EXPECT_ERRNO(ENOENT,2766ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA,2767&unmap_cmd));2768}2769}2770}27712772FIXTURE(iommufd_viommu)2773{2774int fd;2775uint32_t ioas_id;2776uint32_t stdev_id;2777uint32_t hwpt_id;2778uint32_t nested_hwpt_id;2779uint32_t device_id;2780uint32_t viommu_id;2781};27822783FIXTURE_VARIANT(iommufd_viommu)2784{2785unsigned int viommu;2786};27872788FIXTURE_SETUP(iommufd_viommu)2789{2790self->fd = open("/dev/iommu", O_RDWR);2791ASSERT_NE(-1, self->fd);2792test_ioctl_ioas_alloc(&self->ioas_id);2793test_ioctl_set_default_memory_limit();27942795if (variant->viommu) {2796struct iommu_hwpt_selftest data = {2797.iotlb = IOMMU_TEST_IOTLB_DEFAULT,2798};27992800test_cmd_mock_domain(self->ioas_id, &self->stdev_id, NULL,2801&self->device_id);28022803/* Allocate a nesting parent hwpt */2804test_cmd_hwpt_alloc(self->device_id, self->ioas_id,2805IOMMU_HWPT_ALLOC_NEST_PARENT,2806&self->hwpt_id);28072808/* Allocate a vIOMMU taking refcount of the parent hwpt */2809test_cmd_viommu_alloc(self->device_id, self->hwpt_id,2810IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0,2811&self->viommu_id);28122813/* Allocate a regular nested hwpt */2814test_cmd_hwpt_alloc_nested(self->device_id, self->viommu_id, 0,2815&self->nested_hwpt_id,2816IOMMU_HWPT_DATA_SELFTEST, &data,2817sizeof(data));2818}2819}28202821FIXTURE_TEARDOWN(iommufd_viommu)2822{2823teardown_iommufd(self->fd, _metadata);2824}28252826FIXTURE_VARIANT_ADD(iommufd_viommu, no_viommu)2827{2828.viommu = 0,2829};28302831FIXTURE_VARIANT_ADD(iommufd_viommu, mock_viommu)2832{2833.viommu = 1,2834};28352836TEST_F(iommufd_viommu, viommu_auto_destroy)2837{2838}28392840TEST_F(iommufd_viommu, viommu_negative_tests)2841{2842uint32_t device_id = self->device_id;2843uint32_t ioas_id = self->ioas_id;2844uint32_t hwpt_id;28452846if (self->device_id) {2847/* Negative test -- invalid hwpt (hwpt_id=0) */2848test_err_viommu_alloc(ENOENT, device_id, 0,2849IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0,2850NULL);28512852/* Negative test -- not a nesting parent hwpt */2853test_cmd_hwpt_alloc(device_id, ioas_id, 0, &hwpt_id);2854test_err_viommu_alloc(EINVAL, device_id, hwpt_id,2855IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0,2856NULL);2857test_ioctl_destroy(hwpt_id);28582859/* Negative test -- unsupported viommu type */2860test_err_viommu_alloc(EOPNOTSUPP, device_id, self->hwpt_id,28610xdead, NULL, 0, NULL);2862EXPECT_ERRNO(EBUSY,2863_test_ioctl_destroy(self->fd, self->hwpt_id));2864EXPECT_ERRNO(EBUSY,2865_test_ioctl_destroy(self->fd, self->viommu_id));2866} else {2867test_err_viommu_alloc(ENOENT, self->device_id, self->hwpt_id,2868IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0,2869NULL);2870}2871}28722873TEST_F(iommufd_viommu, viommu_alloc_nested_iopf)2874{2875struct iommu_hwpt_selftest data = {2876.iotlb = IOMMU_TEST_IOTLB_DEFAULT,2877};2878uint32_t viommu_id = self->viommu_id;2879uint32_t dev_id = self->device_id;2880uint32_t iopf_hwpt_id;2881uint32_t fault_id;2882uint32_t fault_fd;2883uint32_t vdev_id;28842885if (!dev_id)2886SKIP(return, "Skipping test for variant no_viommu");28872888test_ioctl_fault_alloc(&fault_id, &fault_fd);2889test_err_hwpt_alloc_iopf(ENOENT, dev_id, viommu_id, UINT32_MAX,2890IOMMU_HWPT_FAULT_ID_VALID, &iopf_hwpt_id,2891IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data));2892test_err_hwpt_alloc_iopf(EOPNOTSUPP, dev_id, viommu_id, fault_id,2893IOMMU_HWPT_FAULT_ID_VALID | (1 << 31),2894&iopf_hwpt_id, IOMMU_HWPT_DATA_SELFTEST, &data,2895sizeof(data));2896test_cmd_hwpt_alloc_iopf(dev_id, viommu_id, fault_id,2897IOMMU_HWPT_FAULT_ID_VALID, &iopf_hwpt_id,2898IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data));28992900/* Must allocate vdevice before attaching to a nested hwpt */2901test_err_mock_domain_replace(ENOENT, self->stdev_id, iopf_hwpt_id);2902test_cmd_vdevice_alloc(viommu_id, dev_id, 0x99, &vdev_id);2903test_cmd_mock_domain_replace(self->stdev_id, iopf_hwpt_id);2904EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, iopf_hwpt_id));2905test_cmd_trigger_iopf(dev_id, fault_fd);29062907test_cmd_mock_domain_replace(self->stdev_id, self->ioas_id);2908test_ioctl_destroy(iopf_hwpt_id);2909close(fault_fd);2910test_ioctl_destroy(fault_id);2911}29122913TEST_F(iommufd_viommu, viommu_alloc_with_data)2914{2915struct iommu_viommu_selftest data = {2916.in_data = 0xbeef,2917};2918uint32_t *test;29192920if (!self->device_id)2921SKIP(return, "Skipping test for variant no_viommu");29222923test_cmd_viommu_alloc(self->device_id, self->hwpt_id,2924IOMMU_VIOMMU_TYPE_SELFTEST, &data, sizeof(data),2925&self->viommu_id);2926ASSERT_EQ(data.out_data, data.in_data);29272928/* Negative mmap tests -- offset and length cannot be changed */2929test_err_mmap(ENXIO, data.out_mmap_length,2930data.out_mmap_offset + PAGE_SIZE);2931test_err_mmap(ENXIO, data.out_mmap_length,2932data.out_mmap_offset + PAGE_SIZE * 2);2933test_err_mmap(ENXIO, data.out_mmap_length / 2, data.out_mmap_offset);2934test_err_mmap(ENXIO, data.out_mmap_length * 2, data.out_mmap_offset);29352936/* Now do a correct mmap for a loopback test */2937test = mmap(NULL, data.out_mmap_length, PROT_READ | PROT_WRITE,2938MAP_SHARED, self->fd, data.out_mmap_offset);2939ASSERT_NE(MAP_FAILED, test);2940ASSERT_EQ(data.in_data, *test);29412942/* The owner of the mmap region should be blocked */2943EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, self->viommu_id));2944munmap(test, data.out_mmap_length);2945}29462947TEST_F(iommufd_viommu, vdevice_alloc)2948{2949uint32_t viommu_id = self->viommu_id;2950uint32_t dev_id = self->device_id;2951uint32_t vdev_id = 0;2952uint32_t veventq_id;2953uint32_t veventq_fd;2954int prev_seq = -1;29552956if (dev_id) {2957/* Must allocate vdevice before attaching to a nested hwpt */2958test_err_mock_domain_replace(ENOENT, self->stdev_id,2959self->nested_hwpt_id);29602961/* Allocate a vEVENTQ with veventq_depth=2 */2962test_cmd_veventq_alloc(viommu_id, IOMMU_VEVENTQ_TYPE_SELFTEST,2963&veventq_id, &veventq_fd);2964test_err_veventq_alloc(EEXIST, viommu_id,2965IOMMU_VEVENTQ_TYPE_SELFTEST, NULL, NULL);2966/* Set vdev_id to 0x99, unset it, and set to 0x88 */2967test_cmd_vdevice_alloc(viommu_id, dev_id, 0x99, &vdev_id);2968test_cmd_mock_domain_replace(self->stdev_id,2969self->nested_hwpt_id);2970test_cmd_trigger_vevents(dev_id, 1);2971test_cmd_read_vevents(veventq_fd, 1, 0x99, &prev_seq);2972test_err_vdevice_alloc(EEXIST, viommu_id, dev_id, 0x99,2973&vdev_id);2974test_cmd_mock_domain_replace(self->stdev_id, self->ioas_id);2975test_ioctl_destroy(vdev_id);29762977/* Try again with 0x88 */2978test_cmd_vdevice_alloc(viommu_id, dev_id, 0x88, &vdev_id);2979test_cmd_mock_domain_replace(self->stdev_id,2980self->nested_hwpt_id);2981/* Trigger an overflow with three events */2982test_cmd_trigger_vevents(dev_id, 3);2983test_err_read_vevents(EOVERFLOW, veventq_fd, 3, 0x88,2984&prev_seq);2985/* Overflow must be gone after the previous reads */2986test_cmd_trigger_vevents(dev_id, 1);2987test_cmd_read_vevents(veventq_fd, 1, 0x88, &prev_seq);2988close(veventq_fd);2989test_cmd_mock_domain_replace(self->stdev_id, self->ioas_id);2990test_ioctl_destroy(vdev_id);2991test_ioctl_destroy(veventq_id);2992} else {2993test_err_vdevice_alloc(ENOENT, viommu_id, dev_id, 0x99, NULL);2994}2995}29962997TEST_F(iommufd_viommu, vdevice_cache)2998{2999struct iommu_viommu_invalidate_selftest inv_reqs[2] = {};3000uint32_t viommu_id = self->viommu_id;3001uint32_t dev_id = self->device_id;3002uint32_t vdev_id = 0;3003uint32_t num_inv;30043005if (!dev_id)3006SKIP(return, "Skipping test for variant no_viommu");30073008test_cmd_vdevice_alloc(viommu_id, dev_id, 0x99, &vdev_id);30093010test_cmd_dev_check_cache_all(dev_id, IOMMU_TEST_DEV_CACHE_DEFAULT);30113012/* Check data_type by passing zero-length array */3013num_inv = 0;3014test_cmd_viommu_invalidate(viommu_id, inv_reqs, sizeof(*inv_reqs),3015&num_inv);3016assert(!num_inv);30173018/* Negative test: Invalid data_type */3019num_inv = 1;3020test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs,3021IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST_INVALID,3022sizeof(*inv_reqs), &num_inv);3023assert(!num_inv);30243025/* Negative test: structure size sanity */3026num_inv = 1;3027test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs,3028IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST,3029sizeof(*inv_reqs) + 1, &num_inv);3030assert(!num_inv);30313032num_inv = 1;3033test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs,3034IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, 1,3035&num_inv);3036assert(!num_inv);30373038/* Negative test: invalid flag is passed */3039num_inv = 1;3040inv_reqs[0].flags = 0xffffffff;3041inv_reqs[0].vdev_id = 0x99;3042test_err_viommu_invalidate(EOPNOTSUPP, viommu_id, inv_reqs,3043IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST,3044sizeof(*inv_reqs), &num_inv);3045assert(!num_inv);30463047/* Negative test: invalid data_uptr when array is not empty */3048num_inv = 1;3049inv_reqs[0].flags = 0;3050inv_reqs[0].vdev_id = 0x99;3051test_err_viommu_invalidate(EINVAL, viommu_id, NULL,3052IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST,3053sizeof(*inv_reqs), &num_inv);3054assert(!num_inv);30553056/* Negative test: invalid entry_len when array is not empty */3057num_inv = 1;3058inv_reqs[0].flags = 0;3059inv_reqs[0].vdev_id = 0x99;3060test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs,3061IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, 0,3062&num_inv);3063assert(!num_inv);30643065/* Negative test: invalid cache_id */3066num_inv = 1;3067inv_reqs[0].flags = 0;3068inv_reqs[0].vdev_id = 0x99;3069inv_reqs[0].cache_id = MOCK_DEV_CACHE_ID_MAX + 1;3070test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs,3071IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST,3072sizeof(*inv_reqs), &num_inv);3073assert(!num_inv);30743075/* Negative test: invalid vdev_id */3076num_inv = 1;3077inv_reqs[0].flags = 0;3078inv_reqs[0].vdev_id = 0x9;3079inv_reqs[0].cache_id = 0;3080test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs,3081IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST,3082sizeof(*inv_reqs), &num_inv);3083assert(!num_inv);30843085/*3086* Invalidate the 1st cache entry but fail the 2nd request3087* due to invalid flags configuration in the 2nd request.3088*/3089num_inv = 2;3090inv_reqs[0].flags = 0;3091inv_reqs[0].vdev_id = 0x99;3092inv_reqs[0].cache_id = 0;3093inv_reqs[1].flags = 0xffffffff;3094inv_reqs[1].vdev_id = 0x99;3095inv_reqs[1].cache_id = 1;3096test_err_viommu_invalidate(EOPNOTSUPP, viommu_id, inv_reqs,3097IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST,3098sizeof(*inv_reqs), &num_inv);3099assert(num_inv == 1);3100test_cmd_dev_check_cache(dev_id, 0, 0);3101test_cmd_dev_check_cache(dev_id, 1, IOMMU_TEST_DEV_CACHE_DEFAULT);3102test_cmd_dev_check_cache(dev_id, 2, IOMMU_TEST_DEV_CACHE_DEFAULT);3103test_cmd_dev_check_cache(dev_id, 3, IOMMU_TEST_DEV_CACHE_DEFAULT);31043105/*3106* Invalidate the 1st cache entry but fail the 2nd request3107* due to invalid cache_id configuration in the 2nd request.3108*/3109num_inv = 2;3110inv_reqs[0].flags = 0;3111inv_reqs[0].vdev_id = 0x99;3112inv_reqs[0].cache_id = 0;3113inv_reqs[1].flags = 0;3114inv_reqs[1].vdev_id = 0x99;3115inv_reqs[1].cache_id = MOCK_DEV_CACHE_ID_MAX + 1;3116test_err_viommu_invalidate(EINVAL, viommu_id, inv_reqs,3117IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST,3118sizeof(*inv_reqs), &num_inv);3119assert(num_inv == 1);3120test_cmd_dev_check_cache(dev_id, 0, 0);3121test_cmd_dev_check_cache(dev_id, 1, IOMMU_TEST_DEV_CACHE_DEFAULT);3122test_cmd_dev_check_cache(dev_id, 2, IOMMU_TEST_DEV_CACHE_DEFAULT);3123test_cmd_dev_check_cache(dev_id, 3, IOMMU_TEST_DEV_CACHE_DEFAULT);31243125/* Invalidate the 2nd cache entry and verify */3126num_inv = 1;3127inv_reqs[0].flags = 0;3128inv_reqs[0].vdev_id = 0x99;3129inv_reqs[0].cache_id = 1;3130test_cmd_viommu_invalidate(viommu_id, inv_reqs, sizeof(*inv_reqs),3131&num_inv);3132assert(num_inv == 1);3133test_cmd_dev_check_cache(dev_id, 0, 0);3134test_cmd_dev_check_cache(dev_id, 1, 0);3135test_cmd_dev_check_cache(dev_id, 2, IOMMU_TEST_DEV_CACHE_DEFAULT);3136test_cmd_dev_check_cache(dev_id, 3, IOMMU_TEST_DEV_CACHE_DEFAULT);31373138/* Invalidate the 3rd and 4th cache entries and verify */3139num_inv = 2;3140inv_reqs[0].flags = 0;3141inv_reqs[0].vdev_id = 0x99;3142inv_reqs[0].cache_id = 2;3143inv_reqs[1].flags = 0;3144inv_reqs[1].vdev_id = 0x99;3145inv_reqs[1].cache_id = 3;3146test_cmd_viommu_invalidate(viommu_id, inv_reqs, sizeof(*inv_reqs),3147&num_inv);3148assert(num_inv == 2);3149test_cmd_dev_check_cache_all(dev_id, 0);31503151/* Invalidate all cache entries for nested_dev_id[1] and verify */3152num_inv = 1;3153inv_reqs[0].vdev_id = 0x99;3154inv_reqs[0].flags = IOMMU_TEST_INVALIDATE_FLAG_ALL;3155test_cmd_viommu_invalidate(viommu_id, inv_reqs, sizeof(*inv_reqs),3156&num_inv);3157assert(num_inv == 1);3158test_cmd_dev_check_cache_all(dev_id, 0);3159test_ioctl_destroy(vdev_id);3160}31613162TEST_F(iommufd_viommu, hw_queue)3163{3164__u64 iova = MOCK_APERTURE_START, iova2;3165uint32_t viommu_id = self->viommu_id;3166uint32_t hw_queue_id[2];31673168if (!viommu_id)3169SKIP(return, "Skipping test for variant no_viommu");31703171/* Fail IOMMU_HW_QUEUE_TYPE_DEFAULT */3172test_err_hw_queue_alloc(EOPNOTSUPP, viommu_id,3173IOMMU_HW_QUEUE_TYPE_DEFAULT, 0, iova, PAGE_SIZE,3174&hw_queue_id[0]);3175/* Fail queue addr and length */3176test_err_hw_queue_alloc(EINVAL, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST,31770, iova, 0, &hw_queue_id[0]);3178test_err_hw_queue_alloc(EOVERFLOW, viommu_id,3179IOMMU_HW_QUEUE_TYPE_SELFTEST, 0, ~(uint64_t)0,3180PAGE_SIZE, &hw_queue_id[0]);3181/* Fail missing iova */3182test_err_hw_queue_alloc(ENOENT, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST,31830, iova, PAGE_SIZE, &hw_queue_id[0]);31843185/* Map iova */3186test_ioctl_ioas_map(buffer, PAGE_SIZE, &iova);3187test_ioctl_ioas_map(buffer + PAGE_SIZE, PAGE_SIZE, &iova2);31883189/* Fail index=1 and =MAX; must start from index=0 */3190test_err_hw_queue_alloc(EIO, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST, 1,3191iova, PAGE_SIZE, &hw_queue_id[0]);3192test_err_hw_queue_alloc(EINVAL, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST,3193IOMMU_TEST_HW_QUEUE_MAX, iova, PAGE_SIZE,3194&hw_queue_id[0]);31953196/* Allocate index=0, declare ownership of the iova */3197test_cmd_hw_queue_alloc(viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST, 0,3198iova, PAGE_SIZE, &hw_queue_id[0]);3199/* Fail duplicated index */3200test_err_hw_queue_alloc(EEXIST, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST,32010, iova, PAGE_SIZE, &hw_queue_id[0]);3202/* Fail unmap, due to iova ownership */3203test_err_ioctl_ioas_unmap(EBUSY, iova, PAGE_SIZE);3204/* The 2nd page is not pinned, so it can be unmmap */3205test_ioctl_ioas_unmap(iova2, PAGE_SIZE);32063207/* Allocate index=1, with an unaligned case */3208test_cmd_hw_queue_alloc(viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST, 1,3209iova + PAGE_SIZE / 2, PAGE_SIZE / 2,3210&hw_queue_id[1]);3211/* Fail to destroy, due to dependency */3212EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, hw_queue_id[0]));32133214/* Destroy in descending order */3215test_ioctl_destroy(hw_queue_id[1]);3216test_ioctl_destroy(hw_queue_id[0]);3217/* Now it can unmap the first page */3218test_ioctl_ioas_unmap(iova, PAGE_SIZE);3219}32203221TEST_F(iommufd_viommu, vdevice_tombstone)3222{3223uint32_t viommu_id = self->viommu_id;3224uint32_t dev_id = self->device_id;3225uint32_t vdev_id = 0;32263227if (!dev_id)3228SKIP(return, "Skipping test for variant no_viommu");32293230test_cmd_vdevice_alloc(viommu_id, dev_id, 0x99, &vdev_id);3231test_ioctl_destroy(self->stdev_id);3232EXPECT_ERRNO(ENOENT, _test_ioctl_destroy(self->fd, vdev_id));3233}32343235FIXTURE(iommufd_device_pasid)3236{3237int fd;3238uint32_t ioas_id;3239uint32_t hwpt_id;3240uint32_t stdev_id;3241uint32_t device_id;3242uint32_t no_pasid_stdev_id;3243uint32_t no_pasid_device_id;3244};32453246FIXTURE_VARIANT(iommufd_device_pasid)3247{3248bool pasid_capable;3249};32503251FIXTURE_SETUP(iommufd_device_pasid)3252{3253self->fd = open("/dev/iommu", O_RDWR);3254ASSERT_NE(-1, self->fd);3255test_ioctl_ioas_alloc(&self->ioas_id);32563257test_cmd_mock_domain_flags(self->ioas_id,3258MOCK_FLAGS_DEVICE_PASID,3259&self->stdev_id, &self->hwpt_id,3260&self->device_id);3261if (!variant->pasid_capable)3262test_cmd_mock_domain_flags(self->ioas_id, 0,3263&self->no_pasid_stdev_id, NULL,3264&self->no_pasid_device_id);3265}32663267FIXTURE_TEARDOWN(iommufd_device_pasid)3268{3269teardown_iommufd(self->fd, _metadata);3270}32713272FIXTURE_VARIANT_ADD(iommufd_device_pasid, no_pasid)3273{3274.pasid_capable = false,3275};32763277FIXTURE_VARIANT_ADD(iommufd_device_pasid, has_pasid)3278{3279.pasid_capable = true,3280};32813282TEST_F(iommufd_device_pasid, pasid_attach)3283{3284struct iommu_hwpt_selftest data = {3285.iotlb = IOMMU_TEST_IOTLB_DEFAULT,3286};3287uint32_t nested_hwpt_id[3] = {};3288uint32_t parent_hwpt_id = 0;3289uint32_t fault_id, fault_fd;3290uint32_t s2_hwpt_id = 0;3291uint32_t iopf_hwpt_id;3292uint32_t pasid = 100;3293uint32_t viommu_id;32943295/*3296* Negative, detach pasid without attaching, this is not expected.3297* But it should not result in failure anyway.3298*/3299test_cmd_pasid_detach(pasid);33003301/* Allocate two nested hwpts sharing one common parent hwpt */3302test_cmd_hwpt_alloc(self->device_id, self->ioas_id,3303IOMMU_HWPT_ALLOC_NEST_PARENT,3304&parent_hwpt_id);3305test_cmd_hwpt_alloc_nested(self->device_id, parent_hwpt_id,3306IOMMU_HWPT_ALLOC_PASID,3307&nested_hwpt_id[0],3308IOMMU_HWPT_DATA_SELFTEST,3309&data, sizeof(data));3310test_cmd_hwpt_alloc_nested(self->device_id, parent_hwpt_id,3311IOMMU_HWPT_ALLOC_PASID,3312&nested_hwpt_id[1],3313IOMMU_HWPT_DATA_SELFTEST,3314&data, sizeof(data));33153316/* Fault related preparation */3317test_ioctl_fault_alloc(&fault_id, &fault_fd);3318test_cmd_hwpt_alloc_iopf(self->device_id, parent_hwpt_id, fault_id,3319IOMMU_HWPT_FAULT_ID_VALID | IOMMU_HWPT_ALLOC_PASID,3320&iopf_hwpt_id,3321IOMMU_HWPT_DATA_SELFTEST, &data,3322sizeof(data));33233324/* Allocate a regular nested hwpt based on viommu */3325test_cmd_viommu_alloc(self->device_id, parent_hwpt_id,3326IOMMU_VIOMMU_TYPE_SELFTEST, NULL, 0, &viommu_id);3327test_cmd_hwpt_alloc_nested(self->device_id, viommu_id,3328IOMMU_HWPT_ALLOC_PASID,3329&nested_hwpt_id[2],3330IOMMU_HWPT_DATA_SELFTEST, &data,3331sizeof(data));33323333test_cmd_hwpt_alloc(self->device_id, self->ioas_id,3334IOMMU_HWPT_ALLOC_PASID,3335&s2_hwpt_id);33363337/* Attach RID to non-pasid compat domain, */3338test_cmd_mock_domain_replace(self->stdev_id, parent_hwpt_id);3339/* then attach to pasid should fail */3340test_err_pasid_attach(EINVAL, pasid, s2_hwpt_id);33413342/* Attach RID to pasid compat domain, */3343test_cmd_mock_domain_replace(self->stdev_id, s2_hwpt_id);3344/* then attach to pasid should succeed, */3345test_cmd_pasid_attach(pasid, nested_hwpt_id[0]);3346/* but attach RID to non-pasid compat domain should fail now. */3347test_err_mock_domain_replace(EINVAL, self->stdev_id, parent_hwpt_id);3348/*3349* Detach hwpt from pasid 100, and check if the pasid 1003350* has null domain.3351*/3352test_cmd_pasid_detach(pasid);3353ASSERT_EQ(0,3354test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3355pasid, 0));3356/* RID is attached to pasid-comapt domain, pasid path is not used */33573358if (!variant->pasid_capable) {3359/*3360* PASID-compatible domain can be used by non-PASID-capable3361* device.3362*/3363test_cmd_mock_domain_replace(self->no_pasid_stdev_id, nested_hwpt_id[0]);3364test_cmd_mock_domain_replace(self->no_pasid_stdev_id, self->ioas_id);3365/*3366* Attach hwpt to pasid 100 of non-PASID-capable device,3367* should fail, no matter domain is pasid-comapt or not.3368*/3369EXPECT_ERRNO(EINVAL,3370_test_cmd_pasid_attach(self->fd, self->no_pasid_stdev_id,3371pasid, parent_hwpt_id));3372EXPECT_ERRNO(EINVAL,3373_test_cmd_pasid_attach(self->fd, self->no_pasid_stdev_id,3374pasid, s2_hwpt_id));3375}33763377/*3378* Attach non pasid compat hwpt to pasid-capable device, should3379* fail, and have null domain.3380*/3381test_err_pasid_attach(EINVAL, pasid, parent_hwpt_id);3382ASSERT_EQ(0,3383test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3384pasid, 0));33853386/*3387* Attach ioas to pasid 100, should fail, domain should3388* be null.3389*/3390test_err_pasid_attach(EINVAL, pasid, self->ioas_id);3391ASSERT_EQ(0,3392test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3393pasid, 0));33943395/*3396* Attach the s2_hwpt to pasid 100, should succeed, domain should3397* be valid.3398*/3399test_cmd_pasid_attach(pasid, s2_hwpt_id);3400ASSERT_EQ(0,3401test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3402pasid, s2_hwpt_id));34033404/*3405* Try attach pasid 100 with another hwpt, should FAIL3406* as attach does not allow overwrite, use REPLACE instead.3407*/3408test_err_pasid_attach(EBUSY, pasid, nested_hwpt_id[0]);34093410/*3411* Detach hwpt from pasid 100 for next test, should succeed,3412* and have null domain.3413*/3414test_cmd_pasid_detach(pasid);3415ASSERT_EQ(0,3416test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3417pasid, 0));34183419/*3420* Attach nested hwpt to pasid 100, should succeed, domain3421* should be valid.3422*/3423test_cmd_pasid_attach(pasid, nested_hwpt_id[0]);3424ASSERT_EQ(0,3425test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3426pasid, nested_hwpt_id[0]));34273428/* Attach to pasid 100 which has been attached, should fail. */3429test_err_pasid_attach(EBUSY, pasid, nested_hwpt_id[0]);34303431/* cleanup pasid 100 */3432test_cmd_pasid_detach(pasid);34333434/* Replace tests */34353436pasid = 200;3437/*3438* Replace pasid 200 without attaching it, should fail3439* with -EINVAL.3440*/3441test_err_pasid_replace(EINVAL, pasid, s2_hwpt_id);34423443/*3444* Attach the s2 hwpt to pasid 200, should succeed, domain should3445* be valid.3446*/3447test_cmd_pasid_attach(pasid, s2_hwpt_id);3448ASSERT_EQ(0,3449test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3450pasid, s2_hwpt_id));34513452/*3453* Replace pasid 200 with self->ioas_id, should fail3454* and domain should be the prior s2 hwpt.3455*/3456test_err_pasid_replace(EINVAL, pasid, self->ioas_id);3457ASSERT_EQ(0,3458test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3459pasid, s2_hwpt_id));34603461/*3462* Replace a nested hwpt for pasid 200, should succeed,3463* and have valid domain.3464*/3465test_cmd_pasid_replace(pasid, nested_hwpt_id[0]);3466ASSERT_EQ(0,3467test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3468pasid, nested_hwpt_id[0]));34693470/*3471* Replace with another nested hwpt for pasid 200, should3472* succeed, and have valid domain.3473*/3474test_cmd_pasid_replace(pasid, nested_hwpt_id[1]);3475ASSERT_EQ(0,3476test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3477pasid, nested_hwpt_id[1]));34783479/* cleanup pasid 200 */3480test_cmd_pasid_detach(pasid);34813482/* Negative Tests for pasid replace, use pasid 1024 */34833484/*3485* Attach the s2 hwpt to pasid 1024, should succeed, domain should3486* be valid.3487*/3488pasid = 1024;3489test_cmd_pasid_attach(pasid, s2_hwpt_id);3490ASSERT_EQ(0,3491test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3492pasid, s2_hwpt_id));34933494/*3495* Replace pasid 1024 with nested_hwpt_id[0], should fail,3496* but have the old valid domain. This is a designed3497* negative case. Normally, this shall succeed.3498*/3499test_err_pasid_replace(ENOMEM, pasid, nested_hwpt_id[0]);3500ASSERT_EQ(0,3501test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3502pasid, s2_hwpt_id));35033504/* cleanup pasid 1024 */3505test_cmd_pasid_detach(pasid);35063507/* Attach to iopf-capable hwpt */35083509/*3510* Attach an iopf hwpt to pasid 2048, should succeed, domain should3511* be valid.3512*/3513pasid = 2048;3514test_cmd_pasid_attach(pasid, iopf_hwpt_id);3515ASSERT_EQ(0,3516test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3517pasid, iopf_hwpt_id));35183519test_cmd_trigger_iopf_pasid(self->device_id, pasid, fault_fd);35203521/*3522* Replace with s2_hwpt_id for pasid 2048, should3523* succeed, and have valid domain.3524*/3525test_cmd_pasid_replace(pasid, s2_hwpt_id);3526ASSERT_EQ(0,3527test_cmd_pasid_check_hwpt(self->fd, self->stdev_id,3528pasid, s2_hwpt_id));35293530/* cleanup pasid 2048 */3531test_cmd_pasid_detach(pasid);35323533test_ioctl_destroy(iopf_hwpt_id);3534close(fault_fd);3535test_ioctl_destroy(fault_id);35363537/* Detach the s2_hwpt_id from RID */3538test_cmd_mock_domain_replace(self->stdev_id, self->ioas_id);3539}35403541TEST_HARNESS_MAIN354235433544