162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/interval_tree.h> 662306a36Sopenharmony_ci#include <linux/iommufd.h> 762306a36Sopenharmony_ci#include <linux/iommu.h> 862306a36Sopenharmony_ci#include <uapi/linux/iommufd.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "io_pagetable.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_civoid iommufd_ioas_destroy(struct iommufd_object *obj) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci struct iommufd_ioas *ioas = container_of(obj, struct iommufd_ioas, obj); 1562306a36Sopenharmony_ci int rc; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci rc = iopt_unmap_all(&ioas->iopt, NULL); 1862306a36Sopenharmony_ci WARN_ON(rc && rc != -ENOENT); 1962306a36Sopenharmony_ci iopt_destroy_table(&ioas->iopt); 2062306a36Sopenharmony_ci mutex_destroy(&ioas->mutex); 2162306a36Sopenharmony_ci} 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct iommufd_ioas *iommufd_ioas_alloc(struct iommufd_ctx *ictx) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct iommufd_ioas *ioas; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci ioas = iommufd_object_alloc(ictx, ioas, IOMMUFD_OBJ_IOAS); 2862306a36Sopenharmony_ci if (IS_ERR(ioas)) 2962306a36Sopenharmony_ci return ioas; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci iopt_init_table(&ioas->iopt); 3262306a36Sopenharmony_ci INIT_LIST_HEAD(&ioas->hwpt_list); 3362306a36Sopenharmony_ci mutex_init(&ioas->mutex); 3462306a36Sopenharmony_ci return ioas; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ciint iommufd_ioas_alloc_ioctl(struct iommufd_ucmd *ucmd) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct iommu_ioas_alloc *cmd = ucmd->cmd; 4062306a36Sopenharmony_ci struct iommufd_ioas *ioas; 4162306a36Sopenharmony_ci int rc; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (cmd->flags) 4462306a36Sopenharmony_ci return -EOPNOTSUPP; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci ioas = iommufd_ioas_alloc(ucmd->ictx); 4762306a36Sopenharmony_ci if (IS_ERR(ioas)) 4862306a36Sopenharmony_ci return PTR_ERR(ioas); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci cmd->out_ioas_id = ioas->obj.id; 5162306a36Sopenharmony_ci rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd)); 5262306a36Sopenharmony_ci if (rc) 5362306a36Sopenharmony_ci goto out_table; 5462306a36Sopenharmony_ci iommufd_object_finalize(ucmd->ictx, &ioas->obj); 5562306a36Sopenharmony_ci return 0; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ciout_table: 5862306a36Sopenharmony_ci iommufd_object_abort_and_destroy(ucmd->ictx, &ioas->obj); 5962306a36Sopenharmony_ci return rc; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ciint iommufd_ioas_iova_ranges(struct iommufd_ucmd *ucmd) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct iommu_iova_range __user *ranges; 6562306a36Sopenharmony_ci struct iommu_ioas_iova_ranges *cmd = ucmd->cmd; 6662306a36Sopenharmony_ci struct iommufd_ioas *ioas; 6762306a36Sopenharmony_ci struct interval_tree_span_iter span; 6862306a36Sopenharmony_ci u32 max_iovas; 6962306a36Sopenharmony_ci int rc; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (cmd->__reserved) 7262306a36Sopenharmony_ci return -EOPNOTSUPP; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci ioas = iommufd_get_ioas(ucmd->ictx, cmd->ioas_id); 7562306a36Sopenharmony_ci if (IS_ERR(ioas)) 7662306a36Sopenharmony_ci return PTR_ERR(ioas); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci down_read(&ioas->iopt.iova_rwsem); 7962306a36Sopenharmony_ci max_iovas = cmd->num_iovas; 8062306a36Sopenharmony_ci ranges = u64_to_user_ptr(cmd->allowed_iovas); 8162306a36Sopenharmony_ci cmd->num_iovas = 0; 8262306a36Sopenharmony_ci cmd->out_iova_alignment = ioas->iopt.iova_alignment; 8362306a36Sopenharmony_ci interval_tree_for_each_span(&span, &ioas->iopt.reserved_itree, 0, 8462306a36Sopenharmony_ci ULONG_MAX) { 8562306a36Sopenharmony_ci if (!span.is_hole) 8662306a36Sopenharmony_ci continue; 8762306a36Sopenharmony_ci if (cmd->num_iovas < max_iovas) { 8862306a36Sopenharmony_ci struct iommu_iova_range elm = { 8962306a36Sopenharmony_ci .start = span.start_hole, 9062306a36Sopenharmony_ci .last = span.last_hole, 9162306a36Sopenharmony_ci }; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (copy_to_user(&ranges[cmd->num_iovas], &elm, 9462306a36Sopenharmony_ci sizeof(elm))) { 9562306a36Sopenharmony_ci rc = -EFAULT; 9662306a36Sopenharmony_ci goto out_put; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci cmd->num_iovas++; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd)); 10262306a36Sopenharmony_ci if (rc) 10362306a36Sopenharmony_ci goto out_put; 10462306a36Sopenharmony_ci if (cmd->num_iovas > max_iovas) 10562306a36Sopenharmony_ci rc = -EMSGSIZE; 10662306a36Sopenharmony_ciout_put: 10762306a36Sopenharmony_ci up_read(&ioas->iopt.iova_rwsem); 10862306a36Sopenharmony_ci iommufd_put_object(&ioas->obj); 10962306a36Sopenharmony_ci return rc; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int iommufd_ioas_load_iovas(struct rb_root_cached *itree, 11362306a36Sopenharmony_ci struct iommu_iova_range __user *ranges, 11462306a36Sopenharmony_ci u32 num) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci u32 i; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci for (i = 0; i != num; i++) { 11962306a36Sopenharmony_ci struct iommu_iova_range range; 12062306a36Sopenharmony_ci struct iopt_allowed *allowed; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (copy_from_user(&range, ranges + i, sizeof(range))) 12362306a36Sopenharmony_ci return -EFAULT; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (range.start >= range.last) 12662306a36Sopenharmony_ci return -EINVAL; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (interval_tree_iter_first(itree, range.start, range.last)) 12962306a36Sopenharmony_ci return -EINVAL; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci allowed = kzalloc(sizeof(*allowed), GFP_KERNEL_ACCOUNT); 13262306a36Sopenharmony_ci if (!allowed) 13362306a36Sopenharmony_ci return -ENOMEM; 13462306a36Sopenharmony_ci allowed->node.start = range.start; 13562306a36Sopenharmony_ci allowed->node.last = range.last; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci interval_tree_insert(&allowed->node, itree); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ciint iommufd_ioas_allow_iovas(struct iommufd_ucmd *ucmd) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct iommu_ioas_allow_iovas *cmd = ucmd->cmd; 14562306a36Sopenharmony_ci struct rb_root_cached allowed_iova = RB_ROOT_CACHED; 14662306a36Sopenharmony_ci struct interval_tree_node *node; 14762306a36Sopenharmony_ci struct iommufd_ioas *ioas; 14862306a36Sopenharmony_ci struct io_pagetable *iopt; 14962306a36Sopenharmony_ci int rc = 0; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (cmd->__reserved) 15262306a36Sopenharmony_ci return -EOPNOTSUPP; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci ioas = iommufd_get_ioas(ucmd->ictx, cmd->ioas_id); 15562306a36Sopenharmony_ci if (IS_ERR(ioas)) 15662306a36Sopenharmony_ci return PTR_ERR(ioas); 15762306a36Sopenharmony_ci iopt = &ioas->iopt; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci rc = iommufd_ioas_load_iovas(&allowed_iova, 16062306a36Sopenharmony_ci u64_to_user_ptr(cmd->allowed_iovas), 16162306a36Sopenharmony_ci cmd->num_iovas); 16262306a36Sopenharmony_ci if (rc) 16362306a36Sopenharmony_ci goto out_free; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* 16662306a36Sopenharmony_ci * We want the allowed tree update to be atomic, so we have to keep the 16762306a36Sopenharmony_ci * original nodes around, and keep track of the new nodes as we allocate 16862306a36Sopenharmony_ci * memory for them. The simplest solution is to have a new/old tree and 16962306a36Sopenharmony_ci * then swap new for old. On success we free the old tree, on failure we 17062306a36Sopenharmony_ci * free the new tree. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci rc = iopt_set_allow_iova(iopt, &allowed_iova); 17362306a36Sopenharmony_ciout_free: 17462306a36Sopenharmony_ci while ((node = interval_tree_iter_first(&allowed_iova, 0, ULONG_MAX))) { 17562306a36Sopenharmony_ci interval_tree_remove(node, &allowed_iova); 17662306a36Sopenharmony_ci kfree(container_of(node, struct iopt_allowed, node)); 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci iommufd_put_object(&ioas->obj); 17962306a36Sopenharmony_ci return rc; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int conv_iommu_prot(u32 map_flags) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci /* 18562306a36Sopenharmony_ci * We provide no manual cache coherency ioctls to userspace and most 18662306a36Sopenharmony_ci * architectures make the CPU ops for cache flushing privileged. 18762306a36Sopenharmony_ci * Therefore we require the underlying IOMMU to support CPU coherent 18862306a36Sopenharmony_ci * operation. Support for IOMMU_CACHE is enforced by the 18962306a36Sopenharmony_ci * IOMMU_CAP_CACHE_COHERENCY test during bind. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ci int iommu_prot = IOMMU_CACHE; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (map_flags & IOMMU_IOAS_MAP_WRITEABLE) 19462306a36Sopenharmony_ci iommu_prot |= IOMMU_WRITE; 19562306a36Sopenharmony_ci if (map_flags & IOMMU_IOAS_MAP_READABLE) 19662306a36Sopenharmony_ci iommu_prot |= IOMMU_READ; 19762306a36Sopenharmony_ci return iommu_prot; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ciint iommufd_ioas_map(struct iommufd_ucmd *ucmd) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct iommu_ioas_map *cmd = ucmd->cmd; 20362306a36Sopenharmony_ci unsigned long iova = cmd->iova; 20462306a36Sopenharmony_ci struct iommufd_ioas *ioas; 20562306a36Sopenharmony_ci unsigned int flags = 0; 20662306a36Sopenharmony_ci int rc; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if ((cmd->flags & 20962306a36Sopenharmony_ci ~(IOMMU_IOAS_MAP_FIXED_IOVA | IOMMU_IOAS_MAP_WRITEABLE | 21062306a36Sopenharmony_ci IOMMU_IOAS_MAP_READABLE)) || 21162306a36Sopenharmony_ci cmd->__reserved) 21262306a36Sopenharmony_ci return -EOPNOTSUPP; 21362306a36Sopenharmony_ci if (cmd->iova >= ULONG_MAX || cmd->length >= ULONG_MAX) 21462306a36Sopenharmony_ci return -EOVERFLOW; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci ioas = iommufd_get_ioas(ucmd->ictx, cmd->ioas_id); 21762306a36Sopenharmony_ci if (IS_ERR(ioas)) 21862306a36Sopenharmony_ci return PTR_ERR(ioas); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (!(cmd->flags & IOMMU_IOAS_MAP_FIXED_IOVA)) 22162306a36Sopenharmony_ci flags = IOPT_ALLOC_IOVA; 22262306a36Sopenharmony_ci rc = iopt_map_user_pages(ucmd->ictx, &ioas->iopt, &iova, 22362306a36Sopenharmony_ci u64_to_user_ptr(cmd->user_va), cmd->length, 22462306a36Sopenharmony_ci conv_iommu_prot(cmd->flags), flags); 22562306a36Sopenharmony_ci if (rc) 22662306a36Sopenharmony_ci goto out_put; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci cmd->iova = iova; 22962306a36Sopenharmony_ci rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd)); 23062306a36Sopenharmony_ciout_put: 23162306a36Sopenharmony_ci iommufd_put_object(&ioas->obj); 23262306a36Sopenharmony_ci return rc; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ciint iommufd_ioas_copy(struct iommufd_ucmd *ucmd) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct iommu_ioas_copy *cmd = ucmd->cmd; 23862306a36Sopenharmony_ci struct iommufd_ioas *src_ioas; 23962306a36Sopenharmony_ci struct iommufd_ioas *dst_ioas; 24062306a36Sopenharmony_ci unsigned int flags = 0; 24162306a36Sopenharmony_ci LIST_HEAD(pages_list); 24262306a36Sopenharmony_ci unsigned long iova; 24362306a36Sopenharmony_ci int rc; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci iommufd_test_syz_conv_iova_id(ucmd, cmd->src_ioas_id, &cmd->src_iova, 24662306a36Sopenharmony_ci &cmd->flags); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if ((cmd->flags & 24962306a36Sopenharmony_ci ~(IOMMU_IOAS_MAP_FIXED_IOVA | IOMMU_IOAS_MAP_WRITEABLE | 25062306a36Sopenharmony_ci IOMMU_IOAS_MAP_READABLE))) 25162306a36Sopenharmony_ci return -EOPNOTSUPP; 25262306a36Sopenharmony_ci if (cmd->length >= ULONG_MAX || cmd->src_iova >= ULONG_MAX || 25362306a36Sopenharmony_ci cmd->dst_iova >= ULONG_MAX) 25462306a36Sopenharmony_ci return -EOVERFLOW; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci src_ioas = iommufd_get_ioas(ucmd->ictx, cmd->src_ioas_id); 25762306a36Sopenharmony_ci if (IS_ERR(src_ioas)) 25862306a36Sopenharmony_ci return PTR_ERR(src_ioas); 25962306a36Sopenharmony_ci rc = iopt_get_pages(&src_ioas->iopt, cmd->src_iova, cmd->length, 26062306a36Sopenharmony_ci &pages_list); 26162306a36Sopenharmony_ci iommufd_put_object(&src_ioas->obj); 26262306a36Sopenharmony_ci if (rc) 26362306a36Sopenharmony_ci return rc; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci dst_ioas = iommufd_get_ioas(ucmd->ictx, cmd->dst_ioas_id); 26662306a36Sopenharmony_ci if (IS_ERR(dst_ioas)) { 26762306a36Sopenharmony_ci rc = PTR_ERR(dst_ioas); 26862306a36Sopenharmony_ci goto out_pages; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (!(cmd->flags & IOMMU_IOAS_MAP_FIXED_IOVA)) 27262306a36Sopenharmony_ci flags = IOPT_ALLOC_IOVA; 27362306a36Sopenharmony_ci iova = cmd->dst_iova; 27462306a36Sopenharmony_ci rc = iopt_map_pages(&dst_ioas->iopt, &pages_list, cmd->length, &iova, 27562306a36Sopenharmony_ci conv_iommu_prot(cmd->flags), flags); 27662306a36Sopenharmony_ci if (rc) 27762306a36Sopenharmony_ci goto out_put_dst; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci cmd->dst_iova = iova; 28062306a36Sopenharmony_ci rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd)); 28162306a36Sopenharmony_ciout_put_dst: 28262306a36Sopenharmony_ci iommufd_put_object(&dst_ioas->obj); 28362306a36Sopenharmony_ciout_pages: 28462306a36Sopenharmony_ci iopt_free_pages_list(&pages_list); 28562306a36Sopenharmony_ci return rc; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ciint iommufd_ioas_unmap(struct iommufd_ucmd *ucmd) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct iommu_ioas_unmap *cmd = ucmd->cmd; 29162306a36Sopenharmony_ci struct iommufd_ioas *ioas; 29262306a36Sopenharmony_ci unsigned long unmapped = 0; 29362306a36Sopenharmony_ci int rc; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ioas = iommufd_get_ioas(ucmd->ictx, cmd->ioas_id); 29662306a36Sopenharmony_ci if (IS_ERR(ioas)) 29762306a36Sopenharmony_ci return PTR_ERR(ioas); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (cmd->iova == 0 && cmd->length == U64_MAX) { 30062306a36Sopenharmony_ci rc = iopt_unmap_all(&ioas->iopt, &unmapped); 30162306a36Sopenharmony_ci if (rc) 30262306a36Sopenharmony_ci goto out_put; 30362306a36Sopenharmony_ci } else { 30462306a36Sopenharmony_ci if (cmd->iova >= ULONG_MAX || cmd->length >= ULONG_MAX) { 30562306a36Sopenharmony_ci rc = -EOVERFLOW; 30662306a36Sopenharmony_ci goto out_put; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci rc = iopt_unmap_iova(&ioas->iopt, cmd->iova, cmd->length, 30962306a36Sopenharmony_ci &unmapped); 31062306a36Sopenharmony_ci if (rc) 31162306a36Sopenharmony_ci goto out_put; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci cmd->length = unmapped; 31562306a36Sopenharmony_ci rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd)); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ciout_put: 31862306a36Sopenharmony_ci iommufd_put_object(&ioas->obj); 31962306a36Sopenharmony_ci return rc; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ciint iommufd_option_rlimit_mode(struct iommu_option *cmd, 32362306a36Sopenharmony_ci struct iommufd_ctx *ictx) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci if (cmd->object_id) 32662306a36Sopenharmony_ci return -EOPNOTSUPP; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (cmd->op == IOMMU_OPTION_OP_GET) { 32962306a36Sopenharmony_ci cmd->val64 = ictx->account_mode == IOPT_PAGES_ACCOUNT_MM; 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci if (cmd->op == IOMMU_OPTION_OP_SET) { 33362306a36Sopenharmony_ci int rc = 0; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (!capable(CAP_SYS_RESOURCE)) 33662306a36Sopenharmony_ci return -EPERM; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci xa_lock(&ictx->objects); 33962306a36Sopenharmony_ci if (!xa_empty(&ictx->objects)) { 34062306a36Sopenharmony_ci rc = -EBUSY; 34162306a36Sopenharmony_ci } else { 34262306a36Sopenharmony_ci if (cmd->val64 == 0) 34362306a36Sopenharmony_ci ictx->account_mode = IOPT_PAGES_ACCOUNT_USER; 34462306a36Sopenharmony_ci else if (cmd->val64 == 1) 34562306a36Sopenharmony_ci ictx->account_mode = IOPT_PAGES_ACCOUNT_MM; 34662306a36Sopenharmony_ci else 34762306a36Sopenharmony_ci rc = -EINVAL; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci xa_unlock(&ictx->objects); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return rc; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci return -EOPNOTSUPP; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int iommufd_ioas_option_huge_pages(struct iommu_option *cmd, 35762306a36Sopenharmony_ci struct iommufd_ioas *ioas) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci if (cmd->op == IOMMU_OPTION_OP_GET) { 36062306a36Sopenharmony_ci cmd->val64 = !ioas->iopt.disable_large_pages; 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci if (cmd->op == IOMMU_OPTION_OP_SET) { 36462306a36Sopenharmony_ci if (cmd->val64 == 0) 36562306a36Sopenharmony_ci return iopt_disable_large_pages(&ioas->iopt); 36662306a36Sopenharmony_ci if (cmd->val64 == 1) { 36762306a36Sopenharmony_ci iopt_enable_large_pages(&ioas->iopt); 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci return -EINVAL; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci return -EOPNOTSUPP; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ciint iommufd_ioas_option(struct iommufd_ucmd *ucmd) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct iommu_option *cmd = ucmd->cmd; 37862306a36Sopenharmony_ci struct iommufd_ioas *ioas; 37962306a36Sopenharmony_ci int rc = 0; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (cmd->__reserved) 38262306a36Sopenharmony_ci return -EOPNOTSUPP; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci ioas = iommufd_get_ioas(ucmd->ictx, cmd->object_id); 38562306a36Sopenharmony_ci if (IS_ERR(ioas)) 38662306a36Sopenharmony_ci return PTR_ERR(ioas); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci switch (cmd->option_id) { 38962306a36Sopenharmony_ci case IOMMU_OPTION_HUGE_PAGES: 39062306a36Sopenharmony_ci rc = iommufd_ioas_option_huge_pages(cmd, ioas); 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci default: 39362306a36Sopenharmony_ci rc = -EOPNOTSUPP; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci iommufd_put_object(&ioas->obj); 39762306a36Sopenharmony_ci return rc; 39862306a36Sopenharmony_ci} 399