blob: 0827a49f76f16e03adfd336c33d884e61ee475c8 [file] [log] [blame]
Mitchel Humpherys42296fb2015-06-23 16:29:16 -07001/*
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -07002 * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
Mitchel Humpherys42296fb2015-06-23 16:29:16 -07003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#define pr_fmt(fmt) "iommu-debug: %s: " fmt, __func__
16
17#include <linux/debugfs.h>
18#include <linux/device.h>
19#include <linux/iommu.h>
20#include <linux/of.h>
21#include <linux/platform_device.h>
22#include <linux/slab.h>
23#include <linux/module.h>
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -070024#include <linux/uaccess.h>
Mitchel Humpherys5e991f12015-07-30 19:25:54 -070025#include <soc/qcom/secure_buffer.h>
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070026
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070027#ifdef CONFIG_IOMMU_DEBUG_TRACKING
28
29static DEFINE_MUTEX(iommu_debug_attachments_lock);
30static LIST_HEAD(iommu_debug_attachments);
31static struct dentry *debugfs_attachments_dir;
32
33struct iommu_debug_attachment {
34 struct iommu_domain *domain;
35 struct device *dev;
36 struct dentry *dentry;
37 struct list_head list;
38};
39
Mitchel Humpherys088cc582015-07-09 15:02:03 -070040static int iommu_debug_attachment_info_show(struct seq_file *s, void *ignored)
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070041{
42 struct iommu_debug_attachment *attach = s->private;
Mitchel Humpherys5e991f12015-07-30 19:25:54 -070043 int secure_vmid;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070044
45 seq_printf(s, "Domain: 0x%p\n", attach->domain);
Mitchel Humpherys5e991f12015-07-30 19:25:54 -070046
47 seq_puts(s, "SECURE_VMID: ");
48 if (iommu_domain_get_attr(attach->domain,
49 DOMAIN_ATTR_SECURE_VMID,
50 &secure_vmid))
51 seq_puts(s, "(Unknown)\n");
52 else
53 seq_printf(s, "%s (0x%x)\n",
54 msm_secure_vmid_to_string(secure_vmid), secure_vmid);
55
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070056 return 0;
57}
58
Mitchel Humpherys088cc582015-07-09 15:02:03 -070059static int iommu_debug_attachment_info_open(struct inode *inode,
60 struct file *file)
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070061{
Mitchel Humpherys088cc582015-07-09 15:02:03 -070062 return single_open(file, iommu_debug_attachment_info_show,
63 inode->i_private);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070064}
65
Mitchel Humpherys088cc582015-07-09 15:02:03 -070066static const struct file_operations iommu_debug_attachment_info_fops = {
67 .open = iommu_debug_attachment_info_open,
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070068 .read = seq_read,
69 .llseek = seq_lseek,
70 .release = single_release,
71};
72
Mitchel Humpherys288086e2015-07-09 16:55:08 -070073static ssize_t iommu_debug_attachment_trigger_fault_write(
74 struct file *file, const char __user *ubuf, size_t count,
75 loff_t *offset)
76{
77 struct iommu_debug_attachment *attach = file->private_data;
78 unsigned long flags;
79
80 if (kstrtoul_from_user(ubuf, count, 0, &flags)) {
81 pr_err("Invalid flags format\n");
82 return -EFAULT;
83 }
84
85 iommu_trigger_fault(attach->domain, flags);
86
87 return count;
88}
89
90static const struct file_operations
91iommu_debug_attachment_trigger_fault_fops = {
92 .open = simple_open,
93 .write = iommu_debug_attachment_trigger_fault_write,
94};
95
Mitchel Humpherysa05b2522015-07-14 14:30:33 -070096/* should be called with iommu_debug_attachments_lock locked */
97static int iommu_debug_attach_add_debugfs(
98 struct iommu_debug_attachment *attach)
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070099{
Mitchel Humpherys54379212015-08-26 11:52:57 -0700100 const char *attach_name;
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700101 struct device *dev = attach->dev;
102 struct iommu_domain *domain = attach->domain;
Mitchel Humpherys54379212015-08-26 11:52:57 -0700103 int is_dynamic;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700104
Mitchel Humpherys54379212015-08-26 11:52:57 -0700105 if (iommu_domain_get_attr(domain, DOMAIN_ATTR_DYNAMIC, &is_dynamic))
106 is_dynamic = 0;
107
108 if (is_dynamic) {
109 uuid_le uuid;
110
111 uuid_le_gen(&uuid);
112 attach_name = kasprintf(GFP_KERNEL, "%s-%pUl", dev_name(dev),
113 uuid.b);
114 if (!attach_name)
115 return -ENOMEM;
116 } else {
117 attach_name = dev_name(dev);
118 }
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700119
Mitchel Humpherys088cc582015-07-09 15:02:03 -0700120 attach->dentry = debugfs_create_dir(attach_name,
121 debugfs_attachments_dir);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700122 if (!attach->dentry) {
Mitchel Humpherys088cc582015-07-09 15:02:03 -0700123 pr_err("Couldn't create iommu/attachments/%s debugfs directory for domain 0x%p\n",
Mitchel Humpherys876e2be2015-07-10 11:56:56 -0700124 attach_name, domain);
Mitchel Humpherys54379212015-08-26 11:52:57 -0700125 if (is_dynamic)
126 kfree(attach_name);
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700127 return -EIO;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700128 }
Mitchel Humpherys54379212015-08-26 11:52:57 -0700129
130 if (is_dynamic)
131 kfree(attach_name);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700132
Mitchel Humpherys088cc582015-07-09 15:02:03 -0700133 if (!debugfs_create_file(
134 "info", S_IRUSR, attach->dentry, attach,
135 &iommu_debug_attachment_info_fops)) {
136 pr_err("Couldn't create iommu/attachments/%s/info debugfs file for domain 0x%p\n",
137 dev_name(dev), domain);
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700138 goto err_rmdir;
Mitchel Humpherys088cc582015-07-09 15:02:03 -0700139 }
140
Mitchel Humpherys288086e2015-07-09 16:55:08 -0700141 if (!debugfs_create_file(
142 "trigger_fault", S_IRUSR, attach->dentry, attach,
143 &iommu_debug_attachment_trigger_fault_fops)) {
144 pr_err("Couldn't create iommu/attachments/%s/trigger_fault debugfs file for domain 0x%p\n",
145 dev_name(dev), domain);
146 goto err_rmdir;
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700147 return 0;
148
149err_rmdir:
150 debugfs_remove_recursive(attach->dentry);
151 return -EIO;
152}
153
Susheel Khianie66aa5b2015-08-25 17:25:42 +0530154void iommu_debug_domain_add(struct iommu_domain *domain)
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700155{
156 struct iommu_debug_attachment *attach;
157
158 mutex_lock(&iommu_debug_attachments_lock);
159
160 attach = kmalloc(sizeof(*attach), GFP_KERNEL);
161 if (!attach)
162 goto out_unlock;
163
164 attach->domain = domain;
Susheel Khianie66aa5b2015-08-25 17:25:42 +0530165 attach->dev = NULL;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700166 list_add(&attach->list, &iommu_debug_attachments);
Susheel Khianie66aa5b2015-08-25 17:25:42 +0530167
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700168out_unlock:
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700169 mutex_unlock(&iommu_debug_attachments_lock);
170}
171
Susheel Khianie66aa5b2015-08-25 17:25:42 +0530172void iommu_debug_domain_remove(struct iommu_domain *domain)
173{
174 struct iommu_debug_attachment *it;
175
176 mutex_lock(&iommu_debug_attachments_lock);
177 list_for_each_entry(it, &iommu_debug_attachments, list)
178 if (it->domain == domain && it->dev == NULL)
179 break;
180
181 if (&it->list == &iommu_debug_attachments) {
182 WARN(1, "Couldn't find debug attachment for domain=0x%p",
183 domain);
184 } else {
185 list_del(&it->list);
186 kfree(it);
187 }
188 mutex_unlock(&iommu_debug_attachments_lock);
189}
190
191void iommu_debug_attach_device(struct iommu_domain *domain,
192 struct device *dev)
193{
194 struct iommu_debug_attachment *attach;
195
196 mutex_lock(&iommu_debug_attachments_lock);
197
198 list_for_each_entry(attach, &iommu_debug_attachments, list)
199 if (attach->domain == domain && attach->dev == NULL)
200 break;
201
202 if (&attach->list == &iommu_debug_attachments) {
203 WARN(1, "Couldn't find debug attachment for domain=0x%p dev=%s",
204 domain, dev_name(dev));
205 } else {
206 attach->dev = dev;
207
208 /*
209 * we might not init until after other drivers start calling
210 * iommu_attach_device. Only set up the debugfs nodes if we've
211 * already init'd to avoid polluting the top-level debugfs
212 * directory (by calling debugfs_create_dir with a NULL
213 * parent). These will be flushed out later once we init.
214 */
215
216 if (debugfs_attachments_dir)
217 iommu_debug_attach_add_debugfs(attach);
218 }
219
220 mutex_unlock(&iommu_debug_attachments_lock);
221}
222
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700223void iommu_debug_detach_device(struct iommu_domain *domain,
224 struct device *dev)
225{
226 struct iommu_debug_attachment *it;
227
228 mutex_lock(&iommu_debug_attachments_lock);
229 list_for_each_entry(it, &iommu_debug_attachments, list)
230 if (it->domain == domain && it->dev == dev)
231 break;
232
233 if (&it->list == &iommu_debug_attachments) {
234 WARN(1, "Couldn't find debug attachment for domain=0x%p dev=%s",
235 domain, dev_name(dev));
236 } else {
Susheel Khianie66aa5b2015-08-25 17:25:42 +0530237 /*
238 * Just remove debugfs entry and mark dev as NULL on
239 * iommu_detach call. We would remove the actual
240 * attachment entry from the list only on domain_free call.
241 * This is to ensure we keep track of unattached domains too.
242 */
243
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700244 debugfs_remove_recursive(it->dentry);
Susheel Khianie66aa5b2015-08-25 17:25:42 +0530245 it->dev = NULL;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700246 }
247 mutex_unlock(&iommu_debug_attachments_lock);
248}
249
250static int iommu_debug_init_tracking(void)
251{
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700252 int ret = 0;
253 struct iommu_debug_attachment *attach;
254
255 mutex_lock(&iommu_debug_attachments_lock);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700256 debugfs_attachments_dir = debugfs_create_dir("attachments",
Mitchel Humpherysc75ae492015-07-15 18:27:36 -0700257 iommu_debugfs_top);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700258 if (!debugfs_attachments_dir) {
259 pr_err("Couldn't create iommu/attachments debugfs directory\n");
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700260 ret = -ENODEV;
261 goto out_unlock;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700262 }
263
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700264 /* set up debugfs entries for attachments made during early boot */
265 list_for_each_entry(attach, &iommu_debug_attachments, list)
Susheel Khianie66aa5b2015-08-25 17:25:42 +0530266 if (attach->dev)
267 iommu_debug_attach_add_debugfs(attach);
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700268
269out_unlock:
270 mutex_unlock(&iommu_debug_attachments_lock);
271 return ret;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700272}
Mitchel Humpherysc75ae492015-07-15 18:27:36 -0700273
274static void iommu_debug_destroy_tracking(void)
275{
276 debugfs_remove_recursive(debugfs_attachments_dir);
277}
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700278#else
279static inline int iommu_debug_init_tracking(void) { return 0; }
Mitchel Humpherysc75ae492015-07-15 18:27:36 -0700280static inline void iommu_debug_destroy_tracking(void) { }
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700281#endif
282
283#ifdef CONFIG_IOMMU_TESTS
284
285static LIST_HEAD(iommu_debug_devices);
286static struct dentry *debugfs_tests_dir;
287
288struct iommu_debug_device {
289 struct device *dev;
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700290 struct iommu_domain *domain;
291 u64 iova;
292 u64 phys;
293 size_t len;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700294 struct list_head list;
295};
296
297static int iommu_debug_build_phoney_sg_table(struct device *dev,
298 struct sg_table *table,
299 unsigned long total_size,
300 unsigned long chunk_size)
301{
302 unsigned long nents = total_size / chunk_size;
303 struct scatterlist *sg;
304 int i;
305 struct page *page;
306
307 if (!IS_ALIGNED(total_size, PAGE_SIZE))
308 return -EINVAL;
309 if (!IS_ALIGNED(total_size, chunk_size))
310 return -EINVAL;
311 if (sg_alloc_table(table, nents, GFP_KERNEL))
312 return -EINVAL;
313 page = alloc_pages(GFP_KERNEL, get_order(chunk_size));
314 if (!page)
315 goto free_table;
316
317 /* all the same page... why not. */
318 for_each_sg(table->sgl, sg, table->nents, i)
319 sg_set_page(sg, page, chunk_size, 0);
320
321 return 0;
322
323free_table:
324 sg_free_table(table);
325 return -ENOMEM;
326}
327
328static void iommu_debug_destroy_phoney_sg_table(struct device *dev,
329 struct sg_table *table,
330 unsigned long chunk_size)
331{
332 __free_pages(sg_page(table->sgl), get_order(chunk_size));
333 sg_free_table(table);
334}
335
336static const char * const _size_to_string(unsigned long size)
337{
338 switch (size) {
339 case SZ_4K:
340 return "4K";
341 case SZ_64K:
342 return "64K";
343 case SZ_2M:
344 return "2M";
345 case SZ_1M * 12:
346 return "12M";
347 case SZ_1M * 20:
348 return "20M";
349 }
350 return "unknown size, please add to _size_to_string";
351}
352
353static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev)
354{
355 unsigned long sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12,
356 SZ_1M * 20, 0 };
357 unsigned long *sz;
358 struct iommu_domain *domain;
359 unsigned long iova = 0x10000;
360 phys_addr_t paddr = 0xa000;
361
362 domain = iommu_domain_alloc(&platform_bus_type);
363 if (!domain) {
364 seq_puts(s, "Couldn't allocate domain\n");
365 return;
366 }
367
368 if (iommu_attach_device(domain, dev)) {
369 seq_puts(s,
370 "Couldn't attach new domain to device. Is it already attached?\n");
371 goto out_domain_free;
372 }
373
374 seq_printf(s, "%8s %15s %12s\n", "size", "iommu_map", "iommu_unmap");
375 for (sz = sizes; *sz; ++sz) {
376 unsigned long size = *sz;
377 size_t unmapped;
378 s64 map_elapsed_us, unmap_elapsed_us;
379 struct timespec tbefore, tafter, diff;
380
381 getnstimeofday(&tbefore);
382 if (iommu_map(domain, iova, paddr, size,
383 IOMMU_READ | IOMMU_WRITE)) {
384 seq_puts(s, "Failed to map\n");
385 continue;
386 }
387 getnstimeofday(&tafter);
388 diff = timespec_sub(tafter, tbefore);
389 map_elapsed_us = div_s64(timespec_to_ns(&diff), 1000);
390
391 getnstimeofday(&tbefore);
392 unmapped = iommu_unmap(domain, iova, size);
393 if (unmapped != size) {
394 seq_printf(s, "Only unmapped %zx instead of %zx\n",
395 unmapped, size);
396 continue;
397 }
398 getnstimeofday(&tafter);
399 diff = timespec_sub(tafter, tbefore);
400 unmap_elapsed_us = div_s64(timespec_to_ns(&diff), 1000);
401
402 seq_printf(s, "%8s %12lld us %9lld us\n", _size_to_string(size),
403 map_elapsed_us, unmap_elapsed_us);
404 }
405
406 seq_putc(s, '\n');
407 seq_printf(s, "%8s %15s %12s\n", "size", "iommu_map_sg", "iommu_unmap");
408 for (sz = sizes; *sz; ++sz) {
409 unsigned long size = *sz;
410 size_t unmapped;
411 s64 map_elapsed_us, unmap_elapsed_us;
412 struct timespec tbefore, tafter, diff;
413 struct sg_table table;
414 unsigned long chunk_size = SZ_4K;
415
416 if (iommu_debug_build_phoney_sg_table(dev, &table, size,
417 chunk_size)) {
418 seq_puts(s,
419 "couldn't build phoney sg table! bailing...\n");
420 goto out_detach;
421 }
422
423 getnstimeofday(&tbefore);
424 if (iommu_map_sg(domain, iova, table.sgl, table.nents,
425 IOMMU_READ | IOMMU_WRITE) != size) {
426 seq_puts(s, "Failed to map_sg\n");
427 goto next;
428 }
429 getnstimeofday(&tafter);
430 diff = timespec_sub(tafter, tbefore);
431 map_elapsed_us = div_s64(timespec_to_ns(&diff), 1000);
432
433 getnstimeofday(&tbefore);
434 unmapped = iommu_unmap(domain, iova, size);
435 if (unmapped != size) {
436 seq_printf(s, "Only unmapped %zx instead of %zx\n",
437 unmapped, size);
438 goto next;
439 }
440 getnstimeofday(&tafter);
441 diff = timespec_sub(tafter, tbefore);
442 unmap_elapsed_us = div_s64(timespec_to_ns(&diff), 1000);
443
444 seq_printf(s, "%8s %12lld us %9lld us\n", _size_to_string(size),
445 map_elapsed_us, unmap_elapsed_us);
446
447next:
448 iommu_debug_destroy_phoney_sg_table(dev, &table, chunk_size);
449 }
450
451out_detach:
452 iommu_detach_device(domain, dev);
453out_domain_free:
454 iommu_domain_free(domain);
455}
456
Mitchel Humpherys7cc56e42015-07-06 14:58:23 -0700457static int iommu_debug_profiling_show(struct seq_file *s, void *ignored)
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700458{
459 struct iommu_debug_device *ddev = s->private;
460
461 iommu_debug_device_profiling(s, ddev->dev);
462
463 return 0;
464}
465
Mitchel Humpherys7cc56e42015-07-06 14:58:23 -0700466static int iommu_debug_profiling_open(struct inode *inode, struct file *file)
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700467{
Mitchel Humpherys7cc56e42015-07-06 14:58:23 -0700468 return single_open(file, iommu_debug_profiling_show, inode->i_private);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700469}
470
Mitchel Humpherys7cc56e42015-07-06 14:58:23 -0700471static const struct file_operations iommu_debug_profiling_fops = {
472 .open = iommu_debug_profiling_open,
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700473 .read = seq_read,
474 .llseek = seq_lseek,
475 .release = single_release,
476};
477
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700478static int iommu_debug_attach_do_attach(struct iommu_debug_device *ddev)
479{
480 ddev->domain = iommu_domain_alloc(&platform_bus_type);
481 if (!ddev->domain) {
482 pr_err("Couldn't allocate domain\n");
483 return -ENOMEM;
484 }
485
486 if (iommu_attach_device(ddev->domain, ddev->dev)) {
487 pr_err("Couldn't attach new domain to device. Is it already attached?\n");
488 goto out_domain_free;
489 }
490
491 return 0;
492
493out_domain_free:
494 iommu_domain_free(ddev->domain);
495 ddev->domain = NULL;
496 return -EIO;
497}
498
499static ssize_t iommu_debug_attach_write(struct file *file,
500 const char __user *ubuf,
501 size_t count, loff_t *offset)
502{
503 struct iommu_debug_device *ddev = file->private_data;
504 ssize_t retval;
505 char val;
506
507 if (count > 2) {
508 pr_err("Invalid value. Expected 0 or 1.\n");
509 retval = -EINVAL;
510 goto out;
511 }
512
513 if (copy_from_user(&val, ubuf, 1)) {
514 pr_err("Couldn't copy from user\n");
515 retval = -EFAULT;
516 goto out;
517 }
518
519 if (val == '1') {
520 if (ddev->domain) {
521 pr_err("Already attached.\n");
522 retval = -EINVAL;
523 goto out;
524 }
525 if (WARN(ddev->dev->archdata.iommu,
526 "Attachment tracking out of sync with device\n")) {
527 retval = -EINVAL;
528 goto out;
529 }
530 if (iommu_debug_attach_do_attach(ddev)) {
531 retval = -EIO;
532 goto out;
533 }
534 pr_err("Attached\n");
535 } else if (val == '0') {
536 if (!ddev->domain) {
537 pr_err("No domain. Did you already attach?\n");
538 retval = -EINVAL;
539 goto out;
540 }
541 iommu_detach_device(ddev->domain, ddev->dev);
542 iommu_domain_free(ddev->domain);
543 ddev->domain = NULL;
544 pr_err("Detached\n");
545 } else {
546 pr_err("Invalid value. Expected 0 or 1\n");
547 retval = -EFAULT;
548 goto out;
549 }
550
551 retval = count;
552out:
553 return retval;
554}
555
556static ssize_t iommu_debug_attach_read(struct file *file, char __user *ubuf,
557 size_t count, loff_t *offset)
558{
559 struct iommu_debug_device *ddev = file->private_data;
560 char c[2];
561
562 if (*offset)
563 return 0;
564
565 c[0] = ddev->domain ? '1' : '0';
566 c[1] = '\n';
567 if (copy_to_user(ubuf, &c, 2)) {
568 pr_err("copy_to_user failed\n");
569 return -EFAULT;
570 }
571 *offset = 1; /* non-zero means we're done */
572
573 return 2;
574}
575
576static const struct file_operations iommu_debug_attach_fops = {
577 .open = simple_open,
578 .write = iommu_debug_attach_write,
579 .read = iommu_debug_attach_read,
580};
581
582static ssize_t iommu_debug_atos_write(struct file *file,
583 const char __user *ubuf,
584 size_t count, loff_t *offset)
585{
586 struct iommu_debug_device *ddev = file->private_data;
587 dma_addr_t iova;
588
589 if (kstrtoll_from_user(ubuf, count, 0, &iova)) {
590 pr_err("Invalid format for iova\n");
591 ddev->iova = 0;
592 return -EINVAL;
593 }
594
595 ddev->iova = iova;
596 pr_err("Saved iova=%pa for future ATOS commands\n", &iova);
597 return count;
598}
599
600static ssize_t iommu_debug_atos_read(struct file *file, char __user *ubuf,
601 size_t count, loff_t *offset)
602{
603 struct iommu_debug_device *ddev = file->private_data;
604 phys_addr_t phys;
605 char buf[100];
606 ssize_t retval;
607 size_t buflen;
608
609 if (!ddev->domain) {
610 pr_err("No domain. Did you already attach?\n");
611 return -EINVAL;
612 }
613
614 if (*offset)
615 return 0;
616
617 memset(buf, 0, 100);
618
619 phys = iommu_iova_to_phys_hard(ddev->domain, ddev->iova);
620 if (!phys)
621 strlcpy(buf, "FAIL\n", 100);
622 else
623 snprintf(buf, 100, "%pa\n", &phys);
624
625 buflen = strlen(buf);
626 if (copy_to_user(ubuf, buf, buflen)) {
627 pr_err("Couldn't copy_to_user\n");
628 retval = -EFAULT;
629 } else {
630 *offset = 1; /* non-zero means we're done */
631 retval = buflen;
632 }
633
634 return retval;
635}
636
637static const struct file_operations iommu_debug_atos_fops = {
638 .open = simple_open,
639 .write = iommu_debug_atos_write,
640 .read = iommu_debug_atos_read,
641};
642
643static ssize_t iommu_debug_map_write(struct file *file, const char __user *ubuf,
644 size_t count, loff_t *offset)
645{
646 ssize_t retval;
647 int ret;
648 char *comma1, *comma2, *comma3;
649 char buf[100];
650 dma_addr_t iova;
651 phys_addr_t phys;
652 size_t size;
653 int prot;
654 struct iommu_debug_device *ddev = file->private_data;
655
656 if (count >= 100) {
657 pr_err("Value too large\n");
658 return -EINVAL;
659 }
660
661 if (!ddev->domain) {
662 pr_err("No domain. Did you already attach?\n");
663 return -EINVAL;
664 }
665
666 memset(buf, 0, 100);
667
668 if (copy_from_user(buf, ubuf, count)) {
669 pr_err("Couldn't copy from user\n");
670 retval = -EFAULT;
671 }
672
673 comma1 = strnchr(buf, count, ',');
674 if (!comma1)
675 goto invalid_format;
676
677 comma2 = strnchr(comma1 + 1, count, ',');
678 if (!comma2)
679 goto invalid_format;
680
681 comma3 = strnchr(comma2 + 1, count, ',');
682 if (!comma3)
683 goto invalid_format;
684
685 /* split up the words */
686 *comma1 = *comma2 = *comma3 = '\0';
687
688 if (kstrtou64(buf, 0, &iova))
689 goto invalid_format;
690
691 if (kstrtou64(comma1 + 1, 0, &phys))
692 goto invalid_format;
693
694 if (kstrtoul(comma2 + 1, 0, &size))
695 goto invalid_format;
696
697 if (kstrtoint(comma3 + 1, 0, &prot))
698 goto invalid_format;
699
700 ret = iommu_map(ddev->domain, iova, phys, size, prot);
701 if (ret) {
702 pr_err("iommu_map failed with %d\n", ret);
703 retval = -EIO;
704 goto out;
705 }
706
707 retval = count;
708 pr_err("Mapped %pa to %pa (len=0x%zx, prot=0x%x)\n",
709 &iova, &phys, size, prot);
710out:
711 return retval;
712
713invalid_format:
714 pr_err("Invalid format. Expected: iova,phys,len,prot where `prot' is the bitwise OR of IOMMU_READ, IOMMU_WRITE, etc.\n");
715 return -EINVAL;
716}
717
718static const struct file_operations iommu_debug_map_fops = {
719 .open = simple_open,
720 .write = iommu_debug_map_write,
721};
722
723static ssize_t iommu_debug_unmap_write(struct file *file,
724 const char __user *ubuf,
725 size_t count, loff_t *offset)
726{
727 ssize_t retval = 0;
728 char *comma1;
729 char buf[100];
730 dma_addr_t iova;
731 size_t size;
732 size_t unmapped;
733 struct iommu_debug_device *ddev = file->private_data;
734
735 if (count >= 100) {
736 pr_err("Value too large\n");
737 return -EINVAL;
738 }
739
740 if (!ddev->domain) {
741 pr_err("No domain. Did you already attach?\n");
742 return -EINVAL;
743 }
744
745 memset(buf, 0, 100);
746
747 if (copy_from_user(buf, ubuf, count)) {
748 pr_err("Couldn't copy from user\n");
749 retval = -EFAULT;
750 goto out;
751 }
752
753 comma1 = strnchr(buf, count, ',');
754 if (!comma1)
755 goto invalid_format;
756
757 /* split up the words */
758 *comma1 = '\0';
759
760 if (kstrtou64(buf, 0, &iova))
761 goto invalid_format;
762
Mitchel Humpherys70b75112015-07-29 12:45:29 -0700763 if (kstrtoul(comma1 + 1, 0, &size))
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700764 goto invalid_format;
765
766 unmapped = iommu_unmap(ddev->domain, iova, size);
767 if (unmapped != size) {
768 pr_err("iommu_unmap failed. Expected to unmap: 0x%zx, unmapped: 0x%zx",
769 size, unmapped);
770 return -EIO;
771 }
772
773 retval = count;
774 pr_err("Unmapped %pa (len=0x%zx)\n", &iova, size);
775out:
776 return retval;
777
778invalid_format:
779 pr_err("Invalid format. Expected: iova,len\n");
780 return retval;
781}
782
783static const struct file_operations iommu_debug_unmap_fops = {
784 .open = simple_open,
785 .write = iommu_debug_unmap_write,
786};
787
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700788/*
789 * The following will only work for drivers that implement the generic
790 * device tree bindings described in
791 * Documentation/devicetree/bindings/iommu/iommu.txt
792 */
793static int snarf_iommu_devices(struct device *dev, void *ignored)
794{
795 struct iommu_debug_device *ddev;
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700796 struct dentry *dir;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700797
798 if (!of_find_property(dev->of_node, "iommus", NULL))
799 return 0;
800
Mitchel Humpherys89924fd2015-07-09 14:50:22 -0700801 ddev = kzalloc(sizeof(*ddev), GFP_KERNEL);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700802 if (!ddev)
803 return -ENODEV;
804 ddev->dev = dev;
805 dir = debugfs_create_dir(dev_name(dev), debugfs_tests_dir);
806 if (!dir) {
807 pr_err("Couldn't create iommu/devices/%s debugfs dir\n",
808 dev_name(dev));
809 goto err;
810 }
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700811
812 if (!debugfs_create_file("profiling", S_IRUSR, dir, ddev,
813 &iommu_debug_profiling_fops)) {
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700814 pr_err("Couldn't create iommu/devices/%s/profiling debugfs file\n",
815 dev_name(dev));
816 goto err_rmdir;
817 }
818
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700819 if (!debugfs_create_file("attach", S_IRUSR, dir, ddev,
820 &iommu_debug_attach_fops)) {
821 pr_err("Couldn't create iommu/devices/%s/attach debugfs file\n",
822 dev_name(dev));
823 goto err_rmdir;
824 }
825
826 if (!debugfs_create_file("atos", S_IWUSR, dir, ddev,
827 &iommu_debug_atos_fops)) {
828 pr_err("Couldn't create iommu/devices/%s/atos debugfs file\n",
829 dev_name(dev));
830 goto err_rmdir;
831 }
832
833 if (!debugfs_create_file("map", S_IWUSR, dir, ddev,
834 &iommu_debug_map_fops)) {
835 pr_err("Couldn't create iommu/devices/%s/map debugfs file\n",
836 dev_name(dev));
837 goto err_rmdir;
838 }
839
840 if (!debugfs_create_file("unmap", S_IWUSR, dir, ddev,
841 &iommu_debug_unmap_fops)) {
842 pr_err("Couldn't create iommu/devices/%s/unmap debugfs file\n",
843 dev_name(dev));
844 goto err_rmdir;
845 }
846
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700847 list_add(&ddev->list, &iommu_debug_devices);
848 return 0;
849
850err_rmdir:
851 debugfs_remove_recursive(dir);
852err:
853 kfree(ddev);
854 return 0;
855}
856
857static int iommu_debug_init_tests(void)
858{
859 debugfs_tests_dir = debugfs_create_dir("tests",
Mitchel Humpherysc75ae492015-07-15 18:27:36 -0700860 iommu_debugfs_top);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700861 if (!debugfs_tests_dir) {
862 pr_err("Couldn't create iommu/tests debugfs directory\n");
863 return -ENODEV;
864 }
865
866 return bus_for_each_dev(&platform_bus_type, NULL, NULL,
867 snarf_iommu_devices);
868}
Mitchel Humpherysc75ae492015-07-15 18:27:36 -0700869
870static void iommu_debug_destroy_tests(void)
871{
872 debugfs_remove_recursive(debugfs_tests_dir);
873}
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700874#else
875static inline int iommu_debug_init_tests(void) { return 0; }
Mitchel Humpherysc75ae492015-07-15 18:27:36 -0700876static inline void iommu_debug_destroy_tests(void) { }
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700877#endif
878
879static int iommu_debug_init(void)
880{
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700881 if (iommu_debug_init_tracking())
Mitchel Humpherysc75ae492015-07-15 18:27:36 -0700882 return -ENODEV;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700883
884 if (iommu_debug_init_tests())
Mitchel Humpherysc75ae492015-07-15 18:27:36 -0700885 return -ENODEV;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700886
887 return 0;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700888}
889
890static void iommu_debug_exit(void)
891{
Mitchel Humpherysc75ae492015-07-15 18:27:36 -0700892 iommu_debug_destroy_tracking();
893 iommu_debug_destroy_tests();
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700894}
895
896module_init(iommu_debug_init);
897module_exit(iommu_debug_exit);