1f31e65e1SBenjamin Herrenschmidt /* 2f31e65e1SBenjamin Herrenschmidt * This program is free software; you can redistribute it and/or modify 3f31e65e1SBenjamin Herrenschmidt * it under the terms of the GNU General Public License, version 2, as 4f31e65e1SBenjamin Herrenschmidt * published by the Free Software Foundation. 5f31e65e1SBenjamin Herrenschmidt * 6f31e65e1SBenjamin Herrenschmidt * This program is distributed in the hope that it will be useful, 7f31e65e1SBenjamin Herrenschmidt * but WITHOUT ANY WARRANTY; without even the implied warranty of 8f31e65e1SBenjamin Herrenschmidt * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9f31e65e1SBenjamin Herrenschmidt * GNU General Public License for more details. 10f31e65e1SBenjamin Herrenschmidt * 11f31e65e1SBenjamin Herrenschmidt * You should have received a copy of the GNU General Public License 12f31e65e1SBenjamin Herrenschmidt * along with this program; if not, write to the Free Software 13f31e65e1SBenjamin Herrenschmidt * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 14f31e65e1SBenjamin Herrenschmidt * 15f31e65e1SBenjamin Herrenschmidt * Copyright 2010 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com> 16f31e65e1SBenjamin Herrenschmidt * Copyright 2011 David Gibson, IBM Corporation <dwg@au1.ibm.com> 17d3695aa4SAlexey Kardashevskiy * Copyright 2016 Alexey Kardashevskiy, IBM Corporation <aik@au1.ibm.com> 18f31e65e1SBenjamin Herrenschmidt */ 19f31e65e1SBenjamin Herrenschmidt 20f31e65e1SBenjamin Herrenschmidt #include <linux/types.h> 21f31e65e1SBenjamin Herrenschmidt #include <linux/string.h> 22f31e65e1SBenjamin Herrenschmidt #include <linux/kvm.h> 23f31e65e1SBenjamin Herrenschmidt #include <linux/kvm_host.h> 24f31e65e1SBenjamin Herrenschmidt #include <linux/highmem.h> 25f31e65e1SBenjamin Herrenschmidt #include <linux/gfp.h> 26f31e65e1SBenjamin Herrenschmidt #include <linux/slab.h> 273f07c014SIngo Molnar #include <linux/sched/signal.h> 28f31e65e1SBenjamin Herrenschmidt #include <linux/hugetlb.h> 29f31e65e1SBenjamin Herrenschmidt #include <linux/list.h> 30f31e65e1SBenjamin Herrenschmidt #include <linux/anon_inodes.h> 31121f80baSAlexey Kardashevskiy #include <linux/iommu.h> 32121f80baSAlexey Kardashevskiy #include <linux/file.h> 33f31e65e1SBenjamin Herrenschmidt 34f31e65e1SBenjamin Herrenschmidt #include <asm/kvm_ppc.h> 35f31e65e1SBenjamin Herrenschmidt #include <asm/kvm_book3s.h> 36f64e8084SAneesh Kumar K.V #include <asm/book3s/64/mmu-hash.h> 37f31e65e1SBenjamin Herrenschmidt #include <asm/hvcall.h> 38f31e65e1SBenjamin Herrenschmidt #include <asm/synch.h> 39f31e65e1SBenjamin Herrenschmidt #include <asm/ppc-opcode.h> 40f31e65e1SBenjamin Herrenschmidt #include <asm/kvm_host.h> 41f31e65e1SBenjamin Herrenschmidt #include <asm/udbg.h> 42462ee11eSAlexey Kardashevskiy #include <asm/iommu.h> 43d3695aa4SAlexey Kardashevskiy #include <asm/tce.h> 44121f80baSAlexey Kardashevskiy #include <asm/mmu_context.h> 45f31e65e1SBenjamin Herrenschmidt 46fe26e527SAlexey Kardashevskiy static unsigned long kvmppc_tce_pages(unsigned long iommu_pages) 47f31e65e1SBenjamin Herrenschmidt { 48fe26e527SAlexey Kardashevskiy return ALIGN(iommu_pages * sizeof(u64), PAGE_SIZE) / PAGE_SIZE; 49f31e65e1SBenjamin Herrenschmidt } 50f31e65e1SBenjamin Herrenschmidt 51f8626985SAlexey Kardashevskiy static unsigned long kvmppc_stt_pages(unsigned long tce_pages) 52f8626985SAlexey Kardashevskiy { 53f8626985SAlexey Kardashevskiy unsigned long stt_bytes = sizeof(struct kvmppc_spapr_tce_table) + 54f8626985SAlexey Kardashevskiy (tce_pages * sizeof(struct page *)); 55f8626985SAlexey Kardashevskiy 56f8626985SAlexey Kardashevskiy return tce_pages + ALIGN(stt_bytes, PAGE_SIZE) / PAGE_SIZE; 57f8626985SAlexey Kardashevskiy } 58f8626985SAlexey Kardashevskiy 59f8626985SAlexey Kardashevskiy static long kvmppc_account_memlimit(unsigned long stt_pages, bool inc) 60f8626985SAlexey Kardashevskiy { 61f8626985SAlexey Kardashevskiy long ret = 0; 62f8626985SAlexey Kardashevskiy 63f8626985SAlexey Kardashevskiy if (!current || !current->mm) 64f8626985SAlexey Kardashevskiy return ret; /* process exited */ 65f8626985SAlexey Kardashevskiy 66f8626985SAlexey Kardashevskiy down_write(¤t->mm->mmap_sem); 67f8626985SAlexey Kardashevskiy 68f8626985SAlexey Kardashevskiy if (inc) { 69f8626985SAlexey Kardashevskiy unsigned long locked, lock_limit; 70f8626985SAlexey Kardashevskiy 71f8626985SAlexey Kardashevskiy locked = current->mm->locked_vm + stt_pages; 72f8626985SAlexey Kardashevskiy lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; 73f8626985SAlexey Kardashevskiy if (locked > lock_limit && !capable(CAP_IPC_LOCK)) 74f8626985SAlexey Kardashevskiy ret = -ENOMEM; 75f8626985SAlexey Kardashevskiy else 76f8626985SAlexey Kardashevskiy current->mm->locked_vm += stt_pages; 77f8626985SAlexey Kardashevskiy } else { 78f8626985SAlexey Kardashevskiy if (WARN_ON_ONCE(stt_pages > current->mm->locked_vm)) 79f8626985SAlexey Kardashevskiy stt_pages = current->mm->locked_vm; 80f8626985SAlexey Kardashevskiy 81f8626985SAlexey Kardashevskiy current->mm->locked_vm -= stt_pages; 82f8626985SAlexey Kardashevskiy } 83f8626985SAlexey Kardashevskiy 84f8626985SAlexey Kardashevskiy pr_debug("[%d] RLIMIT_MEMLOCK KVM %c%ld %ld/%ld%s\n", current->pid, 85f8626985SAlexey Kardashevskiy inc ? '+' : '-', 86f8626985SAlexey Kardashevskiy stt_pages << PAGE_SHIFT, 87f8626985SAlexey Kardashevskiy current->mm->locked_vm << PAGE_SHIFT, 88f8626985SAlexey Kardashevskiy rlimit(RLIMIT_MEMLOCK), 89f8626985SAlexey Kardashevskiy ret ? " - exceeded" : ""); 90f8626985SAlexey Kardashevskiy 91f8626985SAlexey Kardashevskiy up_write(¤t->mm->mmap_sem); 92f8626985SAlexey Kardashevskiy 93f8626985SAlexey Kardashevskiy return ret; 94f8626985SAlexey Kardashevskiy } 95f8626985SAlexey Kardashevskiy 96121f80baSAlexey Kardashevskiy static void kvm_spapr_tce_iommu_table_free(struct rcu_head *head) 97121f80baSAlexey Kardashevskiy { 98121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit = container_of(head, 99121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table, rcu); 100121f80baSAlexey Kardashevskiy 101121f80baSAlexey Kardashevskiy iommu_tce_table_put(stit->tbl); 102121f80baSAlexey Kardashevskiy 103121f80baSAlexey Kardashevskiy kfree(stit); 104121f80baSAlexey Kardashevskiy } 105121f80baSAlexey Kardashevskiy 106121f80baSAlexey Kardashevskiy static void kvm_spapr_tce_liobn_put(struct kref *kref) 107121f80baSAlexey Kardashevskiy { 108121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit = container_of(kref, 109121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table, kref); 110121f80baSAlexey Kardashevskiy 111121f80baSAlexey Kardashevskiy list_del_rcu(&stit->next); 112121f80baSAlexey Kardashevskiy 113121f80baSAlexey Kardashevskiy call_rcu(&stit->rcu, kvm_spapr_tce_iommu_table_free); 114121f80baSAlexey Kardashevskiy } 115121f80baSAlexey Kardashevskiy 116121f80baSAlexey Kardashevskiy extern void kvm_spapr_tce_release_iommu_group(struct kvm *kvm, 117121f80baSAlexey Kardashevskiy struct iommu_group *grp) 118121f80baSAlexey Kardashevskiy { 119121f80baSAlexey Kardashevskiy int i; 120121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt; 121121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit, *tmp; 122121f80baSAlexey Kardashevskiy struct iommu_table_group *table_group = NULL; 123121f80baSAlexey Kardashevskiy 124121f80baSAlexey Kardashevskiy list_for_each_entry_rcu(stt, &kvm->arch.spapr_tce_tables, list) { 125121f80baSAlexey Kardashevskiy 126121f80baSAlexey Kardashevskiy table_group = iommu_group_get_iommudata(grp); 127121f80baSAlexey Kardashevskiy if (WARN_ON(!table_group)) 128121f80baSAlexey Kardashevskiy continue; 129121f80baSAlexey Kardashevskiy 130121f80baSAlexey Kardashevskiy list_for_each_entry_safe(stit, tmp, &stt->iommu_tables, next) { 131121f80baSAlexey Kardashevskiy for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) { 132121f80baSAlexey Kardashevskiy if (table_group->tables[i] != stit->tbl) 133121f80baSAlexey Kardashevskiy continue; 134121f80baSAlexey Kardashevskiy 135121f80baSAlexey Kardashevskiy kref_put(&stit->kref, kvm_spapr_tce_liobn_put); 136121f80baSAlexey Kardashevskiy } 137121f80baSAlexey Kardashevskiy } 138121f80baSAlexey Kardashevskiy } 139121f80baSAlexey Kardashevskiy } 140121f80baSAlexey Kardashevskiy 141121f80baSAlexey Kardashevskiy extern long kvm_spapr_tce_attach_iommu_group(struct kvm *kvm, int tablefd, 142121f80baSAlexey Kardashevskiy struct iommu_group *grp) 143121f80baSAlexey Kardashevskiy { 144121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt = NULL; 145121f80baSAlexey Kardashevskiy bool found = false; 146121f80baSAlexey Kardashevskiy struct iommu_table *tbl = NULL; 147121f80baSAlexey Kardashevskiy struct iommu_table_group *table_group; 148121f80baSAlexey Kardashevskiy long i; 149121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit; 150121f80baSAlexey Kardashevskiy struct fd f; 151121f80baSAlexey Kardashevskiy 152121f80baSAlexey Kardashevskiy f = fdget(tablefd); 153121f80baSAlexey Kardashevskiy if (!f.file) 154121f80baSAlexey Kardashevskiy return -EBADF; 155121f80baSAlexey Kardashevskiy 156121f80baSAlexey Kardashevskiy list_for_each_entry_rcu(stt, &kvm->arch.spapr_tce_tables, list) { 157121f80baSAlexey Kardashevskiy if (stt == f.file->private_data) { 158121f80baSAlexey Kardashevskiy found = true; 159121f80baSAlexey Kardashevskiy break; 160121f80baSAlexey Kardashevskiy } 161121f80baSAlexey Kardashevskiy } 162121f80baSAlexey Kardashevskiy 163121f80baSAlexey Kardashevskiy fdput(f); 164121f80baSAlexey Kardashevskiy 165121f80baSAlexey Kardashevskiy if (!found) 166121f80baSAlexey Kardashevskiy return -EINVAL; 167121f80baSAlexey Kardashevskiy 168121f80baSAlexey Kardashevskiy table_group = iommu_group_get_iommudata(grp); 169121f80baSAlexey Kardashevskiy if (WARN_ON(!table_group)) 170121f80baSAlexey Kardashevskiy return -EFAULT; 171121f80baSAlexey Kardashevskiy 172121f80baSAlexey Kardashevskiy for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) { 173121f80baSAlexey Kardashevskiy struct iommu_table *tbltmp = table_group->tables[i]; 174121f80baSAlexey Kardashevskiy 175121f80baSAlexey Kardashevskiy if (!tbltmp) 176121f80baSAlexey Kardashevskiy continue; 177ca1fc489SAlexey Kardashevskiy /* Make sure hardware table parameters are compatible */ 178ca1fc489SAlexey Kardashevskiy if ((tbltmp->it_page_shift <= stt->page_shift) && 179ca1fc489SAlexey Kardashevskiy (tbltmp->it_offset << tbltmp->it_page_shift == 180ca1fc489SAlexey Kardashevskiy stt->offset << stt->page_shift) && 18176346cd9SAlexey Kardashevskiy (tbltmp->it_size << tbltmp->it_page_shift >= 182ca1fc489SAlexey Kardashevskiy stt->size << stt->page_shift)) { 183121f80baSAlexey Kardashevskiy /* 184121f80baSAlexey Kardashevskiy * Reference the table to avoid races with 185121f80baSAlexey Kardashevskiy * add/remove DMA windows. 186121f80baSAlexey Kardashevskiy */ 187121f80baSAlexey Kardashevskiy tbl = iommu_tce_table_get(tbltmp); 188121f80baSAlexey Kardashevskiy break; 189121f80baSAlexey Kardashevskiy } 190121f80baSAlexey Kardashevskiy } 191121f80baSAlexey Kardashevskiy if (!tbl) 192121f80baSAlexey Kardashevskiy return -EINVAL; 193121f80baSAlexey Kardashevskiy 194121f80baSAlexey Kardashevskiy list_for_each_entry_rcu(stit, &stt->iommu_tables, next) { 195121f80baSAlexey Kardashevskiy if (tbl != stit->tbl) 196121f80baSAlexey Kardashevskiy continue; 197121f80baSAlexey Kardashevskiy 198121f80baSAlexey Kardashevskiy if (!kref_get_unless_zero(&stit->kref)) { 199121f80baSAlexey Kardashevskiy /* stit is being destroyed */ 200121f80baSAlexey Kardashevskiy iommu_tce_table_put(tbl); 201121f80baSAlexey Kardashevskiy return -ENOTTY; 202121f80baSAlexey Kardashevskiy } 203121f80baSAlexey Kardashevskiy /* 204121f80baSAlexey Kardashevskiy * The table is already known to this KVM, we just increased 205121f80baSAlexey Kardashevskiy * its KVM reference counter and can return. 206121f80baSAlexey Kardashevskiy */ 207121f80baSAlexey Kardashevskiy return 0; 208121f80baSAlexey Kardashevskiy } 209121f80baSAlexey Kardashevskiy 210121f80baSAlexey Kardashevskiy stit = kzalloc(sizeof(*stit), GFP_KERNEL); 211121f80baSAlexey Kardashevskiy if (!stit) { 212121f80baSAlexey Kardashevskiy iommu_tce_table_put(tbl); 213121f80baSAlexey Kardashevskiy return -ENOMEM; 214121f80baSAlexey Kardashevskiy } 215121f80baSAlexey Kardashevskiy 216121f80baSAlexey Kardashevskiy stit->tbl = tbl; 217121f80baSAlexey Kardashevskiy kref_init(&stit->kref); 218121f80baSAlexey Kardashevskiy 219121f80baSAlexey Kardashevskiy list_add_rcu(&stit->next, &stt->iommu_tables); 220121f80baSAlexey Kardashevskiy 221121f80baSAlexey Kardashevskiy return 0; 222121f80baSAlexey Kardashevskiy } 223121f80baSAlexey Kardashevskiy 224366baf28SAlexey Kardashevskiy static void release_spapr_tce_table(struct rcu_head *head) 225f31e65e1SBenjamin Herrenschmidt { 226366baf28SAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt = container_of(head, 227366baf28SAlexey Kardashevskiy struct kvmppc_spapr_tce_table, rcu); 228fe26e527SAlexey Kardashevskiy unsigned long i, npages = kvmppc_tce_pages(stt->size); 229f31e65e1SBenjamin Herrenschmidt 230f8626985SAlexey Kardashevskiy for (i = 0; i < npages; i++) 231*e1a1ef84SAlexey Kardashevskiy if (stt->pages[i]) 232f31e65e1SBenjamin Herrenschmidt __free_page(stt->pages[i]); 233f31e65e1SBenjamin Herrenschmidt 234366baf28SAlexey Kardashevskiy kfree(stt); 235f31e65e1SBenjamin Herrenschmidt } 236f31e65e1SBenjamin Herrenschmidt 237*e1a1ef84SAlexey Kardashevskiy static struct page *kvm_spapr_get_tce_page(struct kvmppc_spapr_tce_table *stt, 238*e1a1ef84SAlexey Kardashevskiy unsigned long sttpage) 239*e1a1ef84SAlexey Kardashevskiy { 240*e1a1ef84SAlexey Kardashevskiy struct page *page = stt->pages[sttpage]; 241*e1a1ef84SAlexey Kardashevskiy 242*e1a1ef84SAlexey Kardashevskiy if (page) 243*e1a1ef84SAlexey Kardashevskiy return page; 244*e1a1ef84SAlexey Kardashevskiy 245*e1a1ef84SAlexey Kardashevskiy mutex_lock(&stt->alloc_lock); 246*e1a1ef84SAlexey Kardashevskiy page = stt->pages[sttpage]; 247*e1a1ef84SAlexey Kardashevskiy if (!page) { 248*e1a1ef84SAlexey Kardashevskiy page = alloc_page(GFP_KERNEL | __GFP_ZERO); 249*e1a1ef84SAlexey Kardashevskiy WARN_ON_ONCE(!page); 250*e1a1ef84SAlexey Kardashevskiy if (page) 251*e1a1ef84SAlexey Kardashevskiy stt->pages[sttpage] = page; 252*e1a1ef84SAlexey Kardashevskiy } 253*e1a1ef84SAlexey Kardashevskiy mutex_unlock(&stt->alloc_lock); 254*e1a1ef84SAlexey Kardashevskiy 255*e1a1ef84SAlexey Kardashevskiy return page; 256*e1a1ef84SAlexey Kardashevskiy } 257*e1a1ef84SAlexey Kardashevskiy 25816d5c39dSSouptick Joarder static vm_fault_t kvm_spapr_tce_fault(struct vm_fault *vmf) 259f31e65e1SBenjamin Herrenschmidt { 26011bac800SDave Jiang struct kvmppc_spapr_tce_table *stt = vmf->vma->vm_file->private_data; 261f31e65e1SBenjamin Herrenschmidt struct page *page; 262f31e65e1SBenjamin Herrenschmidt 263fe26e527SAlexey Kardashevskiy if (vmf->pgoff >= kvmppc_tce_pages(stt->size)) 264f31e65e1SBenjamin Herrenschmidt return VM_FAULT_SIGBUS; 265f31e65e1SBenjamin Herrenschmidt 266*e1a1ef84SAlexey Kardashevskiy page = kvm_spapr_get_tce_page(stt, vmf->pgoff); 267*e1a1ef84SAlexey Kardashevskiy if (!page) 268*e1a1ef84SAlexey Kardashevskiy return VM_FAULT_OOM; 269*e1a1ef84SAlexey Kardashevskiy 270f31e65e1SBenjamin Herrenschmidt get_page(page); 271f31e65e1SBenjamin Herrenschmidt vmf->page = page; 272f31e65e1SBenjamin Herrenschmidt return 0; 273f31e65e1SBenjamin Herrenschmidt } 274f31e65e1SBenjamin Herrenschmidt 275f31e65e1SBenjamin Herrenschmidt static const struct vm_operations_struct kvm_spapr_tce_vm_ops = { 276f31e65e1SBenjamin Herrenschmidt .fault = kvm_spapr_tce_fault, 277f31e65e1SBenjamin Herrenschmidt }; 278f31e65e1SBenjamin Herrenschmidt 279f31e65e1SBenjamin Herrenschmidt static int kvm_spapr_tce_mmap(struct file *file, struct vm_area_struct *vma) 280f31e65e1SBenjamin Herrenschmidt { 281f31e65e1SBenjamin Herrenschmidt vma->vm_ops = &kvm_spapr_tce_vm_ops; 282f31e65e1SBenjamin Herrenschmidt return 0; 283f31e65e1SBenjamin Herrenschmidt } 284f31e65e1SBenjamin Herrenschmidt 285f31e65e1SBenjamin Herrenschmidt static int kvm_spapr_tce_release(struct inode *inode, struct file *filp) 286f31e65e1SBenjamin Herrenschmidt { 287f31e65e1SBenjamin Herrenschmidt struct kvmppc_spapr_tce_table *stt = filp->private_data; 288121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit, *tmp; 289edd03602SPaul Mackerras struct kvm *kvm = stt->kvm; 290f31e65e1SBenjamin Herrenschmidt 291edd03602SPaul Mackerras mutex_lock(&kvm->lock); 292366baf28SAlexey Kardashevskiy list_del_rcu(&stt->list); 293edd03602SPaul Mackerras mutex_unlock(&kvm->lock); 294366baf28SAlexey Kardashevskiy 295121f80baSAlexey Kardashevskiy list_for_each_entry_safe(stit, tmp, &stt->iommu_tables, next) { 296121f80baSAlexey Kardashevskiy WARN_ON(!kref_read(&stit->kref)); 297121f80baSAlexey Kardashevskiy while (1) { 298121f80baSAlexey Kardashevskiy if (kref_put(&stit->kref, kvm_spapr_tce_liobn_put)) 299121f80baSAlexey Kardashevskiy break; 300121f80baSAlexey Kardashevskiy } 301121f80baSAlexey Kardashevskiy } 302121f80baSAlexey Kardashevskiy 303366baf28SAlexey Kardashevskiy kvm_put_kvm(stt->kvm); 304366baf28SAlexey Kardashevskiy 305f8626985SAlexey Kardashevskiy kvmppc_account_memlimit( 306fe26e527SAlexey Kardashevskiy kvmppc_stt_pages(kvmppc_tce_pages(stt->size)), false); 307366baf28SAlexey Kardashevskiy call_rcu(&stt->rcu, release_spapr_tce_table); 308366baf28SAlexey Kardashevskiy 309f31e65e1SBenjamin Herrenschmidt return 0; 310f31e65e1SBenjamin Herrenschmidt } 311f31e65e1SBenjamin Herrenschmidt 31275ef9de1SAl Viro static const struct file_operations kvm_spapr_tce_fops = { 313f31e65e1SBenjamin Herrenschmidt .mmap = kvm_spapr_tce_mmap, 314f31e65e1SBenjamin Herrenschmidt .release = kvm_spapr_tce_release, 315f31e65e1SBenjamin Herrenschmidt }; 316f31e65e1SBenjamin Herrenschmidt 317f31e65e1SBenjamin Herrenschmidt long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm, 31858ded420SAlexey Kardashevskiy struct kvm_create_spapr_tce_64 *args) 319f31e65e1SBenjamin Herrenschmidt { 320f31e65e1SBenjamin Herrenschmidt struct kvmppc_spapr_tce_table *stt = NULL; 32147c5310aSPaul Mackerras struct kvmppc_spapr_tce_table *siter; 32276346cd9SAlexey Kardashevskiy unsigned long npages, size = args->size; 323f31e65e1SBenjamin Herrenschmidt int ret = -ENOMEM; 324f31e65e1SBenjamin Herrenschmidt 325e45719afSAlexey Kardashevskiy if (!args->size || args->page_shift < 12 || args->page_shift > 34 || 326e45719afSAlexey Kardashevskiy (args->offset + args->size > (ULLONG_MAX >> args->page_shift))) 32758ded420SAlexey Kardashevskiy return -EINVAL; 32858ded420SAlexey Kardashevskiy 329fe26e527SAlexey Kardashevskiy npages = kvmppc_tce_pages(size); 330f8626985SAlexey Kardashevskiy ret = kvmppc_account_memlimit(kvmppc_stt_pages(npages), true); 33147c5310aSPaul Mackerras if (ret) 33247c5310aSPaul Mackerras return ret; 333f31e65e1SBenjamin Herrenschmidt 3345982f084SWei Yongjun ret = -ENOMEM; 335f31e65e1SBenjamin Herrenschmidt stt = kzalloc(sizeof(*stt) + npages * sizeof(struct page *), 336f31e65e1SBenjamin Herrenschmidt GFP_KERNEL); 337f31e65e1SBenjamin Herrenschmidt if (!stt) 33847c5310aSPaul Mackerras goto fail_acct; 339f31e65e1SBenjamin Herrenschmidt 340f31e65e1SBenjamin Herrenschmidt stt->liobn = args->liobn; 34158ded420SAlexey Kardashevskiy stt->page_shift = args->page_shift; 34258ded420SAlexey Kardashevskiy stt->offset = args->offset; 343fe26e527SAlexey Kardashevskiy stt->size = size; 344f31e65e1SBenjamin Herrenschmidt stt->kvm = kvm; 345*e1a1ef84SAlexey Kardashevskiy mutex_init(&stt->alloc_lock); 346121f80baSAlexey Kardashevskiy INIT_LIST_HEAD_RCU(&stt->iommu_tables); 347f31e65e1SBenjamin Herrenschmidt 348f31e65e1SBenjamin Herrenschmidt mutex_lock(&kvm->lock); 34947c5310aSPaul Mackerras 35047c5310aSPaul Mackerras /* Check this LIOBN hasn't been previously allocated */ 35147c5310aSPaul Mackerras ret = 0; 35247c5310aSPaul Mackerras list_for_each_entry(siter, &kvm->arch.spapr_tce_tables, list) { 35347c5310aSPaul Mackerras if (siter->liobn == args->liobn) { 35447c5310aSPaul Mackerras ret = -EBUSY; 35547c5310aSPaul Mackerras break; 35647c5310aSPaul Mackerras } 35747c5310aSPaul Mackerras } 35847c5310aSPaul Mackerras 359716cb116SAlexey Kardashevskiy kvm_get_kvm(kvm); 360edd03602SPaul Mackerras if (!ret) 361edd03602SPaul Mackerras ret = anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops, 362edd03602SPaul Mackerras stt, O_RDWR | O_CLOEXEC); 363edd03602SPaul Mackerras 364716cb116SAlexey Kardashevskiy if (ret >= 0) 365366baf28SAlexey Kardashevskiy list_add_rcu(&stt->list, &kvm->arch.spapr_tce_tables); 366716cb116SAlexey Kardashevskiy else 367716cb116SAlexey Kardashevskiy kvm_put_kvm(kvm); 368f31e65e1SBenjamin Herrenschmidt 369f31e65e1SBenjamin Herrenschmidt mutex_unlock(&kvm->lock); 370f31e65e1SBenjamin Herrenschmidt 371edd03602SPaul Mackerras if (ret >= 0) 372edd03602SPaul Mackerras return ret; 373f31e65e1SBenjamin Herrenschmidt 374f31e65e1SBenjamin Herrenschmidt kfree(stt); 37547c5310aSPaul Mackerras fail_acct: 37647c5310aSPaul Mackerras kvmppc_account_memlimit(kvmppc_stt_pages(npages), false); 377f31e65e1SBenjamin Herrenschmidt return ret; 378f31e65e1SBenjamin Herrenschmidt } 379d3695aa4SAlexey Kardashevskiy 3802001825eSAlexey Kardashevskiy static long kvmppc_tce_to_ua(struct kvm *kvm, unsigned long tce, 3812001825eSAlexey Kardashevskiy unsigned long *ua) 3822001825eSAlexey Kardashevskiy { 3832001825eSAlexey Kardashevskiy unsigned long gfn = tce >> PAGE_SHIFT; 3842001825eSAlexey Kardashevskiy struct kvm_memory_slot *memslot; 3852001825eSAlexey Kardashevskiy 3862001825eSAlexey Kardashevskiy memslot = search_memslots(kvm_memslots(kvm), gfn); 3872001825eSAlexey Kardashevskiy if (!memslot) 3882001825eSAlexey Kardashevskiy return -EINVAL; 3892001825eSAlexey Kardashevskiy 3902001825eSAlexey Kardashevskiy *ua = __gfn_to_hva_memslot(memslot, gfn) | 3912001825eSAlexey Kardashevskiy (tce & ~(PAGE_MASK | TCE_PCI_READ | TCE_PCI_WRITE)); 3922001825eSAlexey Kardashevskiy 3932001825eSAlexey Kardashevskiy return 0; 3942001825eSAlexey Kardashevskiy } 3952001825eSAlexey Kardashevskiy 39642de7b9eSAlexey Kardashevskiy static long kvmppc_tce_validate(struct kvmppc_spapr_tce_table *stt, 39742de7b9eSAlexey Kardashevskiy unsigned long tce) 39842de7b9eSAlexey Kardashevskiy { 39942de7b9eSAlexey Kardashevskiy unsigned long gpa = tce & ~(TCE_PCI_READ | TCE_PCI_WRITE); 40042de7b9eSAlexey Kardashevskiy enum dma_data_direction dir = iommu_tce_direction(tce); 40142de7b9eSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit; 40242de7b9eSAlexey Kardashevskiy unsigned long ua = 0; 40342de7b9eSAlexey Kardashevskiy 40442de7b9eSAlexey Kardashevskiy /* Allow userspace to poison TCE table */ 40542de7b9eSAlexey Kardashevskiy if (dir == DMA_NONE) 40642de7b9eSAlexey Kardashevskiy return H_SUCCESS; 40742de7b9eSAlexey Kardashevskiy 40842de7b9eSAlexey Kardashevskiy if (iommu_tce_check_gpa(stt->page_shift, gpa)) 40942de7b9eSAlexey Kardashevskiy return H_TOO_HARD; 41042de7b9eSAlexey Kardashevskiy 4112001825eSAlexey Kardashevskiy if (kvmppc_tce_to_ua(stt->kvm, tce, &ua)) 41242de7b9eSAlexey Kardashevskiy return H_TOO_HARD; 41342de7b9eSAlexey Kardashevskiy 41442de7b9eSAlexey Kardashevskiy list_for_each_entry_rcu(stit, &stt->iommu_tables, next) { 41542de7b9eSAlexey Kardashevskiy unsigned long hpa = 0; 41642de7b9eSAlexey Kardashevskiy struct mm_iommu_table_group_mem_t *mem; 41742de7b9eSAlexey Kardashevskiy long shift = stit->tbl->it_page_shift; 41842de7b9eSAlexey Kardashevskiy 41942de7b9eSAlexey Kardashevskiy mem = mm_iommu_lookup(stt->kvm->mm, ua, 1ULL << shift); 42042de7b9eSAlexey Kardashevskiy if (!mem) 42142de7b9eSAlexey Kardashevskiy return H_TOO_HARD; 42242de7b9eSAlexey Kardashevskiy 42342de7b9eSAlexey Kardashevskiy if (mm_iommu_ua_to_hpa(mem, ua, shift, &hpa)) 42442de7b9eSAlexey Kardashevskiy return H_TOO_HARD; 42542de7b9eSAlexey Kardashevskiy } 42642de7b9eSAlexey Kardashevskiy 42742de7b9eSAlexey Kardashevskiy return H_SUCCESS; 42842de7b9eSAlexey Kardashevskiy } 42942de7b9eSAlexey Kardashevskiy 430*e1a1ef84SAlexey Kardashevskiy /* 431*e1a1ef84SAlexey Kardashevskiy * Handles TCE requests for emulated devices. 432*e1a1ef84SAlexey Kardashevskiy * Puts guest TCE values to the table and expects user space to convert them. 433*e1a1ef84SAlexey Kardashevskiy * Cannot fail so kvmppc_tce_validate must be called before it. 434*e1a1ef84SAlexey Kardashevskiy */ 435*e1a1ef84SAlexey Kardashevskiy static void kvmppc_tce_put(struct kvmppc_spapr_tce_table *stt, 436*e1a1ef84SAlexey Kardashevskiy unsigned long idx, unsigned long tce) 437*e1a1ef84SAlexey Kardashevskiy { 438*e1a1ef84SAlexey Kardashevskiy struct page *page; 439*e1a1ef84SAlexey Kardashevskiy u64 *tbl; 440*e1a1ef84SAlexey Kardashevskiy unsigned long sttpage; 441*e1a1ef84SAlexey Kardashevskiy 442*e1a1ef84SAlexey Kardashevskiy idx -= stt->offset; 443*e1a1ef84SAlexey Kardashevskiy sttpage = idx / TCES_PER_PAGE; 444*e1a1ef84SAlexey Kardashevskiy page = stt->pages[sttpage]; 445*e1a1ef84SAlexey Kardashevskiy 446*e1a1ef84SAlexey Kardashevskiy if (!page) { 447*e1a1ef84SAlexey Kardashevskiy /* We allow any TCE, not just with read|write permissions */ 448*e1a1ef84SAlexey Kardashevskiy if (!tce) 449*e1a1ef84SAlexey Kardashevskiy return; 450*e1a1ef84SAlexey Kardashevskiy 451*e1a1ef84SAlexey Kardashevskiy page = kvm_spapr_get_tce_page(stt, sttpage); 452*e1a1ef84SAlexey Kardashevskiy if (!page) 453*e1a1ef84SAlexey Kardashevskiy return; 454*e1a1ef84SAlexey Kardashevskiy } 455*e1a1ef84SAlexey Kardashevskiy tbl = page_to_virt(page); 456*e1a1ef84SAlexey Kardashevskiy 457*e1a1ef84SAlexey Kardashevskiy tbl[idx % TCES_PER_PAGE] = tce; 458*e1a1ef84SAlexey Kardashevskiy } 459*e1a1ef84SAlexey Kardashevskiy 460c10c21efSAlexey Kardashevskiy static void kvmppc_clear_tce(struct mm_struct *mm, struct iommu_table *tbl, 461c10c21efSAlexey Kardashevskiy unsigned long entry) 462121f80baSAlexey Kardashevskiy { 463121f80baSAlexey Kardashevskiy unsigned long hpa = 0; 464121f80baSAlexey Kardashevskiy enum dma_data_direction dir = DMA_NONE; 465121f80baSAlexey Kardashevskiy 466c10c21efSAlexey Kardashevskiy iommu_tce_xchg(mm, tbl, entry, &hpa, &dir); 467121f80baSAlexey Kardashevskiy } 468121f80baSAlexey Kardashevskiy 469121f80baSAlexey Kardashevskiy static long kvmppc_tce_iommu_mapped_dec(struct kvm *kvm, 470121f80baSAlexey Kardashevskiy struct iommu_table *tbl, unsigned long entry) 471121f80baSAlexey Kardashevskiy { 472121f80baSAlexey Kardashevskiy struct mm_iommu_table_group_mem_t *mem = NULL; 473121f80baSAlexey Kardashevskiy const unsigned long pgsize = 1ULL << tbl->it_page_shift; 4746e301a8eSAlexey Kardashevskiy __be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY_RO(tbl, entry); 475121f80baSAlexey Kardashevskiy 476121f80baSAlexey Kardashevskiy if (!pua) 4776e301a8eSAlexey Kardashevskiy return H_SUCCESS; 478121f80baSAlexey Kardashevskiy 47900a5c58dSAlexey Kardashevskiy mem = mm_iommu_lookup(kvm->mm, be64_to_cpu(*pua), pgsize); 480121f80baSAlexey Kardashevskiy if (!mem) 481121f80baSAlexey Kardashevskiy return H_TOO_HARD; 482121f80baSAlexey Kardashevskiy 483121f80baSAlexey Kardashevskiy mm_iommu_mapped_dec(mem); 484121f80baSAlexey Kardashevskiy 48500a5c58dSAlexey Kardashevskiy *pua = cpu_to_be64(0); 486121f80baSAlexey Kardashevskiy 487121f80baSAlexey Kardashevskiy return H_SUCCESS; 488121f80baSAlexey Kardashevskiy } 489121f80baSAlexey Kardashevskiy 490ca1fc489SAlexey Kardashevskiy static long kvmppc_tce_iommu_do_unmap(struct kvm *kvm, 491121f80baSAlexey Kardashevskiy struct iommu_table *tbl, unsigned long entry) 492121f80baSAlexey Kardashevskiy { 493121f80baSAlexey Kardashevskiy enum dma_data_direction dir = DMA_NONE; 494121f80baSAlexey Kardashevskiy unsigned long hpa = 0; 495121f80baSAlexey Kardashevskiy long ret; 496121f80baSAlexey Kardashevskiy 497c10c21efSAlexey Kardashevskiy if (WARN_ON_ONCE(iommu_tce_xchg(kvm->mm, tbl, entry, &hpa, &dir))) 498f7960e29SAlexey Kardashevskiy return H_TOO_HARD; 499121f80baSAlexey Kardashevskiy 500121f80baSAlexey Kardashevskiy if (dir == DMA_NONE) 501121f80baSAlexey Kardashevskiy return H_SUCCESS; 502121f80baSAlexey Kardashevskiy 503121f80baSAlexey Kardashevskiy ret = kvmppc_tce_iommu_mapped_dec(kvm, tbl, entry); 504121f80baSAlexey Kardashevskiy if (ret != H_SUCCESS) 505c10c21efSAlexey Kardashevskiy iommu_tce_xchg(kvm->mm, tbl, entry, &hpa, &dir); 506121f80baSAlexey Kardashevskiy 507121f80baSAlexey Kardashevskiy return ret; 508121f80baSAlexey Kardashevskiy } 509121f80baSAlexey Kardashevskiy 510ca1fc489SAlexey Kardashevskiy static long kvmppc_tce_iommu_unmap(struct kvm *kvm, 511ca1fc489SAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt, struct iommu_table *tbl, 512ca1fc489SAlexey Kardashevskiy unsigned long entry) 513ca1fc489SAlexey Kardashevskiy { 514ca1fc489SAlexey Kardashevskiy unsigned long i, ret = H_SUCCESS; 515ca1fc489SAlexey Kardashevskiy unsigned long subpages = 1ULL << (stt->page_shift - tbl->it_page_shift); 516ca1fc489SAlexey Kardashevskiy unsigned long io_entry = entry * subpages; 517ca1fc489SAlexey Kardashevskiy 518ca1fc489SAlexey Kardashevskiy for (i = 0; i < subpages; ++i) { 519ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_do_unmap(kvm, tbl, io_entry + i); 520ca1fc489SAlexey Kardashevskiy if (ret != H_SUCCESS) 521ca1fc489SAlexey Kardashevskiy break; 522ca1fc489SAlexey Kardashevskiy } 523ca1fc489SAlexey Kardashevskiy 524ca1fc489SAlexey Kardashevskiy return ret; 525ca1fc489SAlexey Kardashevskiy } 526ca1fc489SAlexey Kardashevskiy 527ca1fc489SAlexey Kardashevskiy long kvmppc_tce_iommu_do_map(struct kvm *kvm, struct iommu_table *tbl, 528121f80baSAlexey Kardashevskiy unsigned long entry, unsigned long ua, 529121f80baSAlexey Kardashevskiy enum dma_data_direction dir) 530121f80baSAlexey Kardashevskiy { 531121f80baSAlexey Kardashevskiy long ret; 53200a5c58dSAlexey Kardashevskiy unsigned long hpa; 53300a5c58dSAlexey Kardashevskiy __be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY(tbl, entry); 534121f80baSAlexey Kardashevskiy struct mm_iommu_table_group_mem_t *mem; 535121f80baSAlexey Kardashevskiy 536121f80baSAlexey Kardashevskiy if (!pua) 537121f80baSAlexey Kardashevskiy /* it_userspace allocation might be delayed */ 538121f80baSAlexey Kardashevskiy return H_TOO_HARD; 539121f80baSAlexey Kardashevskiy 540121f80baSAlexey Kardashevskiy mem = mm_iommu_lookup(kvm->mm, ua, 1ULL << tbl->it_page_shift); 541121f80baSAlexey Kardashevskiy if (!mem) 542121f80baSAlexey Kardashevskiy /* This only handles v2 IOMMU type, v1 is handled via ioctl() */ 543121f80baSAlexey Kardashevskiy return H_TOO_HARD; 544121f80baSAlexey Kardashevskiy 54576fa4975SAlexey Kardashevskiy if (WARN_ON_ONCE(mm_iommu_ua_to_hpa(mem, ua, tbl->it_page_shift, &hpa))) 546f7960e29SAlexey Kardashevskiy return H_TOO_HARD; 547121f80baSAlexey Kardashevskiy 548121f80baSAlexey Kardashevskiy if (mm_iommu_mapped_inc(mem)) 549f7960e29SAlexey Kardashevskiy return H_TOO_HARD; 550121f80baSAlexey Kardashevskiy 551c10c21efSAlexey Kardashevskiy ret = iommu_tce_xchg(kvm->mm, tbl, entry, &hpa, &dir); 552121f80baSAlexey Kardashevskiy if (WARN_ON_ONCE(ret)) { 553121f80baSAlexey Kardashevskiy mm_iommu_mapped_dec(mem); 554f7960e29SAlexey Kardashevskiy return H_TOO_HARD; 555121f80baSAlexey Kardashevskiy } 556121f80baSAlexey Kardashevskiy 557121f80baSAlexey Kardashevskiy if (dir != DMA_NONE) 558121f80baSAlexey Kardashevskiy kvmppc_tce_iommu_mapped_dec(kvm, tbl, entry); 559121f80baSAlexey Kardashevskiy 56000a5c58dSAlexey Kardashevskiy *pua = cpu_to_be64(ua); 561121f80baSAlexey Kardashevskiy 562121f80baSAlexey Kardashevskiy return 0; 563121f80baSAlexey Kardashevskiy } 564121f80baSAlexey Kardashevskiy 565ca1fc489SAlexey Kardashevskiy static long kvmppc_tce_iommu_map(struct kvm *kvm, 566ca1fc489SAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt, struct iommu_table *tbl, 567ca1fc489SAlexey Kardashevskiy unsigned long entry, unsigned long ua, 568ca1fc489SAlexey Kardashevskiy enum dma_data_direction dir) 569ca1fc489SAlexey Kardashevskiy { 570ca1fc489SAlexey Kardashevskiy unsigned long i, pgoff, ret = H_SUCCESS; 571ca1fc489SAlexey Kardashevskiy unsigned long subpages = 1ULL << (stt->page_shift - tbl->it_page_shift); 572ca1fc489SAlexey Kardashevskiy unsigned long io_entry = entry * subpages; 573ca1fc489SAlexey Kardashevskiy 574ca1fc489SAlexey Kardashevskiy for (i = 0, pgoff = 0; i < subpages; 575ca1fc489SAlexey Kardashevskiy ++i, pgoff += IOMMU_PAGE_SIZE(tbl)) { 576ca1fc489SAlexey Kardashevskiy 577ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_do_map(kvm, tbl, 578ca1fc489SAlexey Kardashevskiy io_entry + i, ua + pgoff, dir); 579ca1fc489SAlexey Kardashevskiy if (ret != H_SUCCESS) 580ca1fc489SAlexey Kardashevskiy break; 581ca1fc489SAlexey Kardashevskiy } 582ca1fc489SAlexey Kardashevskiy 583ca1fc489SAlexey Kardashevskiy return ret; 584ca1fc489SAlexey Kardashevskiy } 585ca1fc489SAlexey Kardashevskiy 58631217db7SAlexey Kardashevskiy long kvmppc_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn, 58731217db7SAlexey Kardashevskiy unsigned long ioba, unsigned long tce) 58831217db7SAlexey Kardashevskiy { 589503bfcbeSAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt; 590121f80baSAlexey Kardashevskiy long ret, idx; 591121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit; 592121f80baSAlexey Kardashevskiy unsigned long entry, ua = 0; 593121f80baSAlexey Kardashevskiy enum dma_data_direction dir; 59431217db7SAlexey Kardashevskiy 59531217db7SAlexey Kardashevskiy /* udbg_printf("H_PUT_TCE(): liobn=0x%lx ioba=0x%lx, tce=0x%lx\n", */ 59631217db7SAlexey Kardashevskiy /* liobn, ioba, tce); */ 59731217db7SAlexey Kardashevskiy 598503bfcbeSAlexey Kardashevskiy stt = kvmppc_find_table(vcpu->kvm, liobn); 59931217db7SAlexey Kardashevskiy if (!stt) 60031217db7SAlexey Kardashevskiy return H_TOO_HARD; 60131217db7SAlexey Kardashevskiy 60231217db7SAlexey Kardashevskiy ret = kvmppc_ioba_validate(stt, ioba, 1); 60331217db7SAlexey Kardashevskiy if (ret != H_SUCCESS) 60431217db7SAlexey Kardashevskiy return ret; 60531217db7SAlexey Kardashevskiy 606345077c8SAlexey Kardashevskiy idx = srcu_read_lock(&vcpu->kvm->srcu); 607345077c8SAlexey Kardashevskiy 60831217db7SAlexey Kardashevskiy ret = kvmppc_tce_validate(stt, tce); 60931217db7SAlexey Kardashevskiy if (ret != H_SUCCESS) 610345077c8SAlexey Kardashevskiy goto unlock_exit; 61131217db7SAlexey Kardashevskiy 612121f80baSAlexey Kardashevskiy dir = iommu_tce_direction(tce); 6138f6a9f0dSAlexey Kardashevskiy 6142001825eSAlexey Kardashevskiy if ((dir != DMA_NONE) && kvmppc_tce_to_ua(vcpu->kvm, tce, &ua)) { 6158f6a9f0dSAlexey Kardashevskiy ret = H_PARAMETER; 6168f6a9f0dSAlexey Kardashevskiy goto unlock_exit; 6178f6a9f0dSAlexey Kardashevskiy } 618121f80baSAlexey Kardashevskiy 619121f80baSAlexey Kardashevskiy entry = ioba >> stt->page_shift; 620121f80baSAlexey Kardashevskiy 621121f80baSAlexey Kardashevskiy list_for_each_entry_lockless(stit, &stt->iommu_tables, next) { 6228f6a9f0dSAlexey Kardashevskiy if (dir == DMA_NONE) 623ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_unmap(vcpu->kvm, stt, 624121f80baSAlexey Kardashevskiy stit->tbl, entry); 6258f6a9f0dSAlexey Kardashevskiy else 626ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_map(vcpu->kvm, stt, stit->tbl, 627121f80baSAlexey Kardashevskiy entry, ua, dir); 628121f80baSAlexey Kardashevskiy 6292691f0ffSAlexey Kardashevskiy if (ret != H_SUCCESS) { 630c10c21efSAlexey Kardashevskiy kvmppc_clear_tce(vcpu->kvm->mm, stit->tbl, entry); 6312691f0ffSAlexey Kardashevskiy goto unlock_exit; 6322691f0ffSAlexey Kardashevskiy } 633121f80baSAlexey Kardashevskiy } 634121f80baSAlexey Kardashevskiy 635121f80baSAlexey Kardashevskiy kvmppc_tce_put(stt, entry, tce); 63631217db7SAlexey Kardashevskiy 6378f6a9f0dSAlexey Kardashevskiy unlock_exit: 6388f6a9f0dSAlexey Kardashevskiy srcu_read_unlock(&vcpu->kvm->srcu, idx); 6398f6a9f0dSAlexey Kardashevskiy 6408f6a9f0dSAlexey Kardashevskiy return ret; 64131217db7SAlexey Kardashevskiy } 64231217db7SAlexey Kardashevskiy EXPORT_SYMBOL_GPL(kvmppc_h_put_tce); 64331217db7SAlexey Kardashevskiy 644d3695aa4SAlexey Kardashevskiy long kvmppc_h_put_tce_indirect(struct kvm_vcpu *vcpu, 645d3695aa4SAlexey Kardashevskiy unsigned long liobn, unsigned long ioba, 646d3695aa4SAlexey Kardashevskiy unsigned long tce_list, unsigned long npages) 647d3695aa4SAlexey Kardashevskiy { 648d3695aa4SAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt; 649d3695aa4SAlexey Kardashevskiy long i, ret = H_SUCCESS, idx; 650d3695aa4SAlexey Kardashevskiy unsigned long entry, ua = 0; 651f8750513SDaniel Axtens u64 __user *tces; 652f8750513SDaniel Axtens u64 tce; 653121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit; 654d3695aa4SAlexey Kardashevskiy 655503bfcbeSAlexey Kardashevskiy stt = kvmppc_find_table(vcpu->kvm, liobn); 656d3695aa4SAlexey Kardashevskiy if (!stt) 657d3695aa4SAlexey Kardashevskiy return H_TOO_HARD; 658d3695aa4SAlexey Kardashevskiy 659fe26e527SAlexey Kardashevskiy entry = ioba >> stt->page_shift; 660d3695aa4SAlexey Kardashevskiy /* 661d3695aa4SAlexey Kardashevskiy * SPAPR spec says that the maximum size of the list is 512 TCEs 662d3695aa4SAlexey Kardashevskiy * so the whole table fits in 4K page 663d3695aa4SAlexey Kardashevskiy */ 664d3695aa4SAlexey Kardashevskiy if (npages > 512) 665d3695aa4SAlexey Kardashevskiy return H_PARAMETER; 666d3695aa4SAlexey Kardashevskiy 667d3695aa4SAlexey Kardashevskiy if (tce_list & (SZ_4K - 1)) 668d3695aa4SAlexey Kardashevskiy return H_PARAMETER; 669d3695aa4SAlexey Kardashevskiy 670d3695aa4SAlexey Kardashevskiy ret = kvmppc_ioba_validate(stt, ioba, npages); 671d3695aa4SAlexey Kardashevskiy if (ret != H_SUCCESS) 672d3695aa4SAlexey Kardashevskiy return ret; 673d3695aa4SAlexey Kardashevskiy 674d3695aa4SAlexey Kardashevskiy idx = srcu_read_lock(&vcpu->kvm->srcu); 6752001825eSAlexey Kardashevskiy if (kvmppc_tce_to_ua(vcpu->kvm, tce_list, &ua)) { 676d3695aa4SAlexey Kardashevskiy ret = H_TOO_HARD; 677d3695aa4SAlexey Kardashevskiy goto unlock_exit; 678d3695aa4SAlexey Kardashevskiy } 679d3695aa4SAlexey Kardashevskiy tces = (u64 __user *) ua; 680d3695aa4SAlexey Kardashevskiy 681d3695aa4SAlexey Kardashevskiy for (i = 0; i < npages; ++i) { 682d3695aa4SAlexey Kardashevskiy if (get_user(tce, tces + i)) { 683d3695aa4SAlexey Kardashevskiy ret = H_TOO_HARD; 684d3695aa4SAlexey Kardashevskiy goto unlock_exit; 685d3695aa4SAlexey Kardashevskiy } 686d3695aa4SAlexey Kardashevskiy tce = be64_to_cpu(tce); 687d3695aa4SAlexey Kardashevskiy 688d3695aa4SAlexey Kardashevskiy ret = kvmppc_tce_validate(stt, tce); 689d3695aa4SAlexey Kardashevskiy if (ret != H_SUCCESS) 690d3695aa4SAlexey Kardashevskiy goto unlock_exit; 691e199ad2bSAlexey Kardashevskiy } 692e199ad2bSAlexey Kardashevskiy 693e199ad2bSAlexey Kardashevskiy for (i = 0; i < npages; ++i) { 694e199ad2bSAlexey Kardashevskiy /* 695e199ad2bSAlexey Kardashevskiy * This looks unsafe, because we validate, then regrab 696e199ad2bSAlexey Kardashevskiy * the TCE from userspace which could have been changed by 697e199ad2bSAlexey Kardashevskiy * another thread. 698e199ad2bSAlexey Kardashevskiy * 699e199ad2bSAlexey Kardashevskiy * But it actually is safe, because the relevant checks will be 700e199ad2bSAlexey Kardashevskiy * re-executed in the following code. If userspace tries to 701e199ad2bSAlexey Kardashevskiy * change this dodgily it will result in a messier failure mode 702e199ad2bSAlexey Kardashevskiy * but won't threaten the host. 703e199ad2bSAlexey Kardashevskiy */ 704e199ad2bSAlexey Kardashevskiy if (get_user(tce, tces + i)) { 705e199ad2bSAlexey Kardashevskiy ret = H_TOO_HARD; 706e199ad2bSAlexey Kardashevskiy goto unlock_exit; 707e199ad2bSAlexey Kardashevskiy } 708e199ad2bSAlexey Kardashevskiy tce = be64_to_cpu(tce); 709d3695aa4SAlexey Kardashevskiy 7102001825eSAlexey Kardashevskiy if (kvmppc_tce_to_ua(vcpu->kvm, tce, &ua)) 711121f80baSAlexey Kardashevskiy return H_PARAMETER; 712121f80baSAlexey Kardashevskiy 713121f80baSAlexey Kardashevskiy list_for_each_entry_lockless(stit, &stt->iommu_tables, next) { 714ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_map(vcpu->kvm, stt, 715121f80baSAlexey Kardashevskiy stit->tbl, entry + i, ua, 716121f80baSAlexey Kardashevskiy iommu_tce_direction(tce)); 717121f80baSAlexey Kardashevskiy 7182691f0ffSAlexey Kardashevskiy if (ret != H_SUCCESS) { 719c10c21efSAlexey Kardashevskiy kvmppc_clear_tce(vcpu->kvm->mm, stit->tbl, 720c10c21efSAlexey Kardashevskiy entry); 7212691f0ffSAlexey Kardashevskiy goto unlock_exit; 7222691f0ffSAlexey Kardashevskiy } 723121f80baSAlexey Kardashevskiy } 724121f80baSAlexey Kardashevskiy 725d3695aa4SAlexey Kardashevskiy kvmppc_tce_put(stt, entry + i, tce); 726d3695aa4SAlexey Kardashevskiy } 727d3695aa4SAlexey Kardashevskiy 728d3695aa4SAlexey Kardashevskiy unlock_exit: 729d3695aa4SAlexey Kardashevskiy srcu_read_unlock(&vcpu->kvm->srcu, idx); 730d3695aa4SAlexey Kardashevskiy 731d3695aa4SAlexey Kardashevskiy return ret; 732d3695aa4SAlexey Kardashevskiy } 733d3695aa4SAlexey Kardashevskiy EXPORT_SYMBOL_GPL(kvmppc_h_put_tce_indirect); 73431217db7SAlexey Kardashevskiy 73531217db7SAlexey Kardashevskiy long kvmppc_h_stuff_tce(struct kvm_vcpu *vcpu, 73631217db7SAlexey Kardashevskiy unsigned long liobn, unsigned long ioba, 73731217db7SAlexey Kardashevskiy unsigned long tce_value, unsigned long npages) 73831217db7SAlexey Kardashevskiy { 73931217db7SAlexey Kardashevskiy struct kvmppc_spapr_tce_table *stt; 74031217db7SAlexey Kardashevskiy long i, ret; 741121f80baSAlexey Kardashevskiy struct kvmppc_spapr_tce_iommu_table *stit; 74231217db7SAlexey Kardashevskiy 743503bfcbeSAlexey Kardashevskiy stt = kvmppc_find_table(vcpu->kvm, liobn); 74431217db7SAlexey Kardashevskiy if (!stt) 74531217db7SAlexey Kardashevskiy return H_TOO_HARD; 74631217db7SAlexey Kardashevskiy 74731217db7SAlexey Kardashevskiy ret = kvmppc_ioba_validate(stt, ioba, npages); 74831217db7SAlexey Kardashevskiy if (ret != H_SUCCESS) 74931217db7SAlexey Kardashevskiy return ret; 75031217db7SAlexey Kardashevskiy 75131217db7SAlexey Kardashevskiy /* Check permission bits only to allow userspace poison TCE for debug */ 75231217db7SAlexey Kardashevskiy if (tce_value & (TCE_PCI_WRITE | TCE_PCI_READ)) 75331217db7SAlexey Kardashevskiy return H_PARAMETER; 75431217db7SAlexey Kardashevskiy 755121f80baSAlexey Kardashevskiy list_for_each_entry_lockless(stit, &stt->iommu_tables, next) { 756c6b61661SAlexey Kardashevskiy unsigned long entry = ioba >> stt->page_shift; 757121f80baSAlexey Kardashevskiy 758121f80baSAlexey Kardashevskiy for (i = 0; i < npages; ++i) { 759ca1fc489SAlexey Kardashevskiy ret = kvmppc_tce_iommu_unmap(vcpu->kvm, stt, 760121f80baSAlexey Kardashevskiy stit->tbl, entry + i); 761121f80baSAlexey Kardashevskiy 762121f80baSAlexey Kardashevskiy if (ret == H_SUCCESS) 763121f80baSAlexey Kardashevskiy continue; 764121f80baSAlexey Kardashevskiy 765121f80baSAlexey Kardashevskiy if (ret == H_TOO_HARD) 766121f80baSAlexey Kardashevskiy return ret; 767121f80baSAlexey Kardashevskiy 768121f80baSAlexey Kardashevskiy WARN_ON_ONCE(1); 769c10c21efSAlexey Kardashevskiy kvmppc_clear_tce(vcpu->kvm->mm, stit->tbl, entry); 770121f80baSAlexey Kardashevskiy } 771121f80baSAlexey Kardashevskiy } 772121f80baSAlexey Kardashevskiy 77331217db7SAlexey Kardashevskiy for (i = 0; i < npages; ++i, ioba += (1ULL << stt->page_shift)) 77431217db7SAlexey Kardashevskiy kvmppc_tce_put(stt, ioba >> stt->page_shift, tce_value); 77531217db7SAlexey Kardashevskiy 77631217db7SAlexey Kardashevskiy return H_SUCCESS; 77731217db7SAlexey Kardashevskiy } 77831217db7SAlexey Kardashevskiy EXPORT_SYMBOL_GPL(kvmppc_h_stuff_tce); 779