blob: 88622be0d6b58b965de4cbe9eee22323d1a5a181 [file] [log] [blame]
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +10001/*
2 * VFIO: IOMMU DMA mapping support for TCE on POWER
3 *
4 * Copyright (C) 2013 IBM Corp. All rights reserved.
5 * Author: Alexey Kardashevskiy <aik@ozlabs.ru>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * Derived from original vfio_iommu_type1.c:
12 * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
13 * Author: Alex Williamson <alex.williamson@redhat.com>
14 */
15
16#include <linux/module.h>
17#include <linux/pci.h>
18#include <linux/slab.h>
19#include <linux/uaccess.h>
20#include <linux/err.h>
21#include <linux/vfio.h>
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +100022#include <linux/vmalloc.h>
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +100023#include <asm/iommu.h>
24#include <asm/tce.h>
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +100025#include <asm/mmu_context.h>
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +100026
27#define DRIVER_VERSION "0.1"
28#define DRIVER_AUTHOR "aik@ozlabs.ru"
29#define DRIVER_DESC "VFIO IOMMU SPAPR TCE"
30
31static void tce_iommu_detach_group(void *iommu_data,
32 struct iommu_group *iommu_group);
33
Alexey Kardashevskiy2d270df2015-06-05 16:35:01 +100034static long try_increment_locked_vm(long npages)
35{
36 long ret = 0, locked, lock_limit;
37
38 if (!current || !current->mm)
39 return -ESRCH; /* process exited */
40
41 if (!npages)
42 return 0;
43
44 down_write(&current->mm->mmap_sem);
45 locked = current->mm->locked_vm + npages;
46 lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
47 if (locked > lock_limit && !capable(CAP_IPC_LOCK))
48 ret = -ENOMEM;
49 else
50 current->mm->locked_vm += npages;
51
52 pr_debug("[%d] RLIMIT_MEMLOCK +%ld %ld/%ld%s\n", current->pid,
53 npages << PAGE_SHIFT,
54 current->mm->locked_vm << PAGE_SHIFT,
55 rlimit(RLIMIT_MEMLOCK),
56 ret ? " - exceeded" : "");
57
58 up_write(&current->mm->mmap_sem);
59
60 return ret;
61}
62
63static void decrement_locked_vm(long npages)
64{
65 if (!current || !current->mm || !npages)
66 return; /* process exited */
67
68 down_write(&current->mm->mmap_sem);
69 if (WARN_ON_ONCE(npages > current->mm->locked_vm))
70 npages = current->mm->locked_vm;
71 current->mm->locked_vm -= npages;
72 pr_debug("[%d] RLIMIT_MEMLOCK -%ld %ld/%ld\n", current->pid,
73 npages << PAGE_SHIFT,
74 current->mm->locked_vm << PAGE_SHIFT,
75 rlimit(RLIMIT_MEMLOCK));
76 up_write(&current->mm->mmap_sem);
77}
78
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +100079/*
80 * VFIO IOMMU fd for SPAPR_TCE IOMMU implementation
81 *
82 * This code handles mapping and unmapping of user data buffers
83 * into DMA'ble space using the IOMMU
84 */
85
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +100086struct tce_iommu_group {
87 struct list_head next;
88 struct iommu_group *grp;
89};
90
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +100091/*
92 * The container descriptor supports only a single group per container.
93 * Required by the API as the container is not supplied with the IOMMU group
94 * at the moment of initialization.
95 */
96struct tce_container {
97 struct mutex lock;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +100098 bool enabled;
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +100099 bool v2;
Alexey Kardashevskiyd9c72892016-11-30 17:52:03 +1100100 bool def_window_pending;
Alexey Kardashevskiy2d270df2015-06-05 16:35:01 +1000101 unsigned long locked_pages;
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000102 struct iommu_table *tables[IOMMU_TABLE_GROUP_MAX_TABLES];
103 struct list_head group_list;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000104};
105
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000106static long tce_iommu_unregister_pages(struct tce_container *container,
107 __u64 vaddr, __u64 size)
108{
109 struct mm_iommu_table_group_mem_t *mem;
110
Alexey Kardashevskiyd7baee62016-11-30 17:52:00 +1100111 if (!current || !current->mm)
112 return -ESRCH; /* process exited */
113
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000114 if ((vaddr & ~PAGE_MASK) || (size & ~PAGE_MASK))
115 return -EINVAL;
116
Alexey Kardashevskiyd7baee62016-11-30 17:52:00 +1100117 mem = mm_iommu_find(current->mm, vaddr, size >> PAGE_SHIFT);
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000118 if (!mem)
119 return -ENOENT;
120
Alexey Kardashevskiyd7baee62016-11-30 17:52:00 +1100121 return mm_iommu_put(current->mm, mem);
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000122}
123
124static long tce_iommu_register_pages(struct tce_container *container,
125 __u64 vaddr, __u64 size)
126{
127 long ret = 0;
128 struct mm_iommu_table_group_mem_t *mem = NULL;
129 unsigned long entries = size >> PAGE_SHIFT;
130
Alexey Kardashevskiyd7baee62016-11-30 17:52:00 +1100131 if (!current || !current->mm)
132 return -ESRCH; /* process exited */
133
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000134 if ((vaddr & ~PAGE_MASK) || (size & ~PAGE_MASK) ||
135 ((vaddr + size) < vaddr))
136 return -EINVAL;
137
Alexey Kardashevskiyd7baee62016-11-30 17:52:00 +1100138 ret = mm_iommu_get(current->mm, vaddr, entries, &mem);
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000139 if (ret)
140 return ret;
141
142 container->enabled = true;
143
144 return 0;
145}
146
147static long tce_iommu_userspace_view_alloc(struct iommu_table *tbl)
148{
149 unsigned long cb = _ALIGN_UP(sizeof(tbl->it_userspace[0]) *
150 tbl->it_size, PAGE_SIZE);
151 unsigned long *uas;
152 long ret;
153
154 BUG_ON(tbl->it_userspace);
155
156 ret = try_increment_locked_vm(cb >> PAGE_SHIFT);
157 if (ret)
158 return ret;
159
160 uas = vzalloc(cb);
161 if (!uas) {
162 decrement_locked_vm(cb >> PAGE_SHIFT);
163 return -ENOMEM;
164 }
165 tbl->it_userspace = uas;
166
167 return 0;
168}
169
170static void tce_iommu_userspace_view_free(struct iommu_table *tbl)
171{
172 unsigned long cb = _ALIGN_UP(sizeof(tbl->it_userspace[0]) *
173 tbl->it_size, PAGE_SIZE);
174
175 if (!tbl->it_userspace)
176 return;
177
178 vfree(tbl->it_userspace);
179 tbl->it_userspace = NULL;
180 decrement_locked_vm(cb >> PAGE_SHIFT);
181}
182
Alexey Kardashevskiye432bc72015-06-05 16:34:59 +1000183static bool tce_page_is_contained(struct page *page, unsigned page_shift)
184{
185 /*
186 * Check that the TCE table granularity is not bigger than the size of
187 * a page we just found. Otherwise the hardware can get access to
188 * a bigger memory chunk that it should.
189 */
190 return (PAGE_SHIFT + compound_order(compound_head(page))) >= page_shift;
191}
192
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000193static inline bool tce_groups_attached(struct tce_container *container)
194{
195 return !list_empty(&container->group_list);
196}
197
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +1000198static long tce_iommu_find_table(struct tce_container *container,
199 phys_addr_t ioba, struct iommu_table **ptbl)
200{
201 long i;
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +1000202
203 for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000204 struct iommu_table *tbl = container->tables[i];
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +1000205
206 if (tbl) {
207 unsigned long entry = ioba >> tbl->it_page_shift;
208 unsigned long start = tbl->it_offset;
209 unsigned long end = start + tbl->it_size;
210
211 if ((start <= entry) && (entry < end)) {
212 *ptbl = tbl;
213 return i;
214 }
215 }
216 }
217
218 return -1;
219}
220
Alexey Kardashevskiye633bc82015-06-05 16:35:26 +1000221static int tce_iommu_find_free_table(struct tce_container *container)
222{
223 int i;
224
225 for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
226 if (!container->tables[i])
227 return i;
228 }
229
230 return -ENOSPC;
231}
232
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000233static int tce_iommu_enable(struct tce_container *container)
234{
235 int ret = 0;
Alexey Kardashevskiy2d270df2015-06-05 16:35:01 +1000236 unsigned long locked;
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +1000237 struct iommu_table_group *table_group;
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000238 struct tce_iommu_group *tcegrp;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000239
240 if (!current->mm)
241 return -ESRCH; /* process exited */
242
243 if (container->enabled)
244 return -EBUSY;
245
246 /*
247 * When userspace pages are mapped into the IOMMU, they are effectively
248 * locked memory, so, theoretically, we need to update the accounting
249 * of locked pages on each map and unmap. For powerpc, the map unmap
250 * paths can be very hot, though, and the accounting would kill
251 * performance, especially since it would be difficult to impossible
252 * to handle the accounting in real mode only.
253 *
254 * To address that, rather than precisely accounting every page, we
255 * instead account for a worst case on locked memory when the iommu is
256 * enabled and disabled. The worst case upper bound on locked memory
257 * is the size of the whole iommu window, which is usually relatively
258 * small (compared to total memory sizes) on POWER hardware.
259 *
260 * Also we don't have a nice way to fail on H_PUT_TCE due to ulimits,
261 * that would effectively kill the guest at random points, much better
262 * enforcing the limit based on the max that the guest can map.
Alexey Kardashevskiy2d270df2015-06-05 16:35:01 +1000263 *
264 * Unfortunately at the moment it counts whole tables, no matter how
265 * much memory the guest has. I.e. for 4GB guest and 4 IOMMU groups
266 * each with 2GB DMA window, 8GB will be counted here. The reason for
267 * this is that we cannot tell here the amount of RAM used by the guest
268 * as this information is only available from KVM and VFIO is
269 * KVM agnostic.
Alexey Kardashevskiy4793d652015-06-05 16:35:20 +1000270 *
271 * So we do not allow enabling a container without a group attached
272 * as there is no way to know how much we should increment
273 * the locked_vm counter.
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000274 */
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000275 if (!tce_groups_attached(container))
276 return -ENODEV;
277
278 tcegrp = list_first_entry(&container->group_list,
279 struct tce_iommu_group, next);
280 table_group = iommu_group_get_iommudata(tcegrp->grp);
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +1000281 if (!table_group)
282 return -ENODEV;
283
Alexey Kardashevskiy4793d652015-06-05 16:35:20 +1000284 if (!table_group->tce32_size)
285 return -EPERM;
286
287 locked = table_group->tce32_size >> PAGE_SHIFT;
Alexey Kardashevskiy2d270df2015-06-05 16:35:01 +1000288 ret = try_increment_locked_vm(locked);
289 if (ret)
290 return ret;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000291
Alexey Kardashevskiy2d270df2015-06-05 16:35:01 +1000292 container->locked_pages = locked;
293
294 container->enabled = true;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000295
296 return ret;
297}
298
299static void tce_iommu_disable(struct tce_container *container)
300{
301 if (!container->enabled)
302 return;
303
304 container->enabled = false;
305
Alexey Kardashevskiy2d270df2015-06-05 16:35:01 +1000306 if (!current->mm)
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000307 return;
308
Alexey Kardashevskiy2d270df2015-06-05 16:35:01 +1000309 decrement_locked_vm(container->locked_pages);
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000310}
311
312static void *tce_iommu_open(unsigned long arg)
313{
314 struct tce_container *container;
315
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000316 if ((arg != VFIO_SPAPR_TCE_IOMMU) && (arg != VFIO_SPAPR_TCE_v2_IOMMU)) {
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000317 pr_err("tce_vfio: Wrong IOMMU type\n");
318 return ERR_PTR(-EINVAL);
319 }
320
321 container = kzalloc(sizeof(*container), GFP_KERNEL);
322 if (!container)
323 return ERR_PTR(-ENOMEM);
324
325 mutex_init(&container->lock);
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000326 INIT_LIST_HEAD_RCU(&container->group_list);
327
328 container->v2 = arg == VFIO_SPAPR_TCE_v2_IOMMU;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000329
330 return container;
331}
332
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000333static int tce_iommu_clear(struct tce_container *container,
334 struct iommu_table *tbl,
335 unsigned long entry, unsigned long pages);
336static void tce_iommu_free_table(struct iommu_table *tbl);
337
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000338static void tce_iommu_release(void *iommu_data)
339{
340 struct tce_container *container = iommu_data;
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000341 struct tce_iommu_group *tcegrp;
342 long i;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000343
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000344 while (tce_groups_attached(container)) {
345 tcegrp = list_first_entry(&container->group_list,
346 struct tce_iommu_group, next);
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000347 tce_iommu_detach_group(iommu_data, tcegrp->grp);
348 }
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000349
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000350 /*
351 * If VFIO created a table, it was not disposed
352 * by tce_iommu_detach_group() so do it now.
353 */
354 for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
355 struct iommu_table *tbl = container->tables[i];
356
357 if (!tbl)
358 continue;
359
360 tce_iommu_clear(container, tbl, tbl->it_offset, tbl->it_size);
361 tce_iommu_free_table(tbl);
362 }
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000363
Alexey Kardashevskiy649354b2015-06-05 16:35:03 +1000364 tce_iommu_disable(container);
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000365 mutex_destroy(&container->lock);
366
367 kfree(container);
368}
369
Alexey Kardashevskiy649354b2015-06-05 16:35:03 +1000370static void tce_iommu_unuse_page(struct tce_container *container,
Alexey Kardashevskiy05c6cfb2015-06-05 16:35:15 +1000371 unsigned long hpa)
Alexey Kardashevskiy649354b2015-06-05 16:35:03 +1000372{
373 struct page *page;
374
Alexey Kardashevskiy05c6cfb2015-06-05 16:35:15 +1000375 page = pfn_to_page(hpa >> PAGE_SHIFT);
Alexey Kardashevskiy649354b2015-06-05 16:35:03 +1000376 put_page(page);
377}
378
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000379static int tce_iommu_prereg_ua_to_hpa(unsigned long tce, unsigned long size,
380 unsigned long *phpa, struct mm_iommu_table_group_mem_t **pmem)
381{
382 long ret = 0;
383 struct mm_iommu_table_group_mem_t *mem;
384
Alexey Kardashevskiyd7baee62016-11-30 17:52:00 +1100385 mem = mm_iommu_lookup(current->mm, tce, size);
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000386 if (!mem)
387 return -EINVAL;
388
389 ret = mm_iommu_ua_to_hpa(mem, tce, phpa);
390 if (ret)
391 return -EINVAL;
392
393 *pmem = mem;
394
395 return 0;
396}
397
398static void tce_iommu_unuse_page_v2(struct iommu_table *tbl,
399 unsigned long entry)
400{
401 struct mm_iommu_table_group_mem_t *mem = NULL;
402 int ret;
403 unsigned long hpa = 0;
404 unsigned long *pua = IOMMU_TABLE_USERSPACE_ENTRY(tbl, entry);
405
406 if (!pua || !current || !current->mm)
407 return;
408
409 ret = tce_iommu_prereg_ua_to_hpa(*pua, IOMMU_PAGE_SIZE(tbl),
410 &hpa, &mem);
411 if (ret)
412 pr_debug("%s: tce %lx at #%lx was not cached, ret=%d\n",
413 __func__, *pua, entry, ret);
414 if (mem)
415 mm_iommu_mapped_dec(mem);
416
417 *pua = 0;
418}
419
Alexey Kardashevskiy9b14a1f2015-06-05 16:34:58 +1000420static int tce_iommu_clear(struct tce_container *container,
421 struct iommu_table *tbl,
422 unsigned long entry, unsigned long pages)
423{
Alexey Kardashevskiy05c6cfb2015-06-05 16:35:15 +1000424 unsigned long oldhpa;
425 long ret;
426 enum dma_data_direction direction;
Alexey Kardashevskiy9b14a1f2015-06-05 16:34:58 +1000427
428 for ( ; pages; --pages, ++entry) {
Alexey Kardashevskiy05c6cfb2015-06-05 16:35:15 +1000429 direction = DMA_NONE;
430 oldhpa = 0;
431 ret = iommu_tce_xchg(tbl, entry, &oldhpa, &direction);
432 if (ret)
Alexey Kardashevskiy9b14a1f2015-06-05 16:34:58 +1000433 continue;
434
Alexey Kardashevskiy05c6cfb2015-06-05 16:35:15 +1000435 if (direction == DMA_NONE)
436 continue;
437
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000438 if (container->v2) {
439 tce_iommu_unuse_page_v2(tbl, entry);
440 continue;
441 }
442
Alexey Kardashevskiy05c6cfb2015-06-05 16:35:15 +1000443 tce_iommu_unuse_page(container, oldhpa);
Alexey Kardashevskiy9b14a1f2015-06-05 16:34:58 +1000444 }
445
446 return 0;
447}
448
Alexey Kardashevskiy649354b2015-06-05 16:35:03 +1000449static int tce_iommu_use_page(unsigned long tce, unsigned long *hpa)
450{
451 struct page *page = NULL;
452 enum dma_data_direction direction = iommu_tce_direction(tce);
453
454 if (get_user_pages_fast(tce & PAGE_MASK, 1,
455 direction != DMA_TO_DEVICE, &page) != 1)
456 return -EFAULT;
457
458 *hpa = __pa((unsigned long) page_address(page));
459
460 return 0;
461}
462
Alexey Kardashevskiy9b14a1f2015-06-05 16:34:58 +1000463static long tce_iommu_build(struct tce_container *container,
464 struct iommu_table *tbl,
Alexey Kardashevskiy05c6cfb2015-06-05 16:35:15 +1000465 unsigned long entry, unsigned long tce, unsigned long pages,
466 enum dma_data_direction direction)
Alexey Kardashevskiy9b14a1f2015-06-05 16:34:58 +1000467{
468 long i, ret = 0;
Alexey Kardashevskiy649354b2015-06-05 16:35:03 +1000469 struct page *page;
470 unsigned long hpa;
Alexey Kardashevskiy05c6cfb2015-06-05 16:35:15 +1000471 enum dma_data_direction dirtmp;
Alexey Kardashevskiy9b14a1f2015-06-05 16:34:58 +1000472
473 for (i = 0; i < pages; ++i) {
474 unsigned long offset = tce & IOMMU_PAGE_MASK(tbl) & ~PAGE_MASK;
475
Alexey Kardashevskiy649354b2015-06-05 16:35:03 +1000476 ret = tce_iommu_use_page(tce, &hpa);
477 if (ret)
Alexey Kardashevskiy9b14a1f2015-06-05 16:34:58 +1000478 break;
Alexey Kardashevskiye432bc72015-06-05 16:34:59 +1000479
Alexey Kardashevskiy649354b2015-06-05 16:35:03 +1000480 page = pfn_to_page(hpa >> PAGE_SHIFT);
Alexey Kardashevskiye432bc72015-06-05 16:34:59 +1000481 if (!tce_page_is_contained(page, tbl->it_page_shift)) {
482 ret = -EPERM;
483 break;
484 }
485
Alexey Kardashevskiy649354b2015-06-05 16:35:03 +1000486 hpa |= offset;
Alexey Kardashevskiy05c6cfb2015-06-05 16:35:15 +1000487 dirtmp = direction;
488 ret = iommu_tce_xchg(tbl, entry + i, &hpa, &dirtmp);
Alexey Kardashevskiy9b14a1f2015-06-05 16:34:58 +1000489 if (ret) {
Alexey Kardashevskiy649354b2015-06-05 16:35:03 +1000490 tce_iommu_unuse_page(container, hpa);
Alexey Kardashevskiy9b14a1f2015-06-05 16:34:58 +1000491 pr_err("iommu_tce: %s failed ioba=%lx, tce=%lx, ret=%ld\n",
492 __func__, entry << tbl->it_page_shift,
493 tce, ret);
494 break;
495 }
Alexey Kardashevskiy05c6cfb2015-06-05 16:35:15 +1000496
497 if (dirtmp != DMA_NONE)
498 tce_iommu_unuse_page(container, hpa);
499
Alexey Kardashevskiy00663d42015-06-05 16:35:00 +1000500 tce += IOMMU_PAGE_SIZE(tbl);
Alexey Kardashevskiy9b14a1f2015-06-05 16:34:58 +1000501 }
502
503 if (ret)
504 tce_iommu_clear(container, tbl, entry, i);
505
506 return ret;
507}
508
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000509static long tce_iommu_build_v2(struct tce_container *container,
510 struct iommu_table *tbl,
511 unsigned long entry, unsigned long tce, unsigned long pages,
512 enum dma_data_direction direction)
513{
514 long i, ret = 0;
515 struct page *page;
516 unsigned long hpa;
517 enum dma_data_direction dirtmp;
518
Alexey Kardashevskiy39701e52016-11-30 17:52:01 +1100519 if (!tbl->it_userspace) {
520 ret = tce_iommu_userspace_view_alloc(tbl);
521 if (ret)
522 return ret;
523 }
524
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000525 for (i = 0; i < pages; ++i) {
526 struct mm_iommu_table_group_mem_t *mem = NULL;
527 unsigned long *pua = IOMMU_TABLE_USERSPACE_ENTRY(tbl,
528 entry + i);
529
530 ret = tce_iommu_prereg_ua_to_hpa(tce, IOMMU_PAGE_SIZE(tbl),
531 &hpa, &mem);
532 if (ret)
533 break;
534
535 page = pfn_to_page(hpa >> PAGE_SHIFT);
536 if (!tce_page_is_contained(page, tbl->it_page_shift)) {
537 ret = -EPERM;
538 break;
539 }
540
541 /* Preserve offset within IOMMU page */
542 hpa |= tce & IOMMU_PAGE_MASK(tbl) & ~PAGE_MASK;
543 dirtmp = direction;
544
545 /* The registered region is being unregistered */
546 if (mm_iommu_mapped_inc(mem))
547 break;
548
549 ret = iommu_tce_xchg(tbl, entry + i, &hpa, &dirtmp);
550 if (ret) {
551 /* dirtmp cannot be DMA_NONE here */
552 tce_iommu_unuse_page_v2(tbl, entry + i);
553 pr_err("iommu_tce: %s failed ioba=%lx, tce=%lx, ret=%ld\n",
554 __func__, entry << tbl->it_page_shift,
555 tce, ret);
556 break;
557 }
558
559 if (dirtmp != DMA_NONE)
560 tce_iommu_unuse_page_v2(tbl, entry + i);
561
562 *pua = tce;
563
564 tce += IOMMU_PAGE_SIZE(tbl);
565 }
566
567 if (ret)
568 tce_iommu_clear(container, tbl, entry, i);
569
570 return ret;
571}
572
Alexey Kardashevskiy46d3e1e2015-06-05 16:35:23 +1000573static long tce_iommu_create_table(struct tce_container *container,
574 struct iommu_table_group *table_group,
575 int num,
576 __u32 page_shift,
577 __u64 window_size,
578 __u32 levels,
579 struct iommu_table **ptbl)
580{
581 long ret, table_size;
582
583 table_size = table_group->ops->get_table_size(page_shift, window_size,
584 levels);
585 if (!table_size)
586 return -EINVAL;
587
588 ret = try_increment_locked_vm(table_size >> PAGE_SHIFT);
589 if (ret)
590 return ret;
591
592 ret = table_group->ops->create_table(table_group, num,
593 page_shift, window_size, levels, ptbl);
594
595 WARN_ON(!ret && !(*ptbl)->it_ops->free);
596 WARN_ON(!ret && ((*ptbl)->it_allocated_size != table_size));
597
Alexey Kardashevskiy46d3e1e2015-06-05 16:35:23 +1000598 return ret;
599}
600
601static void tce_iommu_free_table(struct iommu_table *tbl)
602{
603 unsigned long pages = tbl->it_allocated_size >> PAGE_SHIFT;
604
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000605 tce_iommu_userspace_view_free(tbl);
Alexey Kardashevskiy46d3e1e2015-06-05 16:35:23 +1000606 tbl->it_ops->free(tbl);
607 decrement_locked_vm(pages);
608}
609
Alexey Kardashevskiye633bc82015-06-05 16:35:26 +1000610static long tce_iommu_create_window(struct tce_container *container,
611 __u32 page_shift, __u64 window_size, __u32 levels,
612 __u64 *start_addr)
613{
614 struct tce_iommu_group *tcegrp;
615 struct iommu_table_group *table_group;
616 struct iommu_table *tbl = NULL;
617 long ret, num;
618
619 num = tce_iommu_find_free_table(container);
620 if (num < 0)
621 return num;
622
623 /* Get the first group for ops::create_table */
624 tcegrp = list_first_entry(&container->group_list,
625 struct tce_iommu_group, next);
626 table_group = iommu_group_get_iommudata(tcegrp->grp);
627 if (!table_group)
628 return -EFAULT;
629
630 if (!(table_group->pgsizes & (1ULL << page_shift)))
631 return -EINVAL;
632
633 if (!table_group->ops->set_window || !table_group->ops->unset_window ||
634 !table_group->ops->get_table_size ||
635 !table_group->ops->create_table)
636 return -EPERM;
637
638 /* Create TCE table */
639 ret = tce_iommu_create_table(container, table_group, num,
640 page_shift, window_size, levels, &tbl);
641 if (ret)
642 return ret;
643
644 BUG_ON(!tbl->it_ops->free);
645
646 /*
647 * Program the table to every group.
648 * Groups have been tested for compatibility at the attach time.
649 */
650 list_for_each_entry(tcegrp, &container->group_list, next) {
651 table_group = iommu_group_get_iommudata(tcegrp->grp);
652
653 ret = table_group->ops->set_window(table_group, num, tbl);
654 if (ret)
655 goto unset_exit;
656 }
657
658 container->tables[num] = tbl;
659
660 /* Return start address assigned by platform in create_table() */
661 *start_addr = tbl->it_offset << tbl->it_page_shift;
662
663 return 0;
664
665unset_exit:
666 list_for_each_entry(tcegrp, &container->group_list, next) {
667 table_group = iommu_group_get_iommudata(tcegrp->grp);
668 table_group->ops->unset_window(table_group, num);
669 }
670 tce_iommu_free_table(tbl);
671
672 return ret;
673}
674
675static long tce_iommu_remove_window(struct tce_container *container,
676 __u64 start_addr)
677{
678 struct iommu_table_group *table_group = NULL;
679 struct iommu_table *tbl;
680 struct tce_iommu_group *tcegrp;
681 int num;
682
683 num = tce_iommu_find_table(container, start_addr, &tbl);
684 if (num < 0)
685 return -EINVAL;
686
687 BUG_ON(!tbl->it_size);
688
689 /* Detach groups from IOMMUs */
690 list_for_each_entry(tcegrp, &container->group_list, next) {
691 table_group = iommu_group_get_iommudata(tcegrp->grp);
692
693 /*
694 * SPAPR TCE IOMMU exposes the default DMA window to
695 * the guest via dma32_window_start/size of
696 * VFIO_IOMMU_SPAPR_TCE_GET_INFO. Some platforms allow
697 * the userspace to remove this window, some do not so
698 * here we check for the platform capability.
699 */
700 if (!table_group->ops || !table_group->ops->unset_window)
701 return -EPERM;
702
703 table_group->ops->unset_window(table_group, num);
704 }
705
706 /* Free table */
707 tce_iommu_clear(container, tbl, tbl->it_offset, tbl->it_size);
708 tce_iommu_free_table(tbl);
709 container->tables[num] = NULL;
710
711 return 0;
712}
713
Alexey Kardashevskiy6f01cc62016-11-30 17:52:02 +1100714static long tce_iommu_create_default_window(struct tce_container *container)
715{
716 long ret;
717 __u64 start_addr = 0;
718 struct tce_iommu_group *tcegrp;
719 struct iommu_table_group *table_group;
720
Alexey Kardashevskiyd9c72892016-11-30 17:52:03 +1100721 if (!container->def_window_pending)
722 return 0;
723
Alexey Kardashevskiy6f01cc62016-11-30 17:52:02 +1100724 if (!tce_groups_attached(container))
725 return -ENODEV;
726
727 tcegrp = list_first_entry(&container->group_list,
728 struct tce_iommu_group, next);
729 table_group = iommu_group_get_iommudata(tcegrp->grp);
730 if (!table_group)
731 return -ENODEV;
732
733 ret = tce_iommu_create_window(container, IOMMU_PAGE_SHIFT_4K,
734 table_group->tce32_size, 1, &start_addr);
735 WARN_ON_ONCE(!ret && start_addr);
736
Alexey Kardashevskiyd9c72892016-11-30 17:52:03 +1100737 if (!ret)
738 container->def_window_pending = false;
739
Alexey Kardashevskiy6f01cc62016-11-30 17:52:02 +1100740 return ret;
741}
742
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000743static long tce_iommu_ioctl(void *iommu_data,
744 unsigned int cmd, unsigned long arg)
745{
746 struct tce_container *container = iommu_data;
Alexey Kardashevskiye633bc82015-06-05 16:35:26 +1000747 unsigned long minsz, ddwsz;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000748 long ret;
749
750 switch (cmd) {
751 case VFIO_CHECK_EXTENSION:
Gavin Shan1b69be52014-06-10 11:41:57 +1000752 switch (arg) {
753 case VFIO_SPAPR_TCE_IOMMU:
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000754 case VFIO_SPAPR_TCE_v2_IOMMU:
Gavin Shan1b69be52014-06-10 11:41:57 +1000755 ret = 1;
756 break;
757 default:
758 ret = vfio_spapr_iommu_eeh_ioctl(NULL, cmd, arg);
759 break;
760 }
761
762 return (ret < 0) ? 0 : ret;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000763
764 case VFIO_IOMMU_SPAPR_TCE_GET_INFO: {
765 struct vfio_iommu_spapr_tce_info info;
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000766 struct tce_iommu_group *tcegrp;
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +1000767 struct iommu_table_group *table_group;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000768
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000769 if (!tce_groups_attached(container))
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +1000770 return -ENXIO;
771
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000772 tcegrp = list_first_entry(&container->group_list,
773 struct tce_iommu_group, next);
774 table_group = iommu_group_get_iommudata(tcegrp->grp);
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +1000775
Alexey Kardashevskiy4793d652015-06-05 16:35:20 +1000776 if (!table_group)
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000777 return -ENXIO;
778
779 minsz = offsetofend(struct vfio_iommu_spapr_tce_info,
780 dma32_window_size);
781
782 if (copy_from_user(&info, (void __user *)arg, minsz))
783 return -EFAULT;
784
785 if (info.argsz < minsz)
786 return -EINVAL;
787
Alexey Kardashevskiy4793d652015-06-05 16:35:20 +1000788 info.dma32_window_start = table_group->tce32_start;
789 info.dma32_window_size = table_group->tce32_size;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000790 info.flags = 0;
Alexey Kardashevskiye633bc82015-06-05 16:35:26 +1000791 memset(&info.ddw, 0, sizeof(info.ddw));
792
793 if (table_group->max_dynamic_windows_supported &&
794 container->v2) {
795 info.flags |= VFIO_IOMMU_SPAPR_INFO_DDW;
796 info.ddw.pgsizes = table_group->pgsizes;
797 info.ddw.max_dynamic_windows_supported =
798 table_group->max_dynamic_windows_supported;
799 info.ddw.levels = table_group->max_levels;
800 }
801
802 ddwsz = offsetofend(struct vfio_iommu_spapr_tce_info, ddw);
803
804 if (info.argsz >= ddwsz)
805 minsz = ddwsz;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000806
807 if (copy_to_user((void __user *)arg, &info, minsz))
808 return -EFAULT;
809
810 return 0;
811 }
812 case VFIO_IOMMU_MAP_DMA: {
813 struct vfio_iommu_type1_dma_map param;
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +1000814 struct iommu_table *tbl = NULL;
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +1000815 long num;
Alexey Kardashevskiy05c6cfb2015-06-05 16:35:15 +1000816 enum dma_data_direction direction;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000817
Alexey Kardashevskiy3c56e822015-06-05 16:35:02 +1000818 if (!container->enabled)
819 return -EPERM;
820
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000821 minsz = offsetofend(struct vfio_iommu_type1_dma_map, size);
822
823 if (copy_from_user(&param, (void __user *)arg, minsz))
824 return -EFAULT;
825
826 if (param.argsz < minsz)
827 return -EINVAL;
828
829 if (param.flags & ~(VFIO_DMA_MAP_FLAG_READ |
830 VFIO_DMA_MAP_FLAG_WRITE))
831 return -EINVAL;
832
Alexey Kardashevskiyd9c72892016-11-30 17:52:03 +1100833 ret = tce_iommu_create_default_window(container);
834 if (ret)
835 return ret;
836
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +1000837 num = tce_iommu_find_table(container, param.iova, &tbl);
838 if (num < 0)
839 return -ENXIO;
840
Alexey Kardashevskiy00663d42015-06-05 16:35:00 +1000841 if ((param.size & ~IOMMU_PAGE_MASK(tbl)) ||
842 (param.vaddr & ~IOMMU_PAGE_MASK(tbl)))
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000843 return -EINVAL;
844
845 /* iova is checked by the IOMMU API */
Alexey Kardashevskiy05c6cfb2015-06-05 16:35:15 +1000846 if (param.flags & VFIO_DMA_MAP_FLAG_READ) {
847 if (param.flags & VFIO_DMA_MAP_FLAG_WRITE)
848 direction = DMA_BIDIRECTIONAL;
849 else
850 direction = DMA_TO_DEVICE;
851 } else {
852 if (param.flags & VFIO_DMA_MAP_FLAG_WRITE)
853 direction = DMA_FROM_DEVICE;
854 else
855 return -EINVAL;
856 }
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000857
Alexey Kardashevskiy05c6cfb2015-06-05 16:35:15 +1000858 ret = iommu_tce_put_param_check(tbl, param.iova, param.vaddr);
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000859 if (ret)
860 return ret;
861
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000862 if (container->v2)
863 ret = tce_iommu_build_v2(container, tbl,
864 param.iova >> tbl->it_page_shift,
865 param.vaddr,
866 param.size >> tbl->it_page_shift,
867 direction);
868 else
869 ret = tce_iommu_build(container, tbl,
870 param.iova >> tbl->it_page_shift,
871 param.vaddr,
872 param.size >> tbl->it_page_shift,
873 direction);
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000874
875 iommu_flush_tce(tbl);
876
877 return ret;
878 }
879 case VFIO_IOMMU_UNMAP_DMA: {
880 struct vfio_iommu_type1_dma_unmap param;
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +1000881 struct iommu_table *tbl = NULL;
882 long num;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000883
Alexey Kardashevskiy3c56e822015-06-05 16:35:02 +1000884 if (!container->enabled)
885 return -EPERM;
886
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000887 minsz = offsetofend(struct vfio_iommu_type1_dma_unmap,
888 size);
889
890 if (copy_from_user(&param, (void __user *)arg, minsz))
891 return -EFAULT;
892
893 if (param.argsz < minsz)
894 return -EINVAL;
895
896 /* No flag is supported now */
897 if (param.flags)
898 return -EINVAL;
899
Alexey Kardashevskiyd9c72892016-11-30 17:52:03 +1100900 ret = tce_iommu_create_default_window(container);
901 if (ret)
902 return ret;
903
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +1000904 num = tce_iommu_find_table(container, param.iova, &tbl);
905 if (num < 0)
906 return -ENXIO;
907
Alexey Kardashevskiy00663d42015-06-05 16:35:00 +1000908 if (param.size & ~IOMMU_PAGE_MASK(tbl))
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000909 return -EINVAL;
910
911 ret = iommu_tce_clear_param_check(tbl, param.iova, 0,
Alexey Kardashevskiy00663d42015-06-05 16:35:00 +1000912 param.size >> tbl->it_page_shift);
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000913 if (ret)
914 return ret;
915
Alexey Kardashevskiy9b14a1f2015-06-05 16:34:58 +1000916 ret = tce_iommu_clear(container, tbl,
Alexey Kardashevskiy00663d42015-06-05 16:35:00 +1000917 param.iova >> tbl->it_page_shift,
918 param.size >> tbl->it_page_shift);
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000919 iommu_flush_tce(tbl);
920
921 return ret;
922 }
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000923 case VFIO_IOMMU_SPAPR_REGISTER_MEMORY: {
924 struct vfio_iommu_spapr_register_memory param;
925
926 if (!container->v2)
927 break;
928
929 minsz = offsetofend(struct vfio_iommu_spapr_register_memory,
930 size);
931
932 if (copy_from_user(&param, (void __user *)arg, minsz))
933 return -EFAULT;
934
935 if (param.argsz < minsz)
936 return -EINVAL;
937
938 /* No flag is supported now */
939 if (param.flags)
940 return -EINVAL;
941
942 mutex_lock(&container->lock);
943 ret = tce_iommu_register_pages(container, param.vaddr,
944 param.size);
945 mutex_unlock(&container->lock);
946
947 return ret;
948 }
949 case VFIO_IOMMU_SPAPR_UNREGISTER_MEMORY: {
950 struct vfio_iommu_spapr_register_memory param;
951
952 if (!container->v2)
953 break;
954
955 minsz = offsetofend(struct vfio_iommu_spapr_register_memory,
956 size);
957
958 if (copy_from_user(&param, (void __user *)arg, minsz))
959 return -EFAULT;
960
961 if (param.argsz < minsz)
962 return -EINVAL;
963
964 /* No flag is supported now */
965 if (param.flags)
966 return -EINVAL;
967
968 mutex_lock(&container->lock);
969 ret = tce_iommu_unregister_pages(container, param.vaddr,
970 param.size);
971 mutex_unlock(&container->lock);
972
973 return ret;
974 }
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000975 case VFIO_IOMMU_ENABLE:
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000976 if (container->v2)
977 break;
978
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000979 mutex_lock(&container->lock);
980 ret = tce_iommu_enable(container);
981 mutex_unlock(&container->lock);
982 return ret;
983
984
985 case VFIO_IOMMU_DISABLE:
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000986 if (container->v2)
987 break;
988
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +1000989 mutex_lock(&container->lock);
990 tce_iommu_disable(container);
991 mutex_unlock(&container->lock);
992 return 0;
Gavin Shan1b69be52014-06-10 11:41:57 +1000993
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +1000994 case VFIO_EEH_PE_OP: {
995 struct tce_iommu_group *tcegrp;
996
997 ret = 0;
998 list_for_each_entry(tcegrp, &container->group_list, next) {
999 ret = vfio_spapr_iommu_eeh_ioctl(tcegrp->grp,
1000 cmd, arg);
1001 if (ret)
1002 return ret;
1003 }
1004 return ret;
1005 }
1006
Alexey Kardashevskiye633bc82015-06-05 16:35:26 +10001007 case VFIO_IOMMU_SPAPR_TCE_CREATE: {
1008 struct vfio_iommu_spapr_tce_create create;
1009
1010 if (!container->v2)
1011 break;
1012
1013 if (!tce_groups_attached(container))
1014 return -ENXIO;
1015
1016 minsz = offsetofend(struct vfio_iommu_spapr_tce_create,
1017 start_addr);
1018
1019 if (copy_from_user(&create, (void __user *)arg, minsz))
1020 return -EFAULT;
1021
1022 if (create.argsz < minsz)
1023 return -EINVAL;
1024
1025 if (create.flags)
1026 return -EINVAL;
1027
1028 mutex_lock(&container->lock);
1029
Alexey Kardashevskiyd9c72892016-11-30 17:52:03 +11001030 ret = tce_iommu_create_default_window(container);
1031 if (ret)
1032 return ret;
1033
Alexey Kardashevskiye633bc82015-06-05 16:35:26 +10001034 ret = tce_iommu_create_window(container, create.page_shift,
1035 create.window_size, create.levels,
1036 &create.start_addr);
1037
1038 mutex_unlock(&container->lock);
1039
1040 if (!ret && copy_to_user((void __user *)arg, &create, minsz))
1041 ret = -EFAULT;
1042
1043 return ret;
1044 }
1045 case VFIO_IOMMU_SPAPR_TCE_REMOVE: {
1046 struct vfio_iommu_spapr_tce_remove remove;
1047
1048 if (!container->v2)
1049 break;
1050
1051 if (!tce_groups_attached(container))
1052 return -ENXIO;
1053
1054 minsz = offsetofend(struct vfio_iommu_spapr_tce_remove,
1055 start_addr);
1056
1057 if (copy_from_user(&remove, (void __user *)arg, minsz))
1058 return -EFAULT;
1059
1060 if (remove.argsz < minsz)
1061 return -EINVAL;
1062
1063 if (remove.flags)
1064 return -EINVAL;
1065
Alexey Kardashevskiyd9c72892016-11-30 17:52:03 +11001066 if (container->def_window_pending && !remove.start_addr) {
1067 container->def_window_pending = false;
1068 return 0;
1069 }
1070
Alexey Kardashevskiye633bc82015-06-05 16:35:26 +10001071 mutex_lock(&container->lock);
1072
1073 ret = tce_iommu_remove_window(container, remove.start_addr);
1074
1075 mutex_unlock(&container->lock);
1076
1077 return ret;
1078 }
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +10001079 }
1080
1081 return -ENOTTY;
1082}
1083
Alexey Kardashevskiyf87a8862015-06-05 16:35:10 +10001084static void tce_iommu_release_ownership(struct tce_container *container,
1085 struct iommu_table_group *table_group)
1086{
1087 int i;
1088
1089 for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +10001090 struct iommu_table *tbl = container->tables[i];
Alexey Kardashevskiyf87a8862015-06-05 16:35:10 +10001091
1092 if (!tbl)
1093 continue;
1094
1095 tce_iommu_clear(container, tbl, tbl->it_offset, tbl->it_size);
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +10001096 tce_iommu_userspace_view_free(tbl);
Alexey Kardashevskiyf87a8862015-06-05 16:35:10 +10001097 if (tbl->it_map)
1098 iommu_release_ownership(tbl);
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +10001099
1100 container->tables[i] = NULL;
Alexey Kardashevskiyf87a8862015-06-05 16:35:10 +10001101 }
1102}
1103
1104static int tce_iommu_take_ownership(struct tce_container *container,
1105 struct iommu_table_group *table_group)
1106{
1107 int i, j, rc = 0;
1108
1109 for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
1110 struct iommu_table *tbl = table_group->tables[i];
1111
1112 if (!tbl || !tbl->it_map)
1113 continue;
1114
Alexey Kardashevskiy39701e52016-11-30 17:52:01 +11001115 rc = iommu_take_ownership(tbl);
Alexey Kardashevskiyf87a8862015-06-05 16:35:10 +10001116 if (rc) {
1117 for (j = 0; j < i; ++j)
1118 iommu_release_ownership(
1119 table_group->tables[j]);
1120
1121 return rc;
1122 }
1123 }
1124
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +10001125 for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i)
1126 container->tables[i] = table_group->tables[i];
1127
Alexey Kardashevskiyf87a8862015-06-05 16:35:10 +10001128 return 0;
1129}
1130
1131static void tce_iommu_release_ownership_ddw(struct tce_container *container,
1132 struct iommu_table_group *table_group)
1133{
Alexey Kardashevskiy46d3e1e2015-06-05 16:35:23 +10001134 long i;
1135
1136 if (!table_group->ops->unset_window) {
1137 WARN_ON_ONCE(1);
1138 return;
1139 }
1140
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +10001141 for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i)
Alexey Kardashevskiy46d3e1e2015-06-05 16:35:23 +10001142 table_group->ops->unset_window(table_group, i);
Alexey Kardashevskiy46d3e1e2015-06-05 16:35:23 +10001143
Alexey Kardashevskiyf87a8862015-06-05 16:35:10 +10001144 table_group->ops->release_ownership(table_group);
1145}
1146
1147static long tce_iommu_take_ownership_ddw(struct tce_container *container,
1148 struct iommu_table_group *table_group)
1149{
Alexey Kardashevskiy46d3e1e2015-06-05 16:35:23 +10001150 if (!table_group->ops->create_table || !table_group->ops->set_window ||
1151 !table_group->ops->release_ownership) {
1152 WARN_ON_ONCE(1);
1153 return -EFAULT;
1154 }
1155
Alexey Kardashevskiyf87a8862015-06-05 16:35:10 +10001156 table_group->ops->take_ownership(table_group);
1157
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +10001158 return 0;
Alexey Kardashevskiyf87a8862015-06-05 16:35:10 +10001159}
1160
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +10001161static int tce_iommu_attach_group(void *iommu_data,
1162 struct iommu_group *iommu_group)
1163{
1164 int ret;
1165 struct tce_container *container = iommu_data;
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +10001166 struct iommu_table_group *table_group;
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +10001167 struct tce_iommu_group *tcegrp = NULL;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +10001168
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +10001169 mutex_lock(&container->lock);
1170
1171 /* pr_debug("tce_vfio: Attaching group #%u to iommu %p\n",
1172 iommu_group_id(iommu_group), iommu_group); */
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +10001173 table_group = iommu_group_get_iommudata(iommu_group);
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +10001174
1175 if (tce_groups_attached(container) && (!table_group->ops ||
1176 !table_group->ops->take_ownership ||
1177 !table_group->ops->release_ownership)) {
1178 ret = -EBUSY;
1179 goto unlock_exit;
1180 }
1181
1182 /* Check if new group has the same iommu_ops (i.e. compatible) */
1183 list_for_each_entry(tcegrp, &container->group_list, next) {
1184 struct iommu_table_group *table_group_tmp;
1185
1186 if (tcegrp->grp == iommu_group) {
1187 pr_warn("tce_vfio: Group %d is already attached\n",
1188 iommu_group_id(iommu_group));
1189 ret = -EBUSY;
1190 goto unlock_exit;
1191 }
1192 table_group_tmp = iommu_group_get_iommudata(tcegrp->grp);
Alexey Kardashevskiy54de2852016-04-29 18:55:15 +10001193 if (table_group_tmp->ops->create_table !=
1194 table_group->ops->create_table) {
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +10001195 pr_warn("tce_vfio: Group %d is incompatible with group %d\n",
1196 iommu_group_id(iommu_group),
1197 iommu_group_id(tcegrp->grp));
1198 ret = -EPERM;
1199 goto unlock_exit;
1200 }
1201 }
1202
1203 tcegrp = kzalloc(sizeof(*tcegrp), GFP_KERNEL);
1204 if (!tcegrp) {
1205 ret = -ENOMEM;
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +10001206 goto unlock_exit;
1207 }
1208
Alexey Kardashevskiyf87a8862015-06-05 16:35:10 +10001209 if (!table_group->ops || !table_group->ops->take_ownership ||
Alexey Kardashevskiy6f01cc62016-11-30 17:52:02 +11001210 !table_group->ops->release_ownership) {
Alexey Kardashevskiyf87a8862015-06-05 16:35:10 +10001211 ret = tce_iommu_take_ownership(container, table_group);
Alexey Kardashevskiy6f01cc62016-11-30 17:52:02 +11001212 } else {
Alexey Kardashevskiyf87a8862015-06-05 16:35:10 +10001213 ret = tce_iommu_take_ownership_ddw(container, table_group);
Alexey Kardashevskiy6f01cc62016-11-30 17:52:02 +11001214 if (!tce_groups_attached(container) && !container->tables[0])
Alexey Kardashevskiyd9c72892016-11-30 17:52:03 +11001215 container->def_window_pending = true;
Alexey Kardashevskiy6f01cc62016-11-30 17:52:02 +11001216 }
Alexey Kardashevskiyf87a8862015-06-05 16:35:10 +10001217
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +10001218 if (!ret) {
1219 tcegrp->grp = iommu_group;
1220 list_add(&tcegrp->next, &container->group_list);
1221 }
Alexey Kardashevskiy22af4852015-06-05 16:35:04 +10001222
1223unlock_exit:
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +10001224 if (ret && tcegrp)
1225 kfree(tcegrp);
1226
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +10001227 mutex_unlock(&container->lock);
1228
1229 return ret;
1230}
1231
1232static void tce_iommu_detach_group(void *iommu_data,
1233 struct iommu_group *iommu_group)
1234{
1235 struct tce_container *container = iommu_data;
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +10001236 struct iommu_table_group *table_group;
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +10001237 bool found = false;
1238 struct tce_iommu_group *tcegrp;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +10001239
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +10001240 mutex_lock(&container->lock);
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +10001241
1242 list_for_each_entry(tcegrp, &container->group_list, next) {
1243 if (tcegrp->grp == iommu_group) {
1244 found = true;
1245 break;
1246 }
1247 }
1248
1249 if (!found) {
1250 pr_warn("tce_vfio: detaching unattached group #%u\n",
1251 iommu_group_id(iommu_group));
Alexey Kardashevskiy22af4852015-06-05 16:35:04 +10001252 goto unlock_exit;
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +10001253 }
Alexey Kardashevskiy22af4852015-06-05 16:35:04 +10001254
Alexey Kardashevskiy2157e7b2015-06-05 16:35:25 +10001255 list_del(&tcegrp->next);
1256 kfree(tcegrp);
Alexey Kardashevskiy0eaf4de2015-06-05 16:35:09 +10001257
1258 table_group = iommu_group_get_iommudata(iommu_group);
1259 BUG_ON(!table_group);
1260
Alexey Kardashevskiyf87a8862015-06-05 16:35:10 +10001261 if (!table_group->ops || !table_group->ops->release_ownership)
1262 tce_iommu_release_ownership(container, table_group);
1263 else
1264 tce_iommu_release_ownership_ddw(container, table_group);
Alexey Kardashevskiy22af4852015-06-05 16:35:04 +10001265
1266unlock_exit:
Alexey Kardashevskiy5ffd2292013-05-21 13:33:10 +10001267 mutex_unlock(&container->lock);
1268}
1269
1270const struct vfio_iommu_driver_ops tce_iommu_driver_ops = {
1271 .name = "iommu-vfio-powerpc",
1272 .owner = THIS_MODULE,
1273 .open = tce_iommu_open,
1274 .release = tce_iommu_release,
1275 .ioctl = tce_iommu_ioctl,
1276 .attach_group = tce_iommu_attach_group,
1277 .detach_group = tce_iommu_detach_group,
1278};
1279
1280static int __init tce_iommu_init(void)
1281{
1282 return vfio_register_iommu_driver(&tce_iommu_driver_ops);
1283}
1284
1285static void __exit tce_iommu_cleanup(void)
1286{
1287 vfio_unregister_iommu_driver(&tce_iommu_driver_ops);
1288}
1289
1290module_init(tce_iommu_init);
1291module_exit(tce_iommu_cleanup);
1292
1293MODULE_VERSION(DRIVER_VERSION);
1294MODULE_LICENSE("GPL v2");
1295MODULE_AUTHOR(DRIVER_AUTHOR);
1296MODULE_DESCRIPTION(DRIVER_DESC);
1297