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> 2279eb597cSDaniel Jordan #include <linux/mm.h> 231e2f2d31SKent Overstreet #include <linux/rcupdate_wait.h> 24f31e65e1SBenjamin Herrenschmidt 25f31e65e1SBenjamin Herrenschmidt #include <asm/kvm_ppc.h> 26f31e65e1SBenjamin Herrenschmidt #include <asm/kvm_book3s.h> 27f64e8084SAneesh Kumar K.V #include <asm/book3s/64/mmu-hash.h> 28f31e65e1SBenjamin Herrenschmidt #include <asm/hvcall.h> 29f31e65e1SBenjamin Herrenschmidt #include <asm/synch.h> 30f31e65e1SBenjamin Herrenschmidt #include <asm/ppc-opcode.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 36cad32d9dSAlexey Kardashevskiy static struct kvmppc_spapr_tce_table *kvmppc_find_table(struct kvm *kvm, 37cad32d9dSAlexey Kardashevskiy unsigned long liobn) 38cad32d9dSAlexey Kardashevskiy { 39cad32d9dSAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt; 40cad32d9dSAlexey Kardashevskiy 41cad32d9dSAlexey Kardashevskiy list_for_each_entry_lockless(stt, &kvm->arch.spapr_tce_tables, list) 42cad32d9dSAlexey Kardashevskiy if (stt->liobn == liobn) 43cad32d9dSAlexey Kardashevskiy return stt; 44cad32d9dSAlexey Kardashevskiy 45cad32d9dSAlexey Kardashevskiy return NULL; 46cad32d9dSAlexey Kardashevskiy } 47cad32d9dSAlexey Kardashevskiy 48fe26e527SAlexey Kardashevskiy static unsigned long kvmppc_tce_pages(unsigned long iommu_pages) 49f31e65e1SBenjamin Herrenschmidt { 50fe26e527SAlexey Kardashevskiy return ALIGN(iommu_pages * sizeof(u64), PAGE_SIZE) / PAGE_SIZE; 51f31e65e1SBenjamin Herrenschmidt } 52f31e65e1SBenjamin Herrenschmidt 53f8626985SAlexey Kardashevskiy static unsigned long kvmppc_stt_pages(unsigned long tce_pages) 54f8626985SAlexey Kardashevskiy { 55f8626985SAlexey Kardashevskiy unsigned long stt_bytes = sizeof(struct kvmppc_spapr_tce_table) + 56f8626985SAlexey Kardashevskiy (tce_pages * sizeof(struct page *)); 57f8626985SAlexey Kardashevskiy 58f8626985SAlexey Kardashevskiy return tce_pages + ALIGN(stt_bytes, PAGE_SIZE) / PAGE_SIZE; 59f8626985SAlexey Kardashevskiy } 60f8626985SAlexey Kardashevskiy 61121f80baSAlexey Kardashevskiy static void kvm_spapr_tce_iommu_table_free(struct rcu_head *head) 62121f80baSAlexey Kardashevskiy { 63121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit = container_of(head, 64121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table, rcu); 65121f80baSAlexey Kardashevskiy 66121f80baSAlexey Kardashevskiy iommu_tce_table_put(stit->tbl); 67121f80baSAlexey Kardashevskiy 68121f80baSAlexey Kardashevskiy kfree(stit); 69121f80baSAlexey Kardashevskiy } 70121f80baSAlexey Kardashevskiy 71121f80baSAlexey Kardashevskiy static void kvm_spapr_tce_liobn_put(struct kref *kref) 72121f80baSAlexey Kardashevskiy { 73121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit = container_of(kref, 74121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table, kref); 75121f80baSAlexey Kardashevskiy 76121f80baSAlexey Kardashevskiy list_del_rcu(&stit->next); 77121f80baSAlexey Kardashevskiy 78121f80baSAlexey Kardashevskiy call_rcu(&stit->rcu, kvm_spapr_tce_iommu_table_free); 79121f80baSAlexey Kardashevskiy } 80121f80baSAlexey Kardashevskiy 81419d5d11SBenjamin Gray void kvm_spapr_tce_release_iommu_group(struct kvm *kvm, 82121f80baSAlexey Kardashevskiy struct iommu_group *grp) 83121f80baSAlexey Kardashevskiy { 84121f80baSAlexey Kardashevskiy int i; 85121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt; 86121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit, *tmp; 87121f80baSAlexey Kardashevskiy struct iommu_table_group *table_group = NULL; 88121f80baSAlexey Kardashevskiy 89ab8b65beSQian Cai rcu_read_lock(); 90121f80baSAlexey Kardashevskiy list_for_each_entry_rcu(stt, &kvm->arch.spapr_tce_tables, list) { 91121f80baSAlexey Kardashevskiy 92121f80baSAlexey Kardashevskiy table_group = iommu_group_get_iommudata(grp); 93121f80baSAlexey Kardashevskiy if (WARN_ON(!table_group)) 94121f80baSAlexey Kardashevskiy continue; 95121f80baSAlexey Kardashevskiy 96121f80baSAlexey Kardashevskiy list_for_each_entry_safe(stit, tmp, &stt->iommu_tables, next) { 97121f80baSAlexey Kardashevskiy for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) { 98121f80baSAlexey Kardashevskiy if (table_group->tables[i] != stit->tbl) 99121f80baSAlexey Kardashevskiy continue; 100121f80baSAlexey Kardashevskiy 101121f80baSAlexey Kardashevskiy kref_put(&stit->kref, kvm_spapr_tce_liobn_put); 102121f80baSAlexey Kardashevskiy } 103121f80baSAlexey Kardashevskiy } 104ab8b65beSQian Cai cond_resched_rcu(); 105121f80baSAlexey Kardashevskiy } 106ab8b65beSQian Cai rcu_read_unlock(); 107121f80baSAlexey Kardashevskiy } 108121f80baSAlexey Kardashevskiy 109419d5d11SBenjamin Gray long kvm_spapr_tce_attach_iommu_group(struct kvm *kvm, int tablefd, 110121f80baSAlexey Kardashevskiy struct iommu_group *grp) 111121f80baSAlexey Kardashevskiy { 112121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt = NULL; 113121f80baSAlexey Kardashevskiy bool found = false; 114121f80baSAlexey Kardashevskiy struct iommu_table *tbl = NULL; 115121f80baSAlexey Kardashevskiy struct iommu_table_group *table_group; 116121f80baSAlexey Kardashevskiy long i; 117121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit; 118121f80baSAlexey Kardashevskiy struct fd f; 119121f80baSAlexey Kardashevskiy 120121f80baSAlexey Kardashevskiy f = fdget(tablefd); 121121f80baSAlexey Kardashevskiy if (!f.file) 122121f80baSAlexey Kardashevskiy return -EBADF; 123121f80baSAlexey Kardashevskiy 124ab8b65beSQian Cai rcu_read_lock(); 125121f80baSAlexey Kardashevskiy list_for_each_entry_rcu(stt, &kvm->arch.spapr_tce_tables, list) { 126121f80baSAlexey Kardashevskiy if (stt == f.file->private_data) { 127121f80baSAlexey Kardashevskiy found = true; 128121f80baSAlexey Kardashevskiy break; 129121f80baSAlexey Kardashevskiy } 130121f80baSAlexey Kardashevskiy } 131ab8b65beSQian Cai rcu_read_unlock(); 132121f80baSAlexey Kardashevskiy 133*a986fa57SMichael Ellerman if (!found) { 134121f80baSAlexey Kardashevskiy fdput(f); 135121f80baSAlexey Kardashevskiy return -EINVAL; 136*a986fa57SMichael Ellerman } 137121f80baSAlexey Kardashevskiy 138121f80baSAlexey Kardashevskiy table_group = iommu_group_get_iommudata(grp); 139*a986fa57SMichael Ellerman if (WARN_ON(!table_group)) { 140*a986fa57SMichael Ellerman fdput(f); 141121f80baSAlexey Kardashevskiy return -EFAULT; 142*a986fa57SMichael Ellerman } 143121f80baSAlexey Kardashevskiy 144121f80baSAlexey Kardashevskiy for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) { 145121f80baSAlexey Kardashevskiy struct iommu_table *tbltmp = table_group->tables[i]; 146121f80baSAlexey Kardashevskiy 147121f80baSAlexey Kardashevskiy if (!tbltmp) 148121f80baSAlexey Kardashevskiy continue; 149ca1fc489SAlexey Kardashevskiy /* Make sure hardware table parameters are compatible */ 150ca1fc489SAlexey Kardashevskiy if ((tbltmp->it_page_shift <= stt->page_shift) && 151ca1fc489SAlexey Kardashevskiy (tbltmp->it_offset << tbltmp->it_page_shift == 152ca1fc489SAlexey Kardashevskiy stt->offset << stt->page_shift) && 15376346cd9SAlexey Kardashevskiy (tbltmp->it_size << tbltmp->it_page_shift >= 154ca1fc489SAlexey Kardashevskiy stt->size << stt->page_shift)) { 155121f80baSAlexey Kardashevskiy /* 156121f80baSAlexey Kardashevskiy * Reference the table to avoid races with 157121f80baSAlexey Kardashevskiy * add/remove DMA windows. 158121f80baSAlexey Kardashevskiy */ 159121f80baSAlexey Kardashevskiy tbl = iommu_tce_table_get(tbltmp); 160121f80baSAlexey Kardashevskiy break; 161121f80baSAlexey Kardashevskiy } 162121f80baSAlexey Kardashevskiy } 163*a986fa57SMichael Ellerman if (!tbl) { 164*a986fa57SMichael Ellerman fdput(f); 165121f80baSAlexey Kardashevskiy return -EINVAL; 166*a986fa57SMichael Ellerman } 167121f80baSAlexey Kardashevskiy 168ab8b65beSQian Cai rcu_read_lock(); 169121f80baSAlexey Kardashevskiy list_for_each_entry_rcu(stit, &stt->iommu_tables, next) { 170121f80baSAlexey Kardashevskiy if (tbl != stit->tbl) 171121f80baSAlexey Kardashevskiy continue; 172121f80baSAlexey Kardashevskiy 173121f80baSAlexey Kardashevskiy if (!kref_get_unless_zero(&stit->kref)) { 174121f80baSAlexey Kardashevskiy /* stit is being destroyed */ 175121f80baSAlexey Kardashevskiy iommu_tce_table_put(tbl); 176ab8b65beSQian Cai rcu_read_unlock(); 177*a986fa57SMichael Ellerman fdput(f); 178121f80baSAlexey Kardashevskiy return -ENOTTY; 179121f80baSAlexey Kardashevskiy } 180121f80baSAlexey Kardashevskiy /* 181121f80baSAlexey Kardashevskiy * The table is already known to this KVM, we just increased 182121f80baSAlexey Kardashevskiy * its KVM reference counter and can return. 183121f80baSAlexey Kardashevskiy */ 184ab8b65beSQian Cai rcu_read_unlock(); 185*a986fa57SMichael Ellerman fdput(f); 186121f80baSAlexey Kardashevskiy return 0; 187121f80baSAlexey Kardashevskiy } 188ab8b65beSQian Cai rcu_read_unlock(); 189121f80baSAlexey Kardashevskiy 190121f80baSAlexey Kardashevskiy stit = kzalloc(sizeof(*stit), GFP_KERNEL); 191121f80baSAlexey Kardashevskiy if (!stit) { 192121f80baSAlexey Kardashevskiy iommu_tce_table_put(tbl); 193*a986fa57SMichael Ellerman fdput(f); 194121f80baSAlexey Kardashevskiy return -ENOMEM; 195121f80baSAlexey Kardashevskiy } 196121f80baSAlexey Kardashevskiy 197121f80baSAlexey Kardashevskiy stit->tbl = tbl; 198121f80baSAlexey Kardashevskiy kref_init(&stit->kref); 199121f80baSAlexey Kardashevskiy 200121f80baSAlexey Kardashevskiy list_add_rcu(&stit->next, &stt->iommu_tables); 201121f80baSAlexey Kardashevskiy 202*a986fa57SMichael Ellerman fdput(f); 203121f80baSAlexey Kardashevskiy return 0; 204121f80baSAlexey Kardashevskiy } 205121f80baSAlexey Kardashevskiy 206366baf28SAlexey Kardashevskiy static void release_spapr_tce_table(struct rcu_head *head) 207f31e65e1SBenjamin Herrenschmidt { 208366baf28SAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt = container_of(head, 209366baf28SAlexey Kardashevskiy struct kvmppc_spapr_tce_table, rcu); 210fe26e527SAlexey Kardashevskiy unsigned long i, npages = kvmppc_tce_pages(stt->size); 211f31e65e1SBenjamin Herrenschmidt 212f8626985SAlexey Kardashevskiy for (i = 0; i < npages; i++) 213e1a1ef84SAlexey Kardashevskiy if (stt->pages[i]) 214f31e65e1SBenjamin Herrenschmidt __free_page(stt->pages[i]); 215f31e65e1SBenjamin Herrenschmidt 216366baf28SAlexey Kardashevskiy kfree(stt); 217f31e65e1SBenjamin Herrenschmidt } 218f31e65e1SBenjamin Herrenschmidt 219e1a1ef84SAlexey Kardashevskiy static struct page *kvm_spapr_get_tce_page(struct kvmppc_spapr_tce_table *stt, 220e1a1ef84SAlexey Kardashevskiy unsigned long sttpage) 221e1a1ef84SAlexey Kardashevskiy { 222e1a1ef84SAlexey Kardashevskiy struct page *page = stt->pages[sttpage]; 223e1a1ef84SAlexey Kardashevskiy 224e1a1ef84SAlexey Kardashevskiy if (page) 225e1a1ef84SAlexey Kardashevskiy return page; 226e1a1ef84SAlexey Kardashevskiy 227e1a1ef84SAlexey Kardashevskiy mutex_lock(&stt->alloc_lock); 228e1a1ef84SAlexey Kardashevskiy page = stt->pages[sttpage]; 229e1a1ef84SAlexey Kardashevskiy if (!page) { 230e1a1ef84SAlexey Kardashevskiy page = alloc_page(GFP_KERNEL | __GFP_ZERO); 231e1a1ef84SAlexey Kardashevskiy WARN_ON_ONCE(!page); 232e1a1ef84SAlexey Kardashevskiy if (page) 233e1a1ef84SAlexey Kardashevskiy stt->pages[sttpage] = page; 234e1a1ef84SAlexey Kardashevskiy } 235e1a1ef84SAlexey Kardashevskiy mutex_unlock(&stt->alloc_lock); 236e1a1ef84SAlexey Kardashevskiy 237e1a1ef84SAlexey Kardashevskiy return page; 238e1a1ef84SAlexey Kardashevskiy } 239e1a1ef84SAlexey Kardashevskiy 24016d5c39dSSouptick Joarder static vm_fault_t kvm_spapr_tce_fault(struct vm_fault *vmf) 241f31e65e1SBenjamin Herrenschmidt { 24211bac800SDave Jiang struct kvmppc_spapr_tce_table *stt = vmf->vma->vm_file->private_data; 243f31e65e1SBenjamin Herrenschmidt struct page *page; 244f31e65e1SBenjamin Herrenschmidt 245fe26e527SAlexey Kardashevskiy if (vmf->pgoff >= kvmppc_tce_pages(stt->size)) 246f31e65e1SBenjamin Herrenschmidt return VM_FAULT_SIGBUS; 247f31e65e1SBenjamin Herrenschmidt 248e1a1ef84SAlexey Kardashevskiy page = kvm_spapr_get_tce_page(stt, vmf->pgoff); 249e1a1ef84SAlexey Kardashevskiy if (!page) 250e1a1ef84SAlexey Kardashevskiy return VM_FAULT_OOM; 251e1a1ef84SAlexey Kardashevskiy 252f31e65e1SBenjamin Herrenschmidt get_page(page); 253f31e65e1SBenjamin Herrenschmidt vmf->page = page; 254f31e65e1SBenjamin Herrenschmidt return 0; 255f31e65e1SBenjamin Herrenschmidt } 256f31e65e1SBenjamin Herrenschmidt 257f31e65e1SBenjamin Herrenschmidt static const struct vm_operations_struct kvm_spapr_tce_vm_ops = { 258f31e65e1SBenjamin Herrenschmidt .fault = kvm_spapr_tce_fault, 259f31e65e1SBenjamin Herrenschmidt }; 260f31e65e1SBenjamin Herrenschmidt 261f31e65e1SBenjamin Herrenschmidt static int kvm_spapr_tce_mmap(struct file *file, struct vm_area_struct *vma) 262f31e65e1SBenjamin Herrenschmidt { 263f31e65e1SBenjamin Herrenschmidt vma->vm_ops = &kvm_spapr_tce_vm_ops; 264f31e65e1SBenjamin Herrenschmidt return 0; 265f31e65e1SBenjamin Herrenschmidt } 266f31e65e1SBenjamin Herrenschmidt 267f31e65e1SBenjamin Herrenschmidt static int kvm_spapr_tce_release(struct inode *inode, struct file *filp) 268f31e65e1SBenjamin Herrenschmidt { 269f31e65e1SBenjamin Herrenschmidt struct kvmppc_spapr_tce_table *stt = filp->private_data; 270121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit, *tmp; 271edd03602SPaul Mackerras struct kvm *kvm = stt->kvm; 272f31e65e1SBenjamin Herrenschmidt 273edd03602SPaul Mackerras mutex_lock(&kvm->lock); 274366baf28SAlexey Kardashevskiy list_del_rcu(&stt->list); 275edd03602SPaul Mackerras mutex_unlock(&kvm->lock); 276366baf28SAlexey Kardashevskiy 277121f80baSAlexey Kardashevskiy list_for_each_entry_safe(stit, tmp, &stt->iommu_tables, next) { 278121f80baSAlexey Kardashevskiy WARN_ON(!kref_read(&stit->kref)); 279121f80baSAlexey Kardashevskiy while (1) { 280121f80baSAlexey Kardashevskiy if (kref_put(&stit->kref, kvm_spapr_tce_liobn_put)) 281121f80baSAlexey Kardashevskiy break; 282121f80baSAlexey Kardashevskiy } 283121f80baSAlexey Kardashevskiy } 284121f80baSAlexey Kardashevskiy 2858a9c8925SLeonardo Bras account_locked_vm(kvm->mm, 2868a9c8925SLeonardo Bras kvmppc_stt_pages(kvmppc_tce_pages(stt->size)), false); 2878a9c8925SLeonardo Bras 288366baf28SAlexey Kardashevskiy kvm_put_kvm(stt->kvm); 289366baf28SAlexey Kardashevskiy 290366baf28SAlexey Kardashevskiy call_rcu(&stt->rcu, release_spapr_tce_table); 291366baf28SAlexey Kardashevskiy 292f31e65e1SBenjamin Herrenschmidt return 0; 293f31e65e1SBenjamin Herrenschmidt } 294f31e65e1SBenjamin Herrenschmidt 29575ef9de1SAl Viro static const struct file_operations kvm_spapr_tce_fops = { 296f31e65e1SBenjamin Herrenschmidt .mmap = kvm_spapr_tce_mmap, 297f31e65e1SBenjamin Herrenschmidt .release = kvm_spapr_tce_release, 298f31e65e1SBenjamin Herrenschmidt }; 299f31e65e1SBenjamin Herrenschmidt 30067c48662SThomas Huth int kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm, 30158ded420SAlexey Kardashevskiy struct kvm_create_spapr_tce_64 *args) 302f31e65e1SBenjamin Herrenschmidt { 303f31e65e1SBenjamin Herrenschmidt struct kvmppc_spapr_tce_table *stt = NULL; 30447c5310aSPaul Mackerras struct kvmppc_spapr_tce_table *siter; 3058a9c8925SLeonardo Bras struct mm_struct *mm = kvm->mm; 3066fa1efeaSDeming Wang unsigned long npages; 307eb173559SJing Xiangfeng int ret; 308f31e65e1SBenjamin Herrenschmidt 309e45719afSAlexey Kardashevskiy if (!args->size || args->page_shift < 12 || args->page_shift > 34 || 310e45719afSAlexey Kardashevskiy (args->offset + args->size > (ULLONG_MAX >> args->page_shift))) 31158ded420SAlexey Kardashevskiy return -EINVAL; 31258ded420SAlexey Kardashevskiy 3136fa1efeaSDeming Wang npages = kvmppc_tce_pages(args->size); 3148a9c8925SLeonardo Bras ret = account_locked_vm(mm, kvmppc_stt_pages(npages), true); 31547c5310aSPaul Mackerras if (ret) 31647c5310aSPaul Mackerras return ret; 317f31e65e1SBenjamin Herrenschmidt 3185982f084SWei Yongjun ret = -ENOMEM; 3194dee21e0SAlexey Kardashevskiy stt = kzalloc(struct_size(stt, pages, npages), GFP_KERNEL | __GFP_NOWARN); 320f31e65e1SBenjamin Herrenschmidt if (!stt) 32147c5310aSPaul Mackerras goto fail_acct; 322f31e65e1SBenjamin Herrenschmidt 323f31e65e1SBenjamin Herrenschmidt stt->liobn = args->liobn; 32458ded420SAlexey Kardashevskiy stt->page_shift = args->page_shift; 32558ded420SAlexey Kardashevskiy stt->offset = args->offset; 3266fa1efeaSDeming Wang stt->size = args->size; 327f31e65e1SBenjamin Herrenschmidt stt->kvm = kvm; 328e1a1ef84SAlexey Kardashevskiy mutex_init(&stt->alloc_lock); 329121f80baSAlexey Kardashevskiy INIT_LIST_HEAD_RCU(&stt->iommu_tables); 330f31e65e1SBenjamin Herrenschmidt 331f31e65e1SBenjamin Herrenschmidt mutex_lock(&kvm->lock); 33247c5310aSPaul Mackerras 33347c5310aSPaul Mackerras /* Check this LIOBN hasn't been previously allocated */ 33447c5310aSPaul Mackerras ret = 0; 33547c5310aSPaul Mackerras list_for_each_entry(siter, &kvm->arch.spapr_tce_tables, list) { 33647c5310aSPaul Mackerras if (siter->liobn == args->liobn) { 33747c5310aSPaul Mackerras ret = -EBUSY; 33847c5310aSPaul Mackerras break; 33947c5310aSPaul Mackerras } 34047c5310aSPaul Mackerras } 34147c5310aSPaul Mackerras 342716cb116SAlexey Kardashevskiy kvm_get_kvm(kvm); 343edd03602SPaul Mackerras if (!ret) 344edd03602SPaul Mackerras ret = anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops, 345edd03602SPaul Mackerras stt, O_RDWR | O_CLOEXEC); 346edd03602SPaul Mackerras 347716cb116SAlexey Kardashevskiy if (ret >= 0) 348366baf28SAlexey Kardashevskiy list_add_rcu(&stt->list, &kvm->arch.spapr_tce_tables); 349716cb116SAlexey Kardashevskiy else 350149487bdSSean Christopherson kvm_put_kvm_no_destroy(kvm); 351f31e65e1SBenjamin Herrenschmidt 352f31e65e1SBenjamin Herrenschmidt mutex_unlock(&kvm->lock); 353f31e65e1SBenjamin Herrenschmidt 354edd03602SPaul Mackerras if (ret >= 0) 355edd03602SPaul Mackerras return ret; 356f31e65e1SBenjamin Herrenschmidt 357f31e65e1SBenjamin Herrenschmidt kfree(stt); 35847c5310aSPaul Mackerras fail_acct: 3598a9c8925SLeonardo Bras account_locked_vm(mm, kvmppc_stt_pages(npages), false); 360f31e65e1SBenjamin Herrenschmidt return ret; 361f31e65e1SBenjamin Herrenschmidt } 362d3695aa4SAlexey Kardashevskiy 3632001825eSAlexey Kardashevskiy static long kvmppc_tce_to_ua(struct kvm *kvm, unsigned long tce, 3642001825eSAlexey Kardashevskiy unsigned long *ua) 3652001825eSAlexey Kardashevskiy { 3662001825eSAlexey Kardashevskiy unsigned long gfn = tce >> PAGE_SHIFT; 3672001825eSAlexey Kardashevskiy struct kvm_memory_slot *memslot; 3682001825eSAlexey Kardashevskiy 3690f22af94SDavid Matlack memslot = __gfn_to_memslot(kvm_memslots(kvm), gfn); 3702001825eSAlexey Kardashevskiy if (!memslot) 3712001825eSAlexey Kardashevskiy return -EINVAL; 3722001825eSAlexey Kardashevskiy 3732001825eSAlexey Kardashevskiy *ua = __gfn_to_hva_memslot(memslot, gfn) | 3742001825eSAlexey Kardashevskiy (tce & ~(PAGE_MASK | TCE_PCI_READ | TCE_PCI_WRITE)); 3752001825eSAlexey Kardashevskiy 3762001825eSAlexey Kardashevskiy return 0; 3772001825eSAlexey Kardashevskiy } 3782001825eSAlexey Kardashevskiy 37942de7b9eSAlexey Kardashevskiy static long kvmppc_tce_validate(struct kvmppc_spapr_tce_table *stt, 38042de7b9eSAlexey Kardashevskiy unsigned long tce) 38142de7b9eSAlexey Kardashevskiy { 38242de7b9eSAlexey Kardashevskiy unsigned long gpa = tce & ~(TCE_PCI_READ | TCE_PCI_WRITE); 38342de7b9eSAlexey Kardashevskiy enum dma_data_direction dir = iommu_tce_direction(tce); 38442de7b9eSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit; 38542de7b9eSAlexey Kardashevskiy unsigned long ua = 0; 38642de7b9eSAlexey Kardashevskiy 38742de7b9eSAlexey Kardashevskiy /* Allow userspace to poison TCE table */ 38842de7b9eSAlexey Kardashevskiy if (dir == DMA_NONE) 38942de7b9eSAlexey Kardashevskiy return H_SUCCESS; 39042de7b9eSAlexey Kardashevskiy 39142de7b9eSAlexey Kardashevskiy if (iommu_tce_check_gpa(stt->page_shift, gpa)) 39242de7b9eSAlexey Kardashevskiy return H_TOO_HARD; 39342de7b9eSAlexey Kardashevskiy 3942001825eSAlexey Kardashevskiy if (kvmppc_tce_to_ua(stt->kvm, tce, &ua)) 39542de7b9eSAlexey Kardashevskiy return H_TOO_HARD; 39642de7b9eSAlexey Kardashevskiy 397ab8b65beSQian Cai rcu_read_lock(); 39842de7b9eSAlexey Kardashevskiy list_for_each_entry_rcu(stit, &stt->iommu_tables, next) { 39942de7b9eSAlexey Kardashevskiy unsigned long hpa = 0; 40042de7b9eSAlexey Kardashevskiy struct mm_iommu_table_group_mem_t *mem; 40142de7b9eSAlexey Kardashevskiy long shift = stit->tbl->it_page_shift; 40242de7b9eSAlexey Kardashevskiy 40342de7b9eSAlexey Kardashevskiy mem = mm_iommu_lookup(stt->kvm->mm, ua, 1ULL << shift); 404ab8b65beSQian Cai if (!mem || mm_iommu_ua_to_hpa(mem, ua, shift, &hpa)) { 405ab8b65beSQian Cai rcu_read_unlock(); 40642de7b9eSAlexey Kardashevskiy return H_TOO_HARD; 40742de7b9eSAlexey Kardashevskiy } 408ab8b65beSQian Cai } 409ab8b65beSQian Cai rcu_read_unlock(); 41042de7b9eSAlexey Kardashevskiy 41142de7b9eSAlexey Kardashevskiy return H_SUCCESS; 41242de7b9eSAlexey Kardashevskiy } 41342de7b9eSAlexey Kardashevskiy 414e1a1ef84SAlexey Kardashevskiy /* 415e1a1ef84SAlexey Kardashevskiy * Handles TCE requests for emulated devices. 416e1a1ef84SAlexey Kardashevskiy * Puts guest TCE values to the table and expects user space to convert them. 417e1a1ef84SAlexey Kardashevskiy * Cannot fail so kvmppc_tce_validate must be called before it. 418e1a1ef84SAlexey Kardashevskiy */ 419e1a1ef84SAlexey Kardashevskiy static void kvmppc_tce_put(struct kvmppc_spapr_tce_table *stt, 420e1a1ef84SAlexey Kardashevskiy unsigned long idx, unsigned long tce) 421e1a1ef84SAlexey Kardashevskiy { 422e1a1ef84SAlexey Kardashevskiy struct page *page; 423e1a1ef84SAlexey Kardashevskiy u64 *tbl; 424e1a1ef84SAlexey Kardashevskiy unsigned long sttpage; 425e1a1ef84SAlexey Kardashevskiy 426e1a1ef84SAlexey Kardashevskiy idx -= stt->offset; 427e1a1ef84SAlexey Kardashevskiy sttpage = idx / TCES_PER_PAGE; 428e1a1ef84SAlexey Kardashevskiy page = stt->pages[sttpage]; 429e1a1ef84SAlexey Kardashevskiy 430e1a1ef84SAlexey Kardashevskiy if (!page) { 431e1a1ef84SAlexey Kardashevskiy /* We allow any TCE, not just with read|write permissions */ 432e1a1ef84SAlexey Kardashevskiy if (!tce) 433e1a1ef84SAlexey Kardashevskiy return; 434e1a1ef84SAlexey Kardashevskiy 435e1a1ef84SAlexey Kardashevskiy page = kvm_spapr_get_tce_page(stt, sttpage); 436e1a1ef84SAlexey Kardashevskiy if (!page) 437e1a1ef84SAlexey Kardashevskiy return; 438e1a1ef84SAlexey Kardashevskiy } 439e1a1ef84SAlexey Kardashevskiy tbl = page_to_virt(page); 440e1a1ef84SAlexey Kardashevskiy 441e1a1ef84SAlexey Kardashevskiy tbl[idx % TCES_PER_PAGE] = tce; 442e1a1ef84SAlexey Kardashevskiy } 443e1a1ef84SAlexey Kardashevskiy 44426a62b75SAlexey Kardashevskiy static void kvmppc_clear_tce(struct mm_struct *mm, struct kvmppc_spapr_tce_table *stt, 44526a62b75SAlexey Kardashevskiy struct iommu_table *tbl, unsigned long entry) 446121f80baSAlexey Kardashevskiy { 44726a62b75SAlexey Kardashevskiy unsigned long i; 44826a62b75SAlexey Kardashevskiy unsigned long subpages = 1ULL << (stt->page_shift - tbl->it_page_shift); 44926a62b75SAlexey Kardashevskiy unsigned long io_entry = entry << (stt->page_shift - tbl->it_page_shift); 45026a62b75SAlexey Kardashevskiy 45126a62b75SAlexey Kardashevskiy for (i = 0; i < subpages; ++i) { 452121f80baSAlexey Kardashevskiy unsigned long hpa = 0; 453121f80baSAlexey Kardashevskiy enum dma_data_direction dir = DMA_NONE; 454121f80baSAlexey Kardashevskiy 45526a62b75SAlexey Kardashevskiy iommu_tce_xchg_no_kill(mm, tbl, io_entry + i, &hpa, &dir); 45626a62b75SAlexey Kardashevskiy } 457121f80baSAlexey Kardashevskiy } 458121f80baSAlexey Kardashevskiy 459121f80baSAlexey Kardashevskiy static long kvmppc_tce_iommu_mapped_dec(struct kvm *kvm, 460121f80baSAlexey Kardashevskiy struct iommu_table *tbl, unsigned long entry) 461121f80baSAlexey Kardashevskiy { 462121f80baSAlexey Kardashevskiy struct mm_iommu_table_group_mem_t *mem = NULL; 463121f80baSAlexey Kardashevskiy const unsigned long pgsize = 1ULL << tbl->it_page_shift; 4646e301a8eSAlexey Kardashevskiy __be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY_RO(tbl, entry); 465121f80baSAlexey Kardashevskiy 466121f80baSAlexey Kardashevskiy if (!pua) 4676e301a8eSAlexey Kardashevskiy return H_SUCCESS; 468121f80baSAlexey Kardashevskiy 46900a5c58dSAlexey Kardashevskiy mem = mm_iommu_lookup(kvm->mm, be64_to_cpu(*pua), pgsize); 470121f80baSAlexey Kardashevskiy if (!mem) 471121f80baSAlexey Kardashevskiy return H_TOO_HARD; 472121f80baSAlexey Kardashevskiy 473121f80baSAlexey Kardashevskiy mm_iommu_mapped_dec(mem); 474121f80baSAlexey Kardashevskiy 47500a5c58dSAlexey Kardashevskiy *pua = cpu_to_be64(0); 476121f80baSAlexey Kardashevskiy 477121f80baSAlexey Kardashevskiy return H_SUCCESS; 478121f80baSAlexey Kardashevskiy } 479121f80baSAlexey Kardashevskiy 480ca1fc489SAlexey Kardashevskiy static long kvmppc_tce_iommu_do_unmap(struct kvm *kvm, 481121f80baSAlexey Kardashevskiy struct iommu_table *tbl, unsigned long entry) 482121f80baSAlexey Kardashevskiy { 483121f80baSAlexey Kardashevskiy enum dma_data_direction dir = DMA_NONE; 484121f80baSAlexey Kardashevskiy unsigned long hpa = 0; 485121f80baSAlexey Kardashevskiy long ret; 486121f80baSAlexey Kardashevskiy 48701b7d128SAlexey Kardashevskiy if (WARN_ON_ONCE(iommu_tce_xchg_no_kill(kvm->mm, tbl, entry, &hpa, 48801b7d128SAlexey Kardashevskiy &dir))) 489f7960e29SAlexey Kardashevskiy return H_TOO_HARD; 490121f80baSAlexey Kardashevskiy 491121f80baSAlexey Kardashevskiy if (dir == DMA_NONE) 492121f80baSAlexey Kardashevskiy return H_SUCCESS; 493121f80baSAlexey Kardashevskiy 494121f80baSAlexey Kardashevskiy ret = kvmppc_tce_iommu_mapped_dec(kvm, tbl, entry); 495121f80baSAlexey Kardashevskiy if (ret != H_SUCCESS) 49601b7d128SAlexey Kardashevskiy iommu_tce_xchg_no_kill(kvm->mm, tbl, entry, &hpa, &dir); 497121f80baSAlexey Kardashevskiy 498121f80baSAlexey Kardashevskiy return ret; 499121f80baSAlexey Kardashevskiy } 500121f80baSAlexey Kardashevskiy 501ca1fc489SAlexey Kardashevskiy static long kvmppc_tce_iommu_unmap(struct kvm *kvm, 502ca1fc489SAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt, struct iommu_table *tbl, 503ca1fc489SAlexey Kardashevskiy unsigned long entry) 504ca1fc489SAlexey Kardashevskiy { 505ca1fc489SAlexey Kardashevskiy unsigned long i, ret = H_SUCCESS; 506ca1fc489SAlexey Kardashevskiy unsigned long subpages = 1ULL << (stt->page_shift - tbl->it_page_shift); 507ca1fc489SAlexey Kardashevskiy unsigned long io_entry = entry * subpages; 508ca1fc489SAlexey Kardashevskiy 509ca1fc489SAlexey Kardashevskiy for (i = 0; i < subpages; ++i) { 510ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_do_unmap(kvm, tbl, io_entry + i); 511ca1fc489SAlexey Kardashevskiy if (ret != H_SUCCESS) 512ca1fc489SAlexey Kardashevskiy break; 513ca1fc489SAlexey Kardashevskiy } 514ca1fc489SAlexey Kardashevskiy 51526a62b75SAlexey Kardashevskiy iommu_tce_kill(tbl, io_entry, subpages); 51626a62b75SAlexey Kardashevskiy 517ca1fc489SAlexey Kardashevskiy return ret; 518ca1fc489SAlexey Kardashevskiy } 519ca1fc489SAlexey Kardashevskiy 520cf59eb13SWang Wensheng static long kvmppc_tce_iommu_do_map(struct kvm *kvm, struct iommu_table *tbl, 521121f80baSAlexey Kardashevskiy unsigned long entry, unsigned long ua, 522121f80baSAlexey Kardashevskiy enum dma_data_direction dir) 523121f80baSAlexey Kardashevskiy { 524121f80baSAlexey Kardashevskiy long ret; 52500a5c58dSAlexey Kardashevskiy unsigned long hpa; 52600a5c58dSAlexey Kardashevskiy __be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY(tbl, entry); 527121f80baSAlexey Kardashevskiy struct mm_iommu_table_group_mem_t *mem; 528121f80baSAlexey Kardashevskiy 529121f80baSAlexey Kardashevskiy if (!pua) 530121f80baSAlexey Kardashevskiy /* it_userspace allocation might be delayed */ 531121f80baSAlexey Kardashevskiy return H_TOO_HARD; 532121f80baSAlexey Kardashevskiy 533121f80baSAlexey Kardashevskiy mem = mm_iommu_lookup(kvm->mm, ua, 1ULL << tbl->it_page_shift); 534121f80baSAlexey Kardashevskiy if (!mem) 535121f80baSAlexey Kardashevskiy /* This only handles v2 IOMMU type, v1 is handled via ioctl() */ 536121f80baSAlexey Kardashevskiy return H_TOO_HARD; 537121f80baSAlexey Kardashevskiy 53876fa4975SAlexey Kardashevskiy if (WARN_ON_ONCE(mm_iommu_ua_to_hpa(mem, ua, tbl->it_page_shift, &hpa))) 539f7960e29SAlexey Kardashevskiy return H_TOO_HARD; 540121f80baSAlexey Kardashevskiy 541121f80baSAlexey Kardashevskiy if (mm_iommu_mapped_inc(mem)) 542f7960e29SAlexey Kardashevskiy return H_TOO_HARD; 543121f80baSAlexey Kardashevskiy 54401b7d128SAlexey Kardashevskiy ret = iommu_tce_xchg_no_kill(kvm->mm, tbl, entry, &hpa, &dir); 545121f80baSAlexey Kardashevskiy if (WARN_ON_ONCE(ret)) { 546121f80baSAlexey Kardashevskiy mm_iommu_mapped_dec(mem); 547f7960e29SAlexey Kardashevskiy return H_TOO_HARD; 548121f80baSAlexey Kardashevskiy } 549121f80baSAlexey Kardashevskiy 550121f80baSAlexey Kardashevskiy if (dir != DMA_NONE) 551121f80baSAlexey Kardashevskiy kvmppc_tce_iommu_mapped_dec(kvm, tbl, entry); 552121f80baSAlexey Kardashevskiy 55300a5c58dSAlexey Kardashevskiy *pua = cpu_to_be64(ua); 554121f80baSAlexey Kardashevskiy 555121f80baSAlexey Kardashevskiy return 0; 556121f80baSAlexey Kardashevskiy } 557121f80baSAlexey Kardashevskiy 558ca1fc489SAlexey Kardashevskiy static long kvmppc_tce_iommu_map(struct kvm *kvm, 559ca1fc489SAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt, struct iommu_table *tbl, 560ca1fc489SAlexey Kardashevskiy unsigned long entry, unsigned long ua, 561ca1fc489SAlexey Kardashevskiy enum dma_data_direction dir) 562ca1fc489SAlexey Kardashevskiy { 563ca1fc489SAlexey Kardashevskiy unsigned long i, pgoff, ret = H_SUCCESS; 564ca1fc489SAlexey Kardashevskiy unsigned long subpages = 1ULL << (stt->page_shift - tbl->it_page_shift); 565ca1fc489SAlexey Kardashevskiy unsigned long io_entry = entry * subpages; 566ca1fc489SAlexey Kardashevskiy 567ca1fc489SAlexey Kardashevskiy for (i = 0, pgoff = 0; i < subpages; 568ca1fc489SAlexey Kardashevskiy ++i, pgoff += IOMMU_PAGE_SIZE(tbl)) { 569ca1fc489SAlexey Kardashevskiy 570ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_do_map(kvm, tbl, 571ca1fc489SAlexey Kardashevskiy io_entry + i, ua + pgoff, dir); 572ca1fc489SAlexey Kardashevskiy if (ret != H_SUCCESS) 573ca1fc489SAlexey Kardashevskiy break; 574ca1fc489SAlexey Kardashevskiy } 575ca1fc489SAlexey Kardashevskiy 57626a62b75SAlexey Kardashevskiy iommu_tce_kill(tbl, io_entry, subpages); 57726a62b75SAlexey Kardashevskiy 578ca1fc489SAlexey Kardashevskiy return ret; 579ca1fc489SAlexey Kardashevskiy } 580ca1fc489SAlexey Kardashevskiy 58131217db7SAlexey Kardashevskiy long kvmppc_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn, 58231217db7SAlexey Kardashevskiy unsigned long ioba, unsigned long tce) 58331217db7SAlexey Kardashevskiy { 584503bfcbeSAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt; 585121f80baSAlexey Kardashevskiy long ret, idx; 586121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit; 587121f80baSAlexey Kardashevskiy unsigned long entry, ua = 0; 588121f80baSAlexey Kardashevskiy enum dma_data_direction dir; 58931217db7SAlexey Kardashevskiy 59031217db7SAlexey Kardashevskiy /* udbg_printf("H_PUT_TCE(): liobn=0x%lx ioba=0x%lx, tce=0x%lx\n", */ 59131217db7SAlexey Kardashevskiy /* liobn, ioba, tce); */ 59231217db7SAlexey Kardashevskiy 593503bfcbeSAlexey Kardashevskiy stt = kvmppc_find_table(vcpu->kvm, liobn); 59431217db7SAlexey Kardashevskiy if (!stt) 59531217db7SAlexey Kardashevskiy return H_TOO_HARD; 59631217db7SAlexey Kardashevskiy 59731217db7SAlexey Kardashevskiy ret = kvmppc_ioba_validate(stt, ioba, 1); 59831217db7SAlexey Kardashevskiy if (ret != H_SUCCESS) 59931217db7SAlexey Kardashevskiy return ret; 60031217db7SAlexey Kardashevskiy 601345077c8SAlexey Kardashevskiy idx = srcu_read_lock(&vcpu->kvm->srcu); 602345077c8SAlexey Kardashevskiy 60331217db7SAlexey Kardashevskiy ret = kvmppc_tce_validate(stt, tce); 60431217db7SAlexey Kardashevskiy if (ret != H_SUCCESS) 605345077c8SAlexey Kardashevskiy goto unlock_exit; 60631217db7SAlexey Kardashevskiy 607121f80baSAlexey Kardashevskiy dir = iommu_tce_direction(tce); 6088f6a9f0dSAlexey Kardashevskiy 6092001825eSAlexey Kardashevskiy if ((dir != DMA_NONE) && kvmppc_tce_to_ua(vcpu->kvm, tce, &ua)) { 6108f6a9f0dSAlexey Kardashevskiy ret = H_PARAMETER; 6118f6a9f0dSAlexey Kardashevskiy goto unlock_exit; 6128f6a9f0dSAlexey Kardashevskiy } 613121f80baSAlexey Kardashevskiy 614121f80baSAlexey Kardashevskiy entry = ioba >> stt->page_shift; 615121f80baSAlexey Kardashevskiy 616121f80baSAlexey Kardashevskiy list_for_each_entry_lockless(stit, &stt->iommu_tables, next) { 6178f6a9f0dSAlexey Kardashevskiy if (dir == DMA_NONE) 618ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_unmap(vcpu->kvm, stt, 619121f80baSAlexey Kardashevskiy stit->tbl, entry); 6208f6a9f0dSAlexey Kardashevskiy else 621ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_map(vcpu->kvm, stt, stit->tbl, 622121f80baSAlexey Kardashevskiy entry, ua, dir); 623121f80baSAlexey Kardashevskiy 62401b7d128SAlexey Kardashevskiy 6252691f0ffSAlexey Kardashevskiy if (ret != H_SUCCESS) { 62626a62b75SAlexey Kardashevskiy kvmppc_clear_tce(vcpu->kvm->mm, stt, stit->tbl, entry); 6272691f0ffSAlexey Kardashevskiy goto unlock_exit; 6282691f0ffSAlexey Kardashevskiy } 629121f80baSAlexey Kardashevskiy } 630121f80baSAlexey Kardashevskiy 631121f80baSAlexey Kardashevskiy kvmppc_tce_put(stt, entry, tce); 63231217db7SAlexey Kardashevskiy 6338f6a9f0dSAlexey Kardashevskiy unlock_exit: 6348f6a9f0dSAlexey Kardashevskiy srcu_read_unlock(&vcpu->kvm->srcu, idx); 6358f6a9f0dSAlexey Kardashevskiy 6368f6a9f0dSAlexey Kardashevskiy return ret; 63731217db7SAlexey Kardashevskiy } 63831217db7SAlexey Kardashevskiy EXPORT_SYMBOL_GPL(kvmppc_h_put_tce); 63931217db7SAlexey Kardashevskiy 640d3695aa4SAlexey Kardashevskiy long kvmppc_h_put_tce_indirect(struct kvm_vcpu *vcpu, 641d3695aa4SAlexey Kardashevskiy unsigned long liobn, unsigned long ioba, 642d3695aa4SAlexey Kardashevskiy unsigned long tce_list, unsigned long npages) 643d3695aa4SAlexey Kardashevskiy { 644d3695aa4SAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt; 645d3695aa4SAlexey Kardashevskiy long i, ret = H_SUCCESS, idx; 646d3695aa4SAlexey Kardashevskiy unsigned long entry, ua = 0; 647f8750513SDaniel Axtens u64 __user *tces; 648f8750513SDaniel Axtens u64 tce; 649121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit; 650d3695aa4SAlexey Kardashevskiy 651503bfcbeSAlexey Kardashevskiy stt = kvmppc_find_table(vcpu->kvm, liobn); 652d3695aa4SAlexey Kardashevskiy if (!stt) 653d3695aa4SAlexey Kardashevskiy return H_TOO_HARD; 654d3695aa4SAlexey Kardashevskiy 655fe26e527SAlexey Kardashevskiy entry = ioba >> stt->page_shift; 656d3695aa4SAlexey Kardashevskiy /* 657d3695aa4SAlexey Kardashevskiy * SPAPR spec says that the maximum size of the list is 512 TCEs 658d3695aa4SAlexey Kardashevskiy * so the whole table fits in 4K page 659d3695aa4SAlexey Kardashevskiy */ 660d3695aa4SAlexey Kardashevskiy if (npages > 512) 661d3695aa4SAlexey Kardashevskiy return H_PARAMETER; 662d3695aa4SAlexey Kardashevskiy 663d3695aa4SAlexey Kardashevskiy if (tce_list & (SZ_4K - 1)) 664d3695aa4SAlexey Kardashevskiy return H_PARAMETER; 665d3695aa4SAlexey Kardashevskiy 666d3695aa4SAlexey Kardashevskiy ret = kvmppc_ioba_validate(stt, ioba, npages); 667d3695aa4SAlexey Kardashevskiy if (ret != H_SUCCESS) 668d3695aa4SAlexey Kardashevskiy return ret; 669d3695aa4SAlexey Kardashevskiy 670d3695aa4SAlexey Kardashevskiy idx = srcu_read_lock(&vcpu->kvm->srcu); 6712001825eSAlexey Kardashevskiy if (kvmppc_tce_to_ua(vcpu->kvm, tce_list, &ua)) { 672d3695aa4SAlexey Kardashevskiy ret = H_TOO_HARD; 673d3695aa4SAlexey Kardashevskiy goto unlock_exit; 674d3695aa4SAlexey Kardashevskiy } 675d3695aa4SAlexey Kardashevskiy tces = (u64 __user *) ua; 676d3695aa4SAlexey Kardashevskiy 677d3695aa4SAlexey Kardashevskiy for (i = 0; i < npages; ++i) { 678d3695aa4SAlexey Kardashevskiy if (get_user(tce, tces + i)) { 679d3695aa4SAlexey Kardashevskiy ret = H_TOO_HARD; 680d3695aa4SAlexey Kardashevskiy goto unlock_exit; 681d3695aa4SAlexey Kardashevskiy } 682d3695aa4SAlexey Kardashevskiy tce = be64_to_cpu(tce); 683d3695aa4SAlexey Kardashevskiy 684d3695aa4SAlexey Kardashevskiy ret = kvmppc_tce_validate(stt, tce); 685d3695aa4SAlexey Kardashevskiy if (ret != H_SUCCESS) 686d3695aa4SAlexey Kardashevskiy goto unlock_exit; 687e199ad2bSAlexey Kardashevskiy } 688e199ad2bSAlexey Kardashevskiy 689e199ad2bSAlexey Kardashevskiy for (i = 0; i < npages; ++i) { 690e199ad2bSAlexey Kardashevskiy /* 691e199ad2bSAlexey Kardashevskiy * This looks unsafe, because we validate, then regrab 692e199ad2bSAlexey Kardashevskiy * the TCE from userspace which could have been changed by 693e199ad2bSAlexey Kardashevskiy * another thread. 694e199ad2bSAlexey Kardashevskiy * 695e199ad2bSAlexey Kardashevskiy * But it actually is safe, because the relevant checks will be 696e199ad2bSAlexey Kardashevskiy * re-executed in the following code. If userspace tries to 697e199ad2bSAlexey Kardashevskiy * change this dodgily it will result in a messier failure mode 698e199ad2bSAlexey Kardashevskiy * but won't threaten the host. 699e199ad2bSAlexey Kardashevskiy */ 700e199ad2bSAlexey Kardashevskiy if (get_user(tce, tces + i)) { 701e199ad2bSAlexey Kardashevskiy ret = H_TOO_HARD; 70226a62b75SAlexey Kardashevskiy goto unlock_exit; 703e199ad2bSAlexey Kardashevskiy } 704e199ad2bSAlexey Kardashevskiy tce = be64_to_cpu(tce); 705d3695aa4SAlexey Kardashevskiy 7064f916593SAlexey Kardashevskiy if (kvmppc_tce_to_ua(vcpu->kvm, tce, &ua)) { 7074f916593SAlexey Kardashevskiy ret = H_PARAMETER; 70826a62b75SAlexey Kardashevskiy goto unlock_exit; 7094f916593SAlexey Kardashevskiy } 710121f80baSAlexey Kardashevskiy 711121f80baSAlexey Kardashevskiy list_for_each_entry_lockless(stit, &stt->iommu_tables, next) { 712ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_map(vcpu->kvm, stt, 713121f80baSAlexey Kardashevskiy stit->tbl, entry + i, ua, 714121f80baSAlexey Kardashevskiy iommu_tce_direction(tce)); 715121f80baSAlexey Kardashevskiy 7162691f0ffSAlexey Kardashevskiy if (ret != H_SUCCESS) { 71726a62b75SAlexey Kardashevskiy kvmppc_clear_tce(vcpu->kvm->mm, stt, stit->tbl, 71826a62b75SAlexey Kardashevskiy entry + i); 71926a62b75SAlexey Kardashevskiy goto unlock_exit; 7202691f0ffSAlexey Kardashevskiy } 721121f80baSAlexey Kardashevskiy } 722121f80baSAlexey Kardashevskiy 723d3695aa4SAlexey Kardashevskiy kvmppc_tce_put(stt, entry + i, tce); 724d3695aa4SAlexey Kardashevskiy } 725d3695aa4SAlexey Kardashevskiy 726d3695aa4SAlexey Kardashevskiy unlock_exit: 727d3695aa4SAlexey Kardashevskiy srcu_read_unlock(&vcpu->kvm->srcu, idx); 728d3695aa4SAlexey Kardashevskiy 729d3695aa4SAlexey Kardashevskiy return ret; 730d3695aa4SAlexey Kardashevskiy } 731d3695aa4SAlexey Kardashevskiy EXPORT_SYMBOL_GPL(kvmppc_h_put_tce_indirect); 73231217db7SAlexey Kardashevskiy 73331217db7SAlexey Kardashevskiy long kvmppc_h_stuff_tce(struct kvm_vcpu *vcpu, 73431217db7SAlexey Kardashevskiy unsigned long liobn, unsigned long ioba, 73531217db7SAlexey Kardashevskiy unsigned long tce_value, unsigned long npages) 73631217db7SAlexey Kardashevskiy { 73731217db7SAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt; 73831217db7SAlexey Kardashevskiy long i, ret; 739121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit; 74031217db7SAlexey Kardashevskiy 741503bfcbeSAlexey Kardashevskiy stt = kvmppc_find_table(vcpu->kvm, liobn); 74231217db7SAlexey Kardashevskiy if (!stt) 74331217db7SAlexey Kardashevskiy return H_TOO_HARD; 74431217db7SAlexey Kardashevskiy 74531217db7SAlexey Kardashevskiy ret = kvmppc_ioba_validate(stt, ioba, npages); 74631217db7SAlexey Kardashevskiy if (ret != H_SUCCESS) 74731217db7SAlexey Kardashevskiy return ret; 74831217db7SAlexey Kardashevskiy 74931217db7SAlexey Kardashevskiy /* Check permission bits only to allow userspace poison TCE for debug */ 75031217db7SAlexey Kardashevskiy if (tce_value & (TCE_PCI_WRITE | TCE_PCI_READ)) 75131217db7SAlexey Kardashevskiy return H_PARAMETER; 75231217db7SAlexey Kardashevskiy 753121f80baSAlexey Kardashevskiy list_for_each_entry_lockless(stit, &stt->iommu_tables, next) { 754c6b61661SAlexey Kardashevskiy unsigned long entry = ioba >> stt->page_shift; 755121f80baSAlexey Kardashevskiy 756121f80baSAlexey Kardashevskiy for (i = 0; i < npages; ++i) { 757ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_unmap(vcpu->kvm, stt, 758121f80baSAlexey Kardashevskiy stit->tbl, entry + i); 759121f80baSAlexey Kardashevskiy 760121f80baSAlexey Kardashevskiy if (ret == H_SUCCESS) 761121f80baSAlexey Kardashevskiy continue; 762121f80baSAlexey Kardashevskiy 763121f80baSAlexey Kardashevskiy if (ret == H_TOO_HARD) 76426a62b75SAlexey Kardashevskiy return ret; 765121f80baSAlexey Kardashevskiy 766121f80baSAlexey Kardashevskiy WARN_ON_ONCE(1); 76726a62b75SAlexey Kardashevskiy kvmppc_clear_tce(vcpu->kvm->mm, stt, stit->tbl, entry + i); 768121f80baSAlexey Kardashevskiy } 769121f80baSAlexey Kardashevskiy } 770121f80baSAlexey Kardashevskiy 77131217db7SAlexey Kardashevskiy for (i = 0; i < npages; ++i, ioba += (1ULL << stt->page_shift)) 77231217db7SAlexey Kardashevskiy kvmppc_tce_put(stt, ioba >> stt->page_shift, tce_value); 77331217db7SAlexey Kardashevskiy 77401b7d128SAlexey Kardashevskiy return ret; 77531217db7SAlexey Kardashevskiy } 77631217db7SAlexey Kardashevskiy EXPORT_SYMBOL_GPL(kvmppc_h_stuff_tce); 777cad32d9dSAlexey Kardashevskiy 778cad32d9dSAlexey Kardashevskiy long kvmppc_h_get_tce(struct kvm_vcpu *vcpu, unsigned long liobn, 779cad32d9dSAlexey Kardashevskiy unsigned long ioba) 780cad32d9dSAlexey Kardashevskiy { 781cad32d9dSAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt; 782cad32d9dSAlexey Kardashevskiy long ret; 783cad32d9dSAlexey Kardashevskiy unsigned long idx; 784cad32d9dSAlexey Kardashevskiy struct page *page; 785cad32d9dSAlexey Kardashevskiy u64 *tbl; 786cad32d9dSAlexey Kardashevskiy 787cad32d9dSAlexey Kardashevskiy stt = kvmppc_find_table(vcpu->kvm, liobn); 788cad32d9dSAlexey Kardashevskiy if (!stt) 789cad32d9dSAlexey Kardashevskiy return H_TOO_HARD; 790cad32d9dSAlexey Kardashevskiy 791cad32d9dSAlexey Kardashevskiy ret = kvmppc_ioba_validate(stt, ioba, 1); 792cad32d9dSAlexey Kardashevskiy if (ret != H_SUCCESS) 793cad32d9dSAlexey Kardashevskiy return ret; 794cad32d9dSAlexey Kardashevskiy 795cad32d9dSAlexey Kardashevskiy idx = (ioba >> stt->page_shift) - stt->offset; 796cad32d9dSAlexey Kardashevskiy page = stt->pages[idx / TCES_PER_PAGE]; 797cad32d9dSAlexey Kardashevskiy if (!page) { 7980e85b7dfSJordan Niethe kvmppc_set_gpr(vcpu, 4, 0); 799cad32d9dSAlexey Kardashevskiy return H_SUCCESS; 800cad32d9dSAlexey Kardashevskiy } 801cad32d9dSAlexey Kardashevskiy tbl = (u64 *)page_address(page); 802cad32d9dSAlexey Kardashevskiy 8030e85b7dfSJordan Niethe kvmppc_set_gpr(vcpu, 4, tbl[idx % TCES_PER_PAGE]); 804cad32d9dSAlexey Kardashevskiy 805cad32d9dSAlexey Kardashevskiy return H_SUCCESS; 806cad32d9dSAlexey Kardashevskiy } 807cad32d9dSAlexey Kardashevskiy EXPORT_SYMBOL_GPL(kvmppc_h_get_tce); 808