1d94d71cbSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2f31e65e1SBenjamin Herrenschmidt /* 3f31e65e1SBenjamin Herrenschmidt * 4f31e65e1SBenjamin Herrenschmidt * Copyright 2010 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com> 5f31e65e1SBenjamin Herrenschmidt * Copyright 2011 David Gibson, IBM Corporation <dwg@au1.ibm.com> 6d3695aa4SAlexey Kardashevskiy * Copyright 2016 Alexey Kardashevskiy, IBM Corporation <aik@au1.ibm.com> 7f31e65e1SBenjamin Herrenschmidt */ 8f31e65e1SBenjamin Herrenschmidt 9f31e65e1SBenjamin Herrenschmidt #include <linux/types.h> 10f31e65e1SBenjamin Herrenschmidt #include <linux/string.h> 11f31e65e1SBenjamin Herrenschmidt #include <linux/kvm.h> 12f31e65e1SBenjamin Herrenschmidt #include <linux/kvm_host.h> 13f31e65e1SBenjamin Herrenschmidt #include <linux/highmem.h> 14f31e65e1SBenjamin Herrenschmidt #include <linux/gfp.h> 15f31e65e1SBenjamin Herrenschmidt #include <linux/slab.h> 163f07c014SIngo Molnar #include <linux/sched/signal.h> 17f31e65e1SBenjamin Herrenschmidt #include <linux/hugetlb.h> 18f31e65e1SBenjamin Herrenschmidt #include <linux/list.h> 19f31e65e1SBenjamin Herrenschmidt #include <linux/anon_inodes.h> 20121f80baSAlexey Kardashevskiy #include <linux/iommu.h> 21121f80baSAlexey Kardashevskiy #include <linux/file.h> 22*79eb597cSDaniel Jordan #include <linux/mm.h> 23f31e65e1SBenjamin Herrenschmidt 24f31e65e1SBenjamin Herrenschmidt #include <asm/kvm_ppc.h> 25f31e65e1SBenjamin Herrenschmidt #include <asm/kvm_book3s.h> 26f64e8084SAneesh Kumar K.V #include <asm/book3s/64/mmu-hash.h> 27f31e65e1SBenjamin Herrenschmidt #include <asm/hvcall.h> 28f31e65e1SBenjamin Herrenschmidt #include <asm/synch.h> 29f31e65e1SBenjamin Herrenschmidt #include <asm/ppc-opcode.h> 30f31e65e1SBenjamin Herrenschmidt #include <asm/kvm_host.h> 31f31e65e1SBenjamin Herrenschmidt #include <asm/udbg.h> 32462ee11eSAlexey Kardashevskiy #include <asm/iommu.h> 33d3695aa4SAlexey Kardashevskiy #include <asm/tce.h> 34121f80baSAlexey Kardashevskiy #include <asm/mmu_context.h> 35f31e65e1SBenjamin Herrenschmidt 36fe26e527SAlexey Kardashevskiy static unsigned long kvmppc_tce_pages(unsigned long iommu_pages) 37f31e65e1SBenjamin Herrenschmidt { 38fe26e527SAlexey Kardashevskiy return ALIGN(iommu_pages * sizeof(u64), PAGE_SIZE) / PAGE_SIZE; 39f31e65e1SBenjamin Herrenschmidt } 40f31e65e1SBenjamin Herrenschmidt 41f8626985SAlexey Kardashevskiy static unsigned long kvmppc_stt_pages(unsigned long tce_pages) 42f8626985SAlexey Kardashevskiy { 43f8626985SAlexey Kardashevskiy unsigned long stt_bytes = sizeof(struct kvmppc_spapr_tce_table) + 44f8626985SAlexey Kardashevskiy (tce_pages * sizeof(struct page *)); 45f8626985SAlexey Kardashevskiy 46f8626985SAlexey Kardashevskiy return tce_pages + ALIGN(stt_bytes, PAGE_SIZE) / PAGE_SIZE; 47f8626985SAlexey Kardashevskiy } 48f8626985SAlexey Kardashevskiy 49121f80baSAlexey Kardashevskiy static void kvm_spapr_tce_iommu_table_free(struct rcu_head *head) 50121f80baSAlexey Kardashevskiy { 51121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit = container_of(head, 52121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table, rcu); 53121f80baSAlexey Kardashevskiy 54121f80baSAlexey Kardashevskiy iommu_tce_table_put(stit->tbl); 55121f80baSAlexey Kardashevskiy 56121f80baSAlexey Kardashevskiy kfree(stit); 57121f80baSAlexey Kardashevskiy } 58121f80baSAlexey Kardashevskiy 59121f80baSAlexey Kardashevskiy static void kvm_spapr_tce_liobn_put(struct kref *kref) 60121f80baSAlexey Kardashevskiy { 61121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit = container_of(kref, 62121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table, kref); 63121f80baSAlexey Kardashevskiy 64121f80baSAlexey Kardashevskiy list_del_rcu(&stit->next); 65121f80baSAlexey Kardashevskiy 66121f80baSAlexey Kardashevskiy call_rcu(&stit->rcu, kvm_spapr_tce_iommu_table_free); 67121f80baSAlexey Kardashevskiy } 68121f80baSAlexey Kardashevskiy 69121f80baSAlexey Kardashevskiy extern void kvm_spapr_tce_release_iommu_group(struct kvm *kvm, 70121f80baSAlexey Kardashevskiy struct iommu_group *grp) 71121f80baSAlexey Kardashevskiy { 72121f80baSAlexey Kardashevskiy int i; 73121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt; 74121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit, *tmp; 75121f80baSAlexey Kardashevskiy struct iommu_table_group *table_group = NULL; 76121f80baSAlexey Kardashevskiy 77121f80baSAlexey Kardashevskiy list_for_each_entry_rcu(stt, &kvm->arch.spapr_tce_tables, list) { 78121f80baSAlexey Kardashevskiy 79121f80baSAlexey Kardashevskiy table_group = iommu_group_get_iommudata(grp); 80121f80baSAlexey Kardashevskiy if (WARN_ON(!table_group)) 81121f80baSAlexey Kardashevskiy continue; 82121f80baSAlexey Kardashevskiy 83121f80baSAlexey Kardashevskiy list_for_each_entry_safe(stit, tmp, &stt->iommu_tables, next) { 84121f80baSAlexey Kardashevskiy for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) { 85121f80baSAlexey Kardashevskiy if (table_group->tables[i] != stit->tbl) 86121f80baSAlexey Kardashevskiy continue; 87121f80baSAlexey Kardashevskiy 88121f80baSAlexey Kardashevskiy kref_put(&stit->kref, kvm_spapr_tce_liobn_put); 89121f80baSAlexey Kardashevskiy } 90121f80baSAlexey Kardashevskiy } 91121f80baSAlexey Kardashevskiy } 92121f80baSAlexey Kardashevskiy } 93121f80baSAlexey Kardashevskiy 94121f80baSAlexey Kardashevskiy extern long kvm_spapr_tce_attach_iommu_group(struct kvm *kvm, int tablefd, 95121f80baSAlexey Kardashevskiy struct iommu_group *grp) 96121f80baSAlexey Kardashevskiy { 97121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt = NULL; 98121f80baSAlexey Kardashevskiy bool found = false; 99121f80baSAlexey Kardashevskiy struct iommu_table *tbl = NULL; 100121f80baSAlexey Kardashevskiy struct iommu_table_group *table_group; 101121f80baSAlexey Kardashevskiy long i; 102121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit; 103121f80baSAlexey Kardashevskiy struct fd f; 104121f80baSAlexey Kardashevskiy 105121f80baSAlexey Kardashevskiy f = fdget(tablefd); 106121f80baSAlexey Kardashevskiy if (!f.file) 107121f80baSAlexey Kardashevskiy return -EBADF; 108121f80baSAlexey Kardashevskiy 109121f80baSAlexey Kardashevskiy list_for_each_entry_rcu(stt, &kvm->arch.spapr_tce_tables, list) { 110121f80baSAlexey Kardashevskiy if (stt == f.file->private_data) { 111121f80baSAlexey Kardashevskiy found = true; 112121f80baSAlexey Kardashevskiy break; 113121f80baSAlexey Kardashevskiy } 114121f80baSAlexey Kardashevskiy } 115121f80baSAlexey Kardashevskiy 116121f80baSAlexey Kardashevskiy fdput(f); 117121f80baSAlexey Kardashevskiy 118121f80baSAlexey Kardashevskiy if (!found) 119121f80baSAlexey Kardashevskiy return -EINVAL; 120121f80baSAlexey Kardashevskiy 121121f80baSAlexey Kardashevskiy table_group = iommu_group_get_iommudata(grp); 122121f80baSAlexey Kardashevskiy if (WARN_ON(!table_group)) 123121f80baSAlexey Kardashevskiy return -EFAULT; 124121f80baSAlexey Kardashevskiy 125121f80baSAlexey Kardashevskiy for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) { 126121f80baSAlexey Kardashevskiy struct iommu_table *tbltmp = table_group->tables[i]; 127121f80baSAlexey Kardashevskiy 128121f80baSAlexey Kardashevskiy if (!tbltmp) 129121f80baSAlexey Kardashevskiy continue; 130ca1fc489SAlexey Kardashevskiy /* Make sure hardware table parameters are compatible */ 131ca1fc489SAlexey Kardashevskiy if ((tbltmp->it_page_shift <= stt->page_shift) && 132ca1fc489SAlexey Kardashevskiy (tbltmp->it_offset << tbltmp->it_page_shift == 133ca1fc489SAlexey Kardashevskiy stt->offset << stt->page_shift) && 13476346cd9SAlexey Kardashevskiy (tbltmp->it_size << tbltmp->it_page_shift >= 135ca1fc489SAlexey Kardashevskiy stt->size << stt->page_shift)) { 136121f80baSAlexey Kardashevskiy /* 137121f80baSAlexey Kardashevskiy * Reference the table to avoid races with 138121f80baSAlexey Kardashevskiy * add/remove DMA windows. 139121f80baSAlexey Kardashevskiy */ 140121f80baSAlexey Kardashevskiy tbl = iommu_tce_table_get(tbltmp); 141121f80baSAlexey Kardashevskiy break; 142121f80baSAlexey Kardashevskiy } 143121f80baSAlexey Kardashevskiy } 144121f80baSAlexey Kardashevskiy if (!tbl) 145121f80baSAlexey Kardashevskiy return -EINVAL; 146121f80baSAlexey Kardashevskiy 147121f80baSAlexey Kardashevskiy list_for_each_entry_rcu(stit, &stt->iommu_tables, next) { 148121f80baSAlexey Kardashevskiy if (tbl != stit->tbl) 149121f80baSAlexey Kardashevskiy continue; 150121f80baSAlexey Kardashevskiy 151121f80baSAlexey Kardashevskiy if (!kref_get_unless_zero(&stit->kref)) { 152121f80baSAlexey Kardashevskiy /* stit is being destroyed */ 153121f80baSAlexey Kardashevskiy iommu_tce_table_put(tbl); 154121f80baSAlexey Kardashevskiy return -ENOTTY; 155121f80baSAlexey Kardashevskiy } 156121f80baSAlexey Kardashevskiy /* 157121f80baSAlexey Kardashevskiy * The table is already known to this KVM, we just increased 158121f80baSAlexey Kardashevskiy * its KVM reference counter and can return. 159121f80baSAlexey Kardashevskiy */ 160121f80baSAlexey Kardashevskiy return 0; 161121f80baSAlexey Kardashevskiy } 162121f80baSAlexey Kardashevskiy 163121f80baSAlexey Kardashevskiy stit = kzalloc(sizeof(*stit), GFP_KERNEL); 164121f80baSAlexey Kardashevskiy if (!stit) { 165121f80baSAlexey Kardashevskiy iommu_tce_table_put(tbl); 166121f80baSAlexey Kardashevskiy return -ENOMEM; 167121f80baSAlexey Kardashevskiy } 168121f80baSAlexey Kardashevskiy 169121f80baSAlexey Kardashevskiy stit->tbl = tbl; 170121f80baSAlexey Kardashevskiy kref_init(&stit->kref); 171121f80baSAlexey Kardashevskiy 172121f80baSAlexey Kardashevskiy list_add_rcu(&stit->next, &stt->iommu_tables); 173121f80baSAlexey Kardashevskiy 174121f80baSAlexey Kardashevskiy return 0; 175121f80baSAlexey Kardashevskiy } 176121f80baSAlexey Kardashevskiy 177366baf28SAlexey Kardashevskiy static void release_spapr_tce_table(struct rcu_head *head) 178f31e65e1SBenjamin Herrenschmidt { 179366baf28SAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt = container_of(head, 180366baf28SAlexey Kardashevskiy struct kvmppc_spapr_tce_table, rcu); 181fe26e527SAlexey Kardashevskiy unsigned long i, npages = kvmppc_tce_pages(stt->size); 182f31e65e1SBenjamin Herrenschmidt 183f8626985SAlexey Kardashevskiy for (i = 0; i < npages; i++) 184e1a1ef84SAlexey Kardashevskiy if (stt->pages[i]) 185f31e65e1SBenjamin Herrenschmidt __free_page(stt->pages[i]); 186f31e65e1SBenjamin Herrenschmidt 187366baf28SAlexey Kardashevskiy kfree(stt); 188f31e65e1SBenjamin Herrenschmidt } 189f31e65e1SBenjamin Herrenschmidt 190e1a1ef84SAlexey Kardashevskiy static struct page *kvm_spapr_get_tce_page(struct kvmppc_spapr_tce_table *stt, 191e1a1ef84SAlexey Kardashevskiy unsigned long sttpage) 192e1a1ef84SAlexey Kardashevskiy { 193e1a1ef84SAlexey Kardashevskiy struct page *page = stt->pages[sttpage]; 194e1a1ef84SAlexey Kardashevskiy 195e1a1ef84SAlexey Kardashevskiy if (page) 196e1a1ef84SAlexey Kardashevskiy return page; 197e1a1ef84SAlexey Kardashevskiy 198e1a1ef84SAlexey Kardashevskiy mutex_lock(&stt->alloc_lock); 199e1a1ef84SAlexey Kardashevskiy page = stt->pages[sttpage]; 200e1a1ef84SAlexey Kardashevskiy if (!page) { 201e1a1ef84SAlexey Kardashevskiy page = alloc_page(GFP_KERNEL | __GFP_ZERO); 202e1a1ef84SAlexey Kardashevskiy WARN_ON_ONCE(!page); 203e1a1ef84SAlexey Kardashevskiy if (page) 204e1a1ef84SAlexey Kardashevskiy stt->pages[sttpage] = page; 205e1a1ef84SAlexey Kardashevskiy } 206e1a1ef84SAlexey Kardashevskiy mutex_unlock(&stt->alloc_lock); 207e1a1ef84SAlexey Kardashevskiy 208e1a1ef84SAlexey Kardashevskiy return page; 209e1a1ef84SAlexey Kardashevskiy } 210e1a1ef84SAlexey Kardashevskiy 21116d5c39dSSouptick Joarder static vm_fault_t kvm_spapr_tce_fault(struct vm_fault *vmf) 212f31e65e1SBenjamin Herrenschmidt { 21311bac800SDave Jiang struct kvmppc_spapr_tce_table *stt = vmf->vma->vm_file->private_data; 214f31e65e1SBenjamin Herrenschmidt struct page *page; 215f31e65e1SBenjamin Herrenschmidt 216fe26e527SAlexey Kardashevskiy if (vmf->pgoff >= kvmppc_tce_pages(stt->size)) 217f31e65e1SBenjamin Herrenschmidt return VM_FAULT_SIGBUS; 218f31e65e1SBenjamin Herrenschmidt 219e1a1ef84SAlexey Kardashevskiy page = kvm_spapr_get_tce_page(stt, vmf->pgoff); 220e1a1ef84SAlexey Kardashevskiy if (!page) 221e1a1ef84SAlexey Kardashevskiy return VM_FAULT_OOM; 222e1a1ef84SAlexey Kardashevskiy 223f31e65e1SBenjamin Herrenschmidt get_page(page); 224f31e65e1SBenjamin Herrenschmidt vmf->page = page; 225f31e65e1SBenjamin Herrenschmidt return 0; 226f31e65e1SBenjamin Herrenschmidt } 227f31e65e1SBenjamin Herrenschmidt 228f31e65e1SBenjamin Herrenschmidt static const struct vm_operations_struct kvm_spapr_tce_vm_ops = { 229f31e65e1SBenjamin Herrenschmidt .fault = kvm_spapr_tce_fault, 230f31e65e1SBenjamin Herrenschmidt }; 231f31e65e1SBenjamin Herrenschmidt 232f31e65e1SBenjamin Herrenschmidt static int kvm_spapr_tce_mmap(struct file *file, struct vm_area_struct *vma) 233f31e65e1SBenjamin Herrenschmidt { 234f31e65e1SBenjamin Herrenschmidt vma->vm_ops = &kvm_spapr_tce_vm_ops; 235f31e65e1SBenjamin Herrenschmidt return 0; 236f31e65e1SBenjamin Herrenschmidt } 237f31e65e1SBenjamin Herrenschmidt 238f31e65e1SBenjamin Herrenschmidt static int kvm_spapr_tce_release(struct inode *inode, struct file *filp) 239f31e65e1SBenjamin Herrenschmidt { 240f31e65e1SBenjamin Herrenschmidt struct kvmppc_spapr_tce_table *stt = filp->private_data; 241121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit, *tmp; 242edd03602SPaul Mackerras struct kvm *kvm = stt->kvm; 243f31e65e1SBenjamin Herrenschmidt 244edd03602SPaul Mackerras mutex_lock(&kvm->lock); 245366baf28SAlexey Kardashevskiy list_del_rcu(&stt->list); 246edd03602SPaul Mackerras mutex_unlock(&kvm->lock); 247366baf28SAlexey Kardashevskiy 248121f80baSAlexey Kardashevskiy list_for_each_entry_safe(stit, tmp, &stt->iommu_tables, next) { 249121f80baSAlexey Kardashevskiy WARN_ON(!kref_read(&stit->kref)); 250121f80baSAlexey Kardashevskiy while (1) { 251121f80baSAlexey Kardashevskiy if (kref_put(&stit->kref, kvm_spapr_tce_liobn_put)) 252121f80baSAlexey Kardashevskiy break; 253121f80baSAlexey Kardashevskiy } 254121f80baSAlexey Kardashevskiy } 255121f80baSAlexey Kardashevskiy 256366baf28SAlexey Kardashevskiy kvm_put_kvm(stt->kvm); 257366baf28SAlexey Kardashevskiy 258*79eb597cSDaniel Jordan account_locked_vm(current->mm, 259fe26e527SAlexey Kardashevskiy kvmppc_stt_pages(kvmppc_tce_pages(stt->size)), false); 260366baf28SAlexey Kardashevskiy call_rcu(&stt->rcu, release_spapr_tce_table); 261366baf28SAlexey Kardashevskiy 262f31e65e1SBenjamin Herrenschmidt return 0; 263f31e65e1SBenjamin Herrenschmidt } 264f31e65e1SBenjamin Herrenschmidt 26575ef9de1SAl Viro static const struct file_operations kvm_spapr_tce_fops = { 266f31e65e1SBenjamin Herrenschmidt .mmap = kvm_spapr_tce_mmap, 267f31e65e1SBenjamin Herrenschmidt .release = kvm_spapr_tce_release, 268f31e65e1SBenjamin Herrenschmidt }; 269f31e65e1SBenjamin Herrenschmidt 270f31e65e1SBenjamin Herrenschmidt long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm, 27158ded420SAlexey Kardashevskiy struct kvm_create_spapr_tce_64 *args) 272f31e65e1SBenjamin Herrenschmidt { 273f31e65e1SBenjamin Herrenschmidt struct kvmppc_spapr_tce_table *stt = NULL; 27447c5310aSPaul Mackerras struct kvmppc_spapr_tce_table *siter; 27576346cd9SAlexey Kardashevskiy unsigned long npages, size = args->size; 276f31e65e1SBenjamin Herrenschmidt int ret = -ENOMEM; 277f31e65e1SBenjamin Herrenschmidt 278e45719afSAlexey Kardashevskiy if (!args->size || args->page_shift < 12 || args->page_shift > 34 || 279e45719afSAlexey Kardashevskiy (args->offset + args->size > (ULLONG_MAX >> args->page_shift))) 28058ded420SAlexey Kardashevskiy return -EINVAL; 28158ded420SAlexey Kardashevskiy 282fe26e527SAlexey Kardashevskiy npages = kvmppc_tce_pages(size); 283*79eb597cSDaniel Jordan ret = account_locked_vm(current->mm, kvmppc_stt_pages(npages), true); 28447c5310aSPaul Mackerras if (ret) 28547c5310aSPaul Mackerras return ret; 286f31e65e1SBenjamin Herrenschmidt 2875982f084SWei Yongjun ret = -ENOMEM; 288f31e65e1SBenjamin Herrenschmidt stt = kzalloc(sizeof(*stt) + npages * sizeof(struct page *), 289f31e65e1SBenjamin Herrenschmidt GFP_KERNEL); 290f31e65e1SBenjamin Herrenschmidt if (!stt) 29147c5310aSPaul Mackerras goto fail_acct; 292f31e65e1SBenjamin Herrenschmidt 293f31e65e1SBenjamin Herrenschmidt stt->liobn = args->liobn; 29458ded420SAlexey Kardashevskiy stt->page_shift = args->page_shift; 29558ded420SAlexey Kardashevskiy stt->offset = args->offset; 296fe26e527SAlexey Kardashevskiy stt->size = size; 297f31e65e1SBenjamin Herrenschmidt stt->kvm = kvm; 298e1a1ef84SAlexey Kardashevskiy mutex_init(&stt->alloc_lock); 299121f80baSAlexey Kardashevskiy INIT_LIST_HEAD_RCU(&stt->iommu_tables); 300f31e65e1SBenjamin Herrenschmidt 301f31e65e1SBenjamin Herrenschmidt mutex_lock(&kvm->lock); 30247c5310aSPaul Mackerras 30347c5310aSPaul Mackerras /* Check this LIOBN hasn't been previously allocated */ 30447c5310aSPaul Mackerras ret = 0; 30547c5310aSPaul Mackerras list_for_each_entry(siter, &kvm->arch.spapr_tce_tables, list) { 30647c5310aSPaul Mackerras if (siter->liobn == args->liobn) { 30747c5310aSPaul Mackerras ret = -EBUSY; 30847c5310aSPaul Mackerras break; 30947c5310aSPaul Mackerras } 31047c5310aSPaul Mackerras } 31147c5310aSPaul Mackerras 312716cb116SAlexey Kardashevskiy kvm_get_kvm(kvm); 313edd03602SPaul Mackerras if (!ret) 314edd03602SPaul Mackerras ret = anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops, 315edd03602SPaul Mackerras stt, O_RDWR | O_CLOEXEC); 316edd03602SPaul Mackerras 317716cb116SAlexey Kardashevskiy if (ret >= 0) 318366baf28SAlexey Kardashevskiy list_add_rcu(&stt->list, &kvm->arch.spapr_tce_tables); 319716cb116SAlexey Kardashevskiy else 320716cb116SAlexey Kardashevskiy kvm_put_kvm(kvm); 321f31e65e1SBenjamin Herrenschmidt 322f31e65e1SBenjamin Herrenschmidt mutex_unlock(&kvm->lock); 323f31e65e1SBenjamin Herrenschmidt 324edd03602SPaul Mackerras if (ret >= 0) 325edd03602SPaul Mackerras return ret; 326f31e65e1SBenjamin Herrenschmidt 327f31e65e1SBenjamin Herrenschmidt kfree(stt); 32847c5310aSPaul Mackerras fail_acct: 329*79eb597cSDaniel Jordan account_locked_vm(current->mm, kvmppc_stt_pages(npages), false); 330f31e65e1SBenjamin Herrenschmidt return ret; 331f31e65e1SBenjamin Herrenschmidt } 332d3695aa4SAlexey Kardashevskiy 3332001825eSAlexey Kardashevskiy static long kvmppc_tce_to_ua(struct kvm *kvm, unsigned long tce, 3342001825eSAlexey Kardashevskiy unsigned long *ua) 3352001825eSAlexey Kardashevskiy { 3362001825eSAlexey Kardashevskiy unsigned long gfn = tce >> PAGE_SHIFT; 3372001825eSAlexey Kardashevskiy struct kvm_memory_slot *memslot; 3382001825eSAlexey Kardashevskiy 3392001825eSAlexey Kardashevskiy memslot = search_memslots(kvm_memslots(kvm), gfn); 3402001825eSAlexey Kardashevskiy if (!memslot) 3412001825eSAlexey Kardashevskiy return -EINVAL; 3422001825eSAlexey Kardashevskiy 3432001825eSAlexey Kardashevskiy *ua = __gfn_to_hva_memslot(memslot, gfn) | 3442001825eSAlexey Kardashevskiy (tce & ~(PAGE_MASK | TCE_PCI_READ | TCE_PCI_WRITE)); 3452001825eSAlexey Kardashevskiy 3462001825eSAlexey Kardashevskiy return 0; 3472001825eSAlexey Kardashevskiy } 3482001825eSAlexey Kardashevskiy 34942de7b9eSAlexey Kardashevskiy static long kvmppc_tce_validate(struct kvmppc_spapr_tce_table *stt, 35042de7b9eSAlexey Kardashevskiy unsigned long tce) 35142de7b9eSAlexey Kardashevskiy { 35242de7b9eSAlexey Kardashevskiy unsigned long gpa = tce & ~(TCE_PCI_READ | TCE_PCI_WRITE); 35342de7b9eSAlexey Kardashevskiy enum dma_data_direction dir = iommu_tce_direction(tce); 35442de7b9eSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit; 35542de7b9eSAlexey Kardashevskiy unsigned long ua = 0; 35642de7b9eSAlexey Kardashevskiy 35742de7b9eSAlexey Kardashevskiy /* Allow userspace to poison TCE table */ 35842de7b9eSAlexey Kardashevskiy if (dir == DMA_NONE) 35942de7b9eSAlexey Kardashevskiy return H_SUCCESS; 36042de7b9eSAlexey Kardashevskiy 36142de7b9eSAlexey Kardashevskiy if (iommu_tce_check_gpa(stt->page_shift, gpa)) 36242de7b9eSAlexey Kardashevskiy return H_TOO_HARD; 36342de7b9eSAlexey Kardashevskiy 3642001825eSAlexey Kardashevskiy if (kvmppc_tce_to_ua(stt->kvm, tce, &ua)) 36542de7b9eSAlexey Kardashevskiy return H_TOO_HARD; 36642de7b9eSAlexey Kardashevskiy 36742de7b9eSAlexey Kardashevskiy list_for_each_entry_rcu(stit, &stt->iommu_tables, next) { 36842de7b9eSAlexey Kardashevskiy unsigned long hpa = 0; 36942de7b9eSAlexey Kardashevskiy struct mm_iommu_table_group_mem_t *mem; 37042de7b9eSAlexey Kardashevskiy long shift = stit->tbl->it_page_shift; 37142de7b9eSAlexey Kardashevskiy 37242de7b9eSAlexey Kardashevskiy mem = mm_iommu_lookup(stt->kvm->mm, ua, 1ULL << shift); 37342de7b9eSAlexey Kardashevskiy if (!mem) 37442de7b9eSAlexey Kardashevskiy return H_TOO_HARD; 37542de7b9eSAlexey Kardashevskiy 37642de7b9eSAlexey Kardashevskiy if (mm_iommu_ua_to_hpa(mem, ua, shift, &hpa)) 37742de7b9eSAlexey Kardashevskiy return H_TOO_HARD; 37842de7b9eSAlexey Kardashevskiy } 37942de7b9eSAlexey Kardashevskiy 38042de7b9eSAlexey Kardashevskiy return H_SUCCESS; 38142de7b9eSAlexey Kardashevskiy } 38242de7b9eSAlexey Kardashevskiy 383e1a1ef84SAlexey Kardashevskiy /* 384e1a1ef84SAlexey Kardashevskiy * Handles TCE requests for emulated devices. 385e1a1ef84SAlexey Kardashevskiy * Puts guest TCE values to the table and expects user space to convert them. 386e1a1ef84SAlexey Kardashevskiy * Cannot fail so kvmppc_tce_validate must be called before it. 387e1a1ef84SAlexey Kardashevskiy */ 388e1a1ef84SAlexey Kardashevskiy static void kvmppc_tce_put(struct kvmppc_spapr_tce_table *stt, 389e1a1ef84SAlexey Kardashevskiy unsigned long idx, unsigned long tce) 390e1a1ef84SAlexey Kardashevskiy { 391e1a1ef84SAlexey Kardashevskiy struct page *page; 392e1a1ef84SAlexey Kardashevskiy u64 *tbl; 393e1a1ef84SAlexey Kardashevskiy unsigned long sttpage; 394e1a1ef84SAlexey Kardashevskiy 395e1a1ef84SAlexey Kardashevskiy idx -= stt->offset; 396e1a1ef84SAlexey Kardashevskiy sttpage = idx / TCES_PER_PAGE; 397e1a1ef84SAlexey Kardashevskiy page = stt->pages[sttpage]; 398e1a1ef84SAlexey Kardashevskiy 399e1a1ef84SAlexey Kardashevskiy if (!page) { 400e1a1ef84SAlexey Kardashevskiy /* We allow any TCE, not just with read|write permissions */ 401e1a1ef84SAlexey Kardashevskiy if (!tce) 402e1a1ef84SAlexey Kardashevskiy return; 403e1a1ef84SAlexey Kardashevskiy 404e1a1ef84SAlexey Kardashevskiy page = kvm_spapr_get_tce_page(stt, sttpage); 405e1a1ef84SAlexey Kardashevskiy if (!page) 406e1a1ef84SAlexey Kardashevskiy return; 407e1a1ef84SAlexey Kardashevskiy } 408e1a1ef84SAlexey Kardashevskiy tbl = page_to_virt(page); 409e1a1ef84SAlexey Kardashevskiy 410e1a1ef84SAlexey Kardashevskiy tbl[idx % TCES_PER_PAGE] = tce; 411e1a1ef84SAlexey Kardashevskiy } 412e1a1ef84SAlexey Kardashevskiy 413c10c21efSAlexey Kardashevskiy static void kvmppc_clear_tce(struct mm_struct *mm, struct iommu_table *tbl, 414c10c21efSAlexey Kardashevskiy unsigned long entry) 415121f80baSAlexey Kardashevskiy { 416121f80baSAlexey Kardashevskiy unsigned long hpa = 0; 417121f80baSAlexey Kardashevskiy enum dma_data_direction dir = DMA_NONE; 418121f80baSAlexey Kardashevskiy 419c10c21efSAlexey Kardashevskiy iommu_tce_xchg(mm, tbl, entry, &hpa, &dir); 420121f80baSAlexey Kardashevskiy } 421121f80baSAlexey Kardashevskiy 422121f80baSAlexey Kardashevskiy static long kvmppc_tce_iommu_mapped_dec(struct kvm *kvm, 423121f80baSAlexey Kardashevskiy struct iommu_table *tbl, unsigned long entry) 424121f80baSAlexey Kardashevskiy { 425121f80baSAlexey Kardashevskiy struct mm_iommu_table_group_mem_t *mem = NULL; 426121f80baSAlexey Kardashevskiy const unsigned long pgsize = 1ULL << tbl->it_page_shift; 4276e301a8eSAlexey Kardashevskiy __be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY_RO(tbl, entry); 428121f80baSAlexey Kardashevskiy 429121f80baSAlexey Kardashevskiy if (!pua) 4306e301a8eSAlexey Kardashevskiy return H_SUCCESS; 431121f80baSAlexey Kardashevskiy 43200a5c58dSAlexey Kardashevskiy mem = mm_iommu_lookup(kvm->mm, be64_to_cpu(*pua), pgsize); 433121f80baSAlexey Kardashevskiy if (!mem) 434121f80baSAlexey Kardashevskiy return H_TOO_HARD; 435121f80baSAlexey Kardashevskiy 436121f80baSAlexey Kardashevskiy mm_iommu_mapped_dec(mem); 437121f80baSAlexey Kardashevskiy 43800a5c58dSAlexey Kardashevskiy *pua = cpu_to_be64(0); 439121f80baSAlexey Kardashevskiy 440121f80baSAlexey Kardashevskiy return H_SUCCESS; 441121f80baSAlexey Kardashevskiy } 442121f80baSAlexey Kardashevskiy 443ca1fc489SAlexey Kardashevskiy static long kvmppc_tce_iommu_do_unmap(struct kvm *kvm, 444121f80baSAlexey Kardashevskiy struct iommu_table *tbl, unsigned long entry) 445121f80baSAlexey Kardashevskiy { 446121f80baSAlexey Kardashevskiy enum dma_data_direction dir = DMA_NONE; 447121f80baSAlexey Kardashevskiy unsigned long hpa = 0; 448121f80baSAlexey Kardashevskiy long ret; 449121f80baSAlexey Kardashevskiy 450c10c21efSAlexey Kardashevskiy if (WARN_ON_ONCE(iommu_tce_xchg(kvm->mm, tbl, entry, &hpa, &dir))) 451f7960e29SAlexey Kardashevskiy return H_TOO_HARD; 452121f80baSAlexey Kardashevskiy 453121f80baSAlexey Kardashevskiy if (dir == DMA_NONE) 454121f80baSAlexey Kardashevskiy return H_SUCCESS; 455121f80baSAlexey Kardashevskiy 456121f80baSAlexey Kardashevskiy ret = kvmppc_tce_iommu_mapped_dec(kvm, tbl, entry); 457121f80baSAlexey Kardashevskiy if (ret != H_SUCCESS) 458c10c21efSAlexey Kardashevskiy iommu_tce_xchg(kvm->mm, tbl, entry, &hpa, &dir); 459121f80baSAlexey Kardashevskiy 460121f80baSAlexey Kardashevskiy return ret; 461121f80baSAlexey Kardashevskiy } 462121f80baSAlexey Kardashevskiy 463ca1fc489SAlexey Kardashevskiy static long kvmppc_tce_iommu_unmap(struct kvm *kvm, 464ca1fc489SAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt, struct iommu_table *tbl, 465ca1fc489SAlexey Kardashevskiy unsigned long entry) 466ca1fc489SAlexey Kardashevskiy { 467ca1fc489SAlexey Kardashevskiy unsigned long i, ret = H_SUCCESS; 468ca1fc489SAlexey Kardashevskiy unsigned long subpages = 1ULL << (stt->page_shift - tbl->it_page_shift); 469ca1fc489SAlexey Kardashevskiy unsigned long io_entry = entry * subpages; 470ca1fc489SAlexey Kardashevskiy 471ca1fc489SAlexey Kardashevskiy for (i = 0; i < subpages; ++i) { 472ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_do_unmap(kvm, tbl, io_entry + i); 473ca1fc489SAlexey Kardashevskiy if (ret != H_SUCCESS) 474ca1fc489SAlexey Kardashevskiy break; 475ca1fc489SAlexey Kardashevskiy } 476ca1fc489SAlexey Kardashevskiy 477ca1fc489SAlexey Kardashevskiy return ret; 478ca1fc489SAlexey Kardashevskiy } 479ca1fc489SAlexey Kardashevskiy 480ca1fc489SAlexey Kardashevskiy long kvmppc_tce_iommu_do_map(struct kvm *kvm, struct iommu_table *tbl, 481121f80baSAlexey Kardashevskiy unsigned long entry, unsigned long ua, 482121f80baSAlexey Kardashevskiy enum dma_data_direction dir) 483121f80baSAlexey Kardashevskiy { 484121f80baSAlexey Kardashevskiy long ret; 48500a5c58dSAlexey Kardashevskiy unsigned long hpa; 48600a5c58dSAlexey Kardashevskiy __be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY(tbl, entry); 487121f80baSAlexey Kardashevskiy struct mm_iommu_table_group_mem_t *mem; 488121f80baSAlexey Kardashevskiy 489121f80baSAlexey Kardashevskiy if (!pua) 490121f80baSAlexey Kardashevskiy /* it_userspace allocation might be delayed */ 491121f80baSAlexey Kardashevskiy return H_TOO_HARD; 492121f80baSAlexey Kardashevskiy 493121f80baSAlexey Kardashevskiy mem = mm_iommu_lookup(kvm->mm, ua, 1ULL << tbl->it_page_shift); 494121f80baSAlexey Kardashevskiy if (!mem) 495121f80baSAlexey Kardashevskiy /* This only handles v2 IOMMU type, v1 is handled via ioctl() */ 496121f80baSAlexey Kardashevskiy return H_TOO_HARD; 497121f80baSAlexey Kardashevskiy 49876fa4975SAlexey Kardashevskiy if (WARN_ON_ONCE(mm_iommu_ua_to_hpa(mem, ua, tbl->it_page_shift, &hpa))) 499f7960e29SAlexey Kardashevskiy return H_TOO_HARD; 500121f80baSAlexey Kardashevskiy 501121f80baSAlexey Kardashevskiy if (mm_iommu_mapped_inc(mem)) 502f7960e29SAlexey Kardashevskiy return H_TOO_HARD; 503121f80baSAlexey Kardashevskiy 504c10c21efSAlexey Kardashevskiy ret = iommu_tce_xchg(kvm->mm, tbl, entry, &hpa, &dir); 505121f80baSAlexey Kardashevskiy if (WARN_ON_ONCE(ret)) { 506121f80baSAlexey Kardashevskiy mm_iommu_mapped_dec(mem); 507f7960e29SAlexey Kardashevskiy return H_TOO_HARD; 508121f80baSAlexey Kardashevskiy } 509121f80baSAlexey Kardashevskiy 510121f80baSAlexey Kardashevskiy if (dir != DMA_NONE) 511121f80baSAlexey Kardashevskiy kvmppc_tce_iommu_mapped_dec(kvm, tbl, entry); 512121f80baSAlexey Kardashevskiy 51300a5c58dSAlexey Kardashevskiy *pua = cpu_to_be64(ua); 514121f80baSAlexey Kardashevskiy 515121f80baSAlexey Kardashevskiy return 0; 516121f80baSAlexey Kardashevskiy } 517121f80baSAlexey Kardashevskiy 518ca1fc489SAlexey Kardashevskiy static long kvmppc_tce_iommu_map(struct kvm *kvm, 519ca1fc489SAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt, struct iommu_table *tbl, 520ca1fc489SAlexey Kardashevskiy unsigned long entry, unsigned long ua, 521ca1fc489SAlexey Kardashevskiy enum dma_data_direction dir) 522ca1fc489SAlexey Kardashevskiy { 523ca1fc489SAlexey Kardashevskiy unsigned long i, pgoff, ret = H_SUCCESS; 524ca1fc489SAlexey Kardashevskiy unsigned long subpages = 1ULL << (stt->page_shift - tbl->it_page_shift); 525ca1fc489SAlexey Kardashevskiy unsigned long io_entry = entry * subpages; 526ca1fc489SAlexey Kardashevskiy 527ca1fc489SAlexey Kardashevskiy for (i = 0, pgoff = 0; i < subpages; 528ca1fc489SAlexey Kardashevskiy ++i, pgoff += IOMMU_PAGE_SIZE(tbl)) { 529ca1fc489SAlexey Kardashevskiy 530ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_do_map(kvm, tbl, 531ca1fc489SAlexey Kardashevskiy io_entry + i, ua + pgoff, dir); 532ca1fc489SAlexey Kardashevskiy if (ret != H_SUCCESS) 533ca1fc489SAlexey Kardashevskiy break; 534ca1fc489SAlexey Kardashevskiy } 535ca1fc489SAlexey Kardashevskiy 536ca1fc489SAlexey Kardashevskiy return ret; 537ca1fc489SAlexey Kardashevskiy } 538ca1fc489SAlexey Kardashevskiy 53931217db7SAlexey Kardashevskiy long kvmppc_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn, 54031217db7SAlexey Kardashevskiy unsigned long ioba, unsigned long tce) 54131217db7SAlexey Kardashevskiy { 542503bfcbeSAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt; 543121f80baSAlexey Kardashevskiy long ret, idx; 544121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit; 545121f80baSAlexey Kardashevskiy unsigned long entry, ua = 0; 546121f80baSAlexey Kardashevskiy enum dma_data_direction dir; 54731217db7SAlexey Kardashevskiy 54831217db7SAlexey Kardashevskiy /* udbg_printf("H_PUT_TCE(): liobn=0x%lx ioba=0x%lx, tce=0x%lx\n", */ 54931217db7SAlexey Kardashevskiy /* liobn, ioba, tce); */ 55031217db7SAlexey Kardashevskiy 551503bfcbeSAlexey Kardashevskiy stt = kvmppc_find_table(vcpu->kvm, liobn); 55231217db7SAlexey Kardashevskiy if (!stt) 55331217db7SAlexey Kardashevskiy return H_TOO_HARD; 55431217db7SAlexey Kardashevskiy 55531217db7SAlexey Kardashevskiy ret = kvmppc_ioba_validate(stt, ioba, 1); 55631217db7SAlexey Kardashevskiy if (ret != H_SUCCESS) 55731217db7SAlexey Kardashevskiy return ret; 55831217db7SAlexey Kardashevskiy 559345077c8SAlexey Kardashevskiy idx = srcu_read_lock(&vcpu->kvm->srcu); 560345077c8SAlexey Kardashevskiy 56131217db7SAlexey Kardashevskiy ret = kvmppc_tce_validate(stt, tce); 56231217db7SAlexey Kardashevskiy if (ret != H_SUCCESS) 563345077c8SAlexey Kardashevskiy goto unlock_exit; 56431217db7SAlexey Kardashevskiy 565121f80baSAlexey Kardashevskiy dir = iommu_tce_direction(tce); 5668f6a9f0dSAlexey Kardashevskiy 5672001825eSAlexey Kardashevskiy if ((dir != DMA_NONE) && kvmppc_tce_to_ua(vcpu->kvm, tce, &ua)) { 5688f6a9f0dSAlexey Kardashevskiy ret = H_PARAMETER; 5698f6a9f0dSAlexey Kardashevskiy goto unlock_exit; 5708f6a9f0dSAlexey Kardashevskiy } 571121f80baSAlexey Kardashevskiy 572121f80baSAlexey Kardashevskiy entry = ioba >> stt->page_shift; 573121f80baSAlexey Kardashevskiy 574121f80baSAlexey Kardashevskiy list_for_each_entry_lockless(stit, &stt->iommu_tables, next) { 5758f6a9f0dSAlexey Kardashevskiy if (dir == DMA_NONE) 576ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_unmap(vcpu->kvm, stt, 577121f80baSAlexey Kardashevskiy stit->tbl, entry); 5788f6a9f0dSAlexey Kardashevskiy else 579ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_map(vcpu->kvm, stt, stit->tbl, 580121f80baSAlexey Kardashevskiy entry, ua, dir); 581121f80baSAlexey Kardashevskiy 5822691f0ffSAlexey Kardashevskiy if (ret != H_SUCCESS) { 583c10c21efSAlexey Kardashevskiy kvmppc_clear_tce(vcpu->kvm->mm, stit->tbl, entry); 5842691f0ffSAlexey Kardashevskiy goto unlock_exit; 5852691f0ffSAlexey Kardashevskiy } 586121f80baSAlexey Kardashevskiy } 587121f80baSAlexey Kardashevskiy 588121f80baSAlexey Kardashevskiy kvmppc_tce_put(stt, entry, tce); 58931217db7SAlexey Kardashevskiy 5908f6a9f0dSAlexey Kardashevskiy unlock_exit: 5918f6a9f0dSAlexey Kardashevskiy srcu_read_unlock(&vcpu->kvm->srcu, idx); 5928f6a9f0dSAlexey Kardashevskiy 5938f6a9f0dSAlexey Kardashevskiy return ret; 59431217db7SAlexey Kardashevskiy } 59531217db7SAlexey Kardashevskiy EXPORT_SYMBOL_GPL(kvmppc_h_put_tce); 59631217db7SAlexey Kardashevskiy 597d3695aa4SAlexey Kardashevskiy long kvmppc_h_put_tce_indirect(struct kvm_vcpu *vcpu, 598d3695aa4SAlexey Kardashevskiy unsigned long liobn, unsigned long ioba, 599d3695aa4SAlexey Kardashevskiy unsigned long tce_list, unsigned long npages) 600d3695aa4SAlexey Kardashevskiy { 601d3695aa4SAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt; 602d3695aa4SAlexey Kardashevskiy long i, ret = H_SUCCESS, idx; 603d3695aa4SAlexey Kardashevskiy unsigned long entry, ua = 0; 604f8750513SDaniel Axtens u64 __user *tces; 605f8750513SDaniel Axtens u64 tce; 606121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit; 607d3695aa4SAlexey Kardashevskiy 608503bfcbeSAlexey Kardashevskiy stt = kvmppc_find_table(vcpu->kvm, liobn); 609d3695aa4SAlexey Kardashevskiy if (!stt) 610d3695aa4SAlexey Kardashevskiy return H_TOO_HARD; 611d3695aa4SAlexey Kardashevskiy 612fe26e527SAlexey Kardashevskiy entry = ioba >> stt->page_shift; 613d3695aa4SAlexey Kardashevskiy /* 614d3695aa4SAlexey Kardashevskiy * SPAPR spec says that the maximum size of the list is 512 TCEs 615d3695aa4SAlexey Kardashevskiy * so the whole table fits in 4K page 616d3695aa4SAlexey Kardashevskiy */ 617d3695aa4SAlexey Kardashevskiy if (npages > 512) 618d3695aa4SAlexey Kardashevskiy return H_PARAMETER; 619d3695aa4SAlexey Kardashevskiy 620d3695aa4SAlexey Kardashevskiy if (tce_list & (SZ_4K - 1)) 621d3695aa4SAlexey Kardashevskiy return H_PARAMETER; 622d3695aa4SAlexey Kardashevskiy 623d3695aa4SAlexey Kardashevskiy ret = kvmppc_ioba_validate(stt, ioba, npages); 624d3695aa4SAlexey Kardashevskiy if (ret != H_SUCCESS) 625d3695aa4SAlexey Kardashevskiy return ret; 626d3695aa4SAlexey Kardashevskiy 627d3695aa4SAlexey Kardashevskiy idx = srcu_read_lock(&vcpu->kvm->srcu); 6282001825eSAlexey Kardashevskiy if (kvmppc_tce_to_ua(vcpu->kvm, tce_list, &ua)) { 629d3695aa4SAlexey Kardashevskiy ret = H_TOO_HARD; 630d3695aa4SAlexey Kardashevskiy goto unlock_exit; 631d3695aa4SAlexey Kardashevskiy } 632d3695aa4SAlexey Kardashevskiy tces = (u64 __user *) ua; 633d3695aa4SAlexey Kardashevskiy 634d3695aa4SAlexey Kardashevskiy for (i = 0; i < npages; ++i) { 635d3695aa4SAlexey Kardashevskiy if (get_user(tce, tces + i)) { 636d3695aa4SAlexey Kardashevskiy ret = H_TOO_HARD; 637d3695aa4SAlexey Kardashevskiy goto unlock_exit; 638d3695aa4SAlexey Kardashevskiy } 639d3695aa4SAlexey Kardashevskiy tce = be64_to_cpu(tce); 640d3695aa4SAlexey Kardashevskiy 641d3695aa4SAlexey Kardashevskiy ret = kvmppc_tce_validate(stt, tce); 642d3695aa4SAlexey Kardashevskiy if (ret != H_SUCCESS) 643d3695aa4SAlexey Kardashevskiy goto unlock_exit; 644e199ad2bSAlexey Kardashevskiy } 645e199ad2bSAlexey Kardashevskiy 646e199ad2bSAlexey Kardashevskiy for (i = 0; i < npages; ++i) { 647e199ad2bSAlexey Kardashevskiy /* 648e199ad2bSAlexey Kardashevskiy * This looks unsafe, because we validate, then regrab 649e199ad2bSAlexey Kardashevskiy * the TCE from userspace which could have been changed by 650e199ad2bSAlexey Kardashevskiy * another thread. 651e199ad2bSAlexey Kardashevskiy * 652e199ad2bSAlexey Kardashevskiy * But it actually is safe, because the relevant checks will be 653e199ad2bSAlexey Kardashevskiy * re-executed in the following code. If userspace tries to 654e199ad2bSAlexey Kardashevskiy * change this dodgily it will result in a messier failure mode 655e199ad2bSAlexey Kardashevskiy * but won't threaten the host. 656e199ad2bSAlexey Kardashevskiy */ 657e199ad2bSAlexey Kardashevskiy if (get_user(tce, tces + i)) { 658e199ad2bSAlexey Kardashevskiy ret = H_TOO_HARD; 659e199ad2bSAlexey Kardashevskiy goto unlock_exit; 660e199ad2bSAlexey Kardashevskiy } 661e199ad2bSAlexey Kardashevskiy tce = be64_to_cpu(tce); 662d3695aa4SAlexey Kardashevskiy 6632001825eSAlexey Kardashevskiy if (kvmppc_tce_to_ua(vcpu->kvm, tce, &ua)) 664121f80baSAlexey Kardashevskiy return H_PARAMETER; 665121f80baSAlexey Kardashevskiy 666121f80baSAlexey Kardashevskiy list_for_each_entry_lockless(stit, &stt->iommu_tables, next) { 667ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_map(vcpu->kvm, stt, 668121f80baSAlexey Kardashevskiy stit->tbl, entry + i, ua, 669121f80baSAlexey Kardashevskiy iommu_tce_direction(tce)); 670121f80baSAlexey Kardashevskiy 6712691f0ffSAlexey Kardashevskiy if (ret != H_SUCCESS) { 672c10c21efSAlexey Kardashevskiy kvmppc_clear_tce(vcpu->kvm->mm, stit->tbl, 673c10c21efSAlexey Kardashevskiy entry); 6742691f0ffSAlexey Kardashevskiy goto unlock_exit; 6752691f0ffSAlexey Kardashevskiy } 676121f80baSAlexey Kardashevskiy } 677121f80baSAlexey Kardashevskiy 678d3695aa4SAlexey Kardashevskiy kvmppc_tce_put(stt, entry + i, tce); 679d3695aa4SAlexey Kardashevskiy } 680d3695aa4SAlexey Kardashevskiy 681d3695aa4SAlexey Kardashevskiy unlock_exit: 682d3695aa4SAlexey Kardashevskiy srcu_read_unlock(&vcpu->kvm->srcu, idx); 683d3695aa4SAlexey Kardashevskiy 684d3695aa4SAlexey Kardashevskiy return ret; 685d3695aa4SAlexey Kardashevskiy } 686d3695aa4SAlexey Kardashevskiy EXPORT_SYMBOL_GPL(kvmppc_h_put_tce_indirect); 68731217db7SAlexey Kardashevskiy 68831217db7SAlexey Kardashevskiy long kvmppc_h_stuff_tce(struct kvm_vcpu *vcpu, 68931217db7SAlexey Kardashevskiy unsigned long liobn, unsigned long ioba, 69031217db7SAlexey Kardashevskiy unsigned long tce_value, unsigned long npages) 69131217db7SAlexey Kardashevskiy { 69231217db7SAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt; 69331217db7SAlexey Kardashevskiy long i, ret; 694121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit; 69531217db7SAlexey Kardashevskiy 696503bfcbeSAlexey Kardashevskiy stt = kvmppc_find_table(vcpu->kvm, liobn); 69731217db7SAlexey Kardashevskiy if (!stt) 69831217db7SAlexey Kardashevskiy return H_TOO_HARD; 69931217db7SAlexey Kardashevskiy 70031217db7SAlexey Kardashevskiy ret = kvmppc_ioba_validate(stt, ioba, npages); 70131217db7SAlexey Kardashevskiy if (ret != H_SUCCESS) 70231217db7SAlexey Kardashevskiy return ret; 70331217db7SAlexey Kardashevskiy 70431217db7SAlexey Kardashevskiy /* Check permission bits only to allow userspace poison TCE for debug */ 70531217db7SAlexey Kardashevskiy if (tce_value & (TCE_PCI_WRITE | TCE_PCI_READ)) 70631217db7SAlexey Kardashevskiy return H_PARAMETER; 70731217db7SAlexey Kardashevskiy 708121f80baSAlexey Kardashevskiy list_for_each_entry_lockless(stit, &stt->iommu_tables, next) { 709c6b61661SAlexey Kardashevskiy unsigned long entry = ioba >> stt->page_shift; 710121f80baSAlexey Kardashevskiy 711121f80baSAlexey Kardashevskiy for (i = 0; i < npages; ++i) { 712ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_unmap(vcpu->kvm, stt, 713121f80baSAlexey Kardashevskiy stit->tbl, entry + i); 714121f80baSAlexey Kardashevskiy 715121f80baSAlexey Kardashevskiy if (ret == H_SUCCESS) 716121f80baSAlexey Kardashevskiy continue; 717121f80baSAlexey Kardashevskiy 718121f80baSAlexey Kardashevskiy if (ret == H_TOO_HARD) 719121f80baSAlexey Kardashevskiy return ret; 720121f80baSAlexey Kardashevskiy 721121f80baSAlexey Kardashevskiy WARN_ON_ONCE(1); 722c10c21efSAlexey Kardashevskiy kvmppc_clear_tce(vcpu->kvm->mm, stit->tbl, entry); 723121f80baSAlexey Kardashevskiy } 724121f80baSAlexey Kardashevskiy } 725121f80baSAlexey Kardashevskiy 72631217db7SAlexey Kardashevskiy for (i = 0; i < npages; ++i, ioba += (1ULL << stt->page_shift)) 72731217db7SAlexey Kardashevskiy kvmppc_tce_put(stt, ioba >> stt->page_shift, tce_value); 72831217db7SAlexey Kardashevskiy 72931217db7SAlexey Kardashevskiy return H_SUCCESS; 73031217db7SAlexey Kardashevskiy } 73131217db7SAlexey Kardashevskiy EXPORT_SYMBOL_GPL(kvmppc_h_stuff_tce); 732