xref: /linux/arch/powerpc/kvm/book3s_64_vio.c (revision 79eb597cba06c435b72f220e9d426ae413fc2579)
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