blob: 75c0c92c7a88f53ef3f0b556ec7a0901d8c408b4 [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;
Mitchel Humpherys0dc04de2015-08-21 14:08:40 -070038 unsigned long reg_offset;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070039};
40
Mitchel Humpherys088cc582015-07-09 15:02:03 -070041static int iommu_debug_attachment_info_show(struct seq_file *s, void *ignored)
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070042{
43 struct iommu_debug_attachment *attach = s->private;
Mitchel Humpherys5e991f12015-07-30 19:25:54 -070044 int secure_vmid;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070045
46 seq_printf(s, "Domain: 0x%p\n", attach->domain);
Mitchel Humpherys5e991f12015-07-30 19:25:54 -070047
48 seq_puts(s, "SECURE_VMID: ");
49 if (iommu_domain_get_attr(attach->domain,
50 DOMAIN_ATTR_SECURE_VMID,
51 &secure_vmid))
52 seq_puts(s, "(Unknown)\n");
53 else
54 seq_printf(s, "%s (0x%x)\n",
55 msm_secure_vmid_to_string(secure_vmid), secure_vmid);
56
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070057 return 0;
58}
59
Mitchel Humpherys088cc582015-07-09 15:02:03 -070060static int iommu_debug_attachment_info_open(struct inode *inode,
61 struct file *file)
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070062{
Mitchel Humpherys088cc582015-07-09 15:02:03 -070063 return single_open(file, iommu_debug_attachment_info_show,
64 inode->i_private);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070065}
66
Mitchel Humpherys088cc582015-07-09 15:02:03 -070067static const struct file_operations iommu_debug_attachment_info_fops = {
68 .open = iommu_debug_attachment_info_open,
Mitchel Humpherys42296fb2015-06-23 16:29:16 -070069 .read = seq_read,
70 .llseek = seq_lseek,
71 .release = single_release,
72};
73
Mitchel Humpherys288086e2015-07-09 16:55:08 -070074static ssize_t iommu_debug_attachment_trigger_fault_write(
75 struct file *file, const char __user *ubuf, size_t count,
76 loff_t *offset)
77{
78 struct iommu_debug_attachment *attach = file->private_data;
79 unsigned long flags;
80
81 if (kstrtoul_from_user(ubuf, count, 0, &flags)) {
82 pr_err("Invalid flags format\n");
83 return -EFAULT;
84 }
85
86 iommu_trigger_fault(attach->domain, flags);
87
88 return count;
89}
90
91static const struct file_operations
92iommu_debug_attachment_trigger_fault_fops = {
93 .open = simple_open,
94 .write = iommu_debug_attachment_trigger_fault_write,
95};
96
Mitchel Humpherys0dc04de2015-08-21 14:08:40 -070097static ssize_t iommu_debug_attachment_reg_offset_write(
98 struct file *file, const char __user *ubuf, size_t count,
99 loff_t *offset)
100{
101 struct iommu_debug_attachment *attach = file->private_data;
102 unsigned long reg_offset;
103
104 if (kstrtoul_from_user(ubuf, count, 0, &reg_offset)) {
105 pr_err("Invalid reg_offset format\n");
106 return -EFAULT;
107 }
108
109 attach->reg_offset = reg_offset;
110
111 return count;
112}
113
114static const struct file_operations iommu_debug_attachment_reg_offset_fops = {
115 .open = simple_open,
116 .write = iommu_debug_attachment_reg_offset_write,
117};
118
119static ssize_t iommu_debug_attachment_reg_read_read(
120 struct file *file, char __user *ubuf, size_t count, loff_t *offset)
121{
122 struct iommu_debug_attachment *attach = file->private_data;
123 unsigned long val;
124 char *val_str;
125 ssize_t val_str_len;
126
127 if (*offset)
128 return 0;
129
130 val = iommu_reg_read(attach->domain, attach->reg_offset);
131 val_str = kasprintf(GFP_KERNEL, "0x%lx\n", val);
132 if (!val_str)
133 return -ENOMEM;
134 val_str_len = strlen(val_str);
135
136 if (copy_to_user(ubuf, val_str, val_str_len)) {
137 pr_err("copy_to_user failed\n");
138 val_str_len = -EFAULT;
139 goto out;
140 }
141 *offset = 1; /* non-zero means we're done */
142
143out:
144 kfree(val_str);
145 return val_str_len;
146}
147
148static const struct file_operations iommu_debug_attachment_reg_read_fops = {
149 .open = simple_open,
150 .read = iommu_debug_attachment_reg_read_read,
151};
152
153static ssize_t iommu_debug_attachment_reg_write_write(
154 struct file *file, const char __user *ubuf, size_t count,
155 loff_t *offset)
156{
157 struct iommu_debug_attachment *attach = file->private_data;
158 unsigned long val;
159
160 if (kstrtoul_from_user(ubuf, count, 0, &val)) {
161 pr_err("Invalid val format\n");
162 return -EFAULT;
163 }
164
165 iommu_reg_write(attach->domain, attach->reg_offset, val);
166
167 return count;
168}
169
170static const struct file_operations iommu_debug_attachment_reg_write_fops = {
171 .open = simple_open,
172 .write = iommu_debug_attachment_reg_write_write,
173};
174
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700175/* should be called with iommu_debug_attachments_lock locked */
176static int iommu_debug_attach_add_debugfs(
177 struct iommu_debug_attachment *attach)
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700178{
Mitchel Humpherys54379212015-08-26 11:52:57 -0700179 const char *attach_name;
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700180 struct device *dev = attach->dev;
181 struct iommu_domain *domain = attach->domain;
Mitchel Humpherys54379212015-08-26 11:52:57 -0700182 int is_dynamic;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700183
Mitchel Humpherys54379212015-08-26 11:52:57 -0700184 if (iommu_domain_get_attr(domain, DOMAIN_ATTR_DYNAMIC, &is_dynamic))
185 is_dynamic = 0;
186
187 if (is_dynamic) {
188 uuid_le uuid;
189
190 uuid_le_gen(&uuid);
191 attach_name = kasprintf(GFP_KERNEL, "%s-%pUl", dev_name(dev),
192 uuid.b);
193 if (!attach_name)
194 return -ENOMEM;
195 } else {
196 attach_name = dev_name(dev);
197 }
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700198
Mitchel Humpherys088cc582015-07-09 15:02:03 -0700199 attach->dentry = debugfs_create_dir(attach_name,
200 debugfs_attachments_dir);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700201 if (!attach->dentry) {
Mitchel Humpherys088cc582015-07-09 15:02:03 -0700202 pr_err("Couldn't create iommu/attachments/%s debugfs directory for domain 0x%p\n",
Mitchel Humpherys876e2be2015-07-10 11:56:56 -0700203 attach_name, domain);
Mitchel Humpherys54379212015-08-26 11:52:57 -0700204 if (is_dynamic)
205 kfree(attach_name);
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700206 return -EIO;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700207 }
Mitchel Humpherys54379212015-08-26 11:52:57 -0700208
209 if (is_dynamic)
210 kfree(attach_name);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700211
Mitchel Humpherys088cc582015-07-09 15:02:03 -0700212 if (!debugfs_create_file(
213 "info", S_IRUSR, attach->dentry, attach,
214 &iommu_debug_attachment_info_fops)) {
215 pr_err("Couldn't create iommu/attachments/%s/info debugfs file for domain 0x%p\n",
216 dev_name(dev), domain);
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700217 goto err_rmdir;
Mitchel Humpherys088cc582015-07-09 15:02:03 -0700218 }
219
Mitchel Humpherys288086e2015-07-09 16:55:08 -0700220 if (!debugfs_create_file(
221 "trigger_fault", S_IRUSR, attach->dentry, attach,
222 &iommu_debug_attachment_trigger_fault_fops)) {
223 pr_err("Couldn't create iommu/attachments/%s/trigger_fault debugfs file for domain 0x%p\n",
224 dev_name(dev), domain);
225 goto err_rmdir;
Mitchel Humpherys0dc04de2015-08-21 14:08:40 -0700226 }
227
228 if (!debugfs_create_file(
229 "reg_offset", S_IRUSR, attach->dentry, attach,
230 &iommu_debug_attachment_reg_offset_fops)) {
231 pr_err("Couldn't create iommu/attachments/%s/reg_offset debugfs file for domain 0x%p\n",
232 dev_name(dev), domain);
233 goto err_rmdir;
234 }
235
236 if (!debugfs_create_file(
237 "reg_read", S_IRUSR, attach->dentry, attach,
238 &iommu_debug_attachment_reg_read_fops)) {
239 pr_err("Couldn't create iommu/attachments/%s/reg_read debugfs file for domain 0x%p\n",
240 dev_name(dev), domain);
241 goto err_rmdir;
242 }
243
244 if (!debugfs_create_file(
245 "reg_write", S_IRUSR, attach->dentry, attach,
246 &iommu_debug_attachment_reg_write_fops)) {
247 pr_err("Couldn't create iommu/attachments/%s/reg_write debugfs file for domain 0x%p\n",
248 dev_name(dev), domain);
249 goto err_rmdir;
250 }
251
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700252 return 0;
253
254err_rmdir:
255 debugfs_remove_recursive(attach->dentry);
256 return -EIO;
257}
258
Susheel Khianie66aa5b2015-08-25 17:25:42 +0530259void iommu_debug_domain_add(struct iommu_domain *domain)
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700260{
261 struct iommu_debug_attachment *attach;
262
263 mutex_lock(&iommu_debug_attachments_lock);
264
265 attach = kmalloc(sizeof(*attach), GFP_KERNEL);
266 if (!attach)
267 goto out_unlock;
268
269 attach->domain = domain;
Susheel Khianie66aa5b2015-08-25 17:25:42 +0530270 attach->dev = NULL;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700271 list_add(&attach->list, &iommu_debug_attachments);
Susheel Khianie66aa5b2015-08-25 17:25:42 +0530272
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700273out_unlock:
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700274 mutex_unlock(&iommu_debug_attachments_lock);
275}
276
Susheel Khianie66aa5b2015-08-25 17:25:42 +0530277void iommu_debug_domain_remove(struct iommu_domain *domain)
278{
279 struct iommu_debug_attachment *it;
280
281 mutex_lock(&iommu_debug_attachments_lock);
282 list_for_each_entry(it, &iommu_debug_attachments, list)
283 if (it->domain == domain && it->dev == NULL)
284 break;
285
286 if (&it->list == &iommu_debug_attachments) {
287 WARN(1, "Couldn't find debug attachment for domain=0x%p",
288 domain);
289 } else {
290 list_del(&it->list);
291 kfree(it);
292 }
293 mutex_unlock(&iommu_debug_attachments_lock);
294}
295
296void iommu_debug_attach_device(struct iommu_domain *domain,
297 struct device *dev)
298{
299 struct iommu_debug_attachment *attach;
300
301 mutex_lock(&iommu_debug_attachments_lock);
302
303 list_for_each_entry(attach, &iommu_debug_attachments, list)
304 if (attach->domain == domain && attach->dev == NULL)
305 break;
306
307 if (&attach->list == &iommu_debug_attachments) {
308 WARN(1, "Couldn't find debug attachment for domain=0x%p dev=%s",
309 domain, dev_name(dev));
310 } else {
311 attach->dev = dev;
312
313 /*
314 * we might not init until after other drivers start calling
315 * iommu_attach_device. Only set up the debugfs nodes if we've
316 * already init'd to avoid polluting the top-level debugfs
317 * directory (by calling debugfs_create_dir with a NULL
318 * parent). These will be flushed out later once we init.
319 */
320
321 if (debugfs_attachments_dir)
322 iommu_debug_attach_add_debugfs(attach);
323 }
324
325 mutex_unlock(&iommu_debug_attachments_lock);
326}
327
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700328void iommu_debug_detach_device(struct iommu_domain *domain,
329 struct device *dev)
330{
331 struct iommu_debug_attachment *it;
332
333 mutex_lock(&iommu_debug_attachments_lock);
334 list_for_each_entry(it, &iommu_debug_attachments, list)
335 if (it->domain == domain && it->dev == dev)
336 break;
337
338 if (&it->list == &iommu_debug_attachments) {
339 WARN(1, "Couldn't find debug attachment for domain=0x%p dev=%s",
340 domain, dev_name(dev));
341 } else {
Susheel Khianie66aa5b2015-08-25 17:25:42 +0530342 /*
343 * Just remove debugfs entry and mark dev as NULL on
344 * iommu_detach call. We would remove the actual
345 * attachment entry from the list only on domain_free call.
346 * This is to ensure we keep track of unattached domains too.
347 */
348
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700349 debugfs_remove_recursive(it->dentry);
Susheel Khianie66aa5b2015-08-25 17:25:42 +0530350 it->dev = NULL;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700351 }
352 mutex_unlock(&iommu_debug_attachments_lock);
353}
354
355static int iommu_debug_init_tracking(void)
356{
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700357 int ret = 0;
358 struct iommu_debug_attachment *attach;
359
360 mutex_lock(&iommu_debug_attachments_lock);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700361 debugfs_attachments_dir = debugfs_create_dir("attachments",
Mitchel Humpherysc75ae492015-07-15 18:27:36 -0700362 iommu_debugfs_top);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700363 if (!debugfs_attachments_dir) {
364 pr_err("Couldn't create iommu/attachments debugfs directory\n");
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700365 ret = -ENODEV;
366 goto out_unlock;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700367 }
368
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700369 /* set up debugfs entries for attachments made during early boot */
370 list_for_each_entry(attach, &iommu_debug_attachments, list)
Susheel Khianie66aa5b2015-08-25 17:25:42 +0530371 if (attach->dev)
372 iommu_debug_attach_add_debugfs(attach);
Mitchel Humpherysa05b2522015-07-14 14:30:33 -0700373
374out_unlock:
375 mutex_unlock(&iommu_debug_attachments_lock);
376 return ret;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700377}
Mitchel Humpherysc75ae492015-07-15 18:27:36 -0700378
379static void iommu_debug_destroy_tracking(void)
380{
381 debugfs_remove_recursive(debugfs_attachments_dir);
382}
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700383#else
384static inline int iommu_debug_init_tracking(void) { return 0; }
Mitchel Humpherysc75ae492015-07-15 18:27:36 -0700385static inline void iommu_debug_destroy_tracking(void) { }
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700386#endif
387
388#ifdef CONFIG_IOMMU_TESTS
389
390static LIST_HEAD(iommu_debug_devices);
391static struct dentry *debugfs_tests_dir;
392
393struct iommu_debug_device {
394 struct device *dev;
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700395 struct iommu_domain *domain;
396 u64 iova;
397 u64 phys;
398 size_t len;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700399 struct list_head list;
400};
401
402static int iommu_debug_build_phoney_sg_table(struct device *dev,
403 struct sg_table *table,
404 unsigned long total_size,
405 unsigned long chunk_size)
406{
407 unsigned long nents = total_size / chunk_size;
408 struct scatterlist *sg;
409 int i;
410 struct page *page;
411
412 if (!IS_ALIGNED(total_size, PAGE_SIZE))
413 return -EINVAL;
414 if (!IS_ALIGNED(total_size, chunk_size))
415 return -EINVAL;
416 if (sg_alloc_table(table, nents, GFP_KERNEL))
417 return -EINVAL;
418 page = alloc_pages(GFP_KERNEL, get_order(chunk_size));
419 if (!page)
420 goto free_table;
421
422 /* all the same page... why not. */
423 for_each_sg(table->sgl, sg, table->nents, i)
424 sg_set_page(sg, page, chunk_size, 0);
425
426 return 0;
427
428free_table:
429 sg_free_table(table);
430 return -ENOMEM;
431}
432
433static void iommu_debug_destroy_phoney_sg_table(struct device *dev,
434 struct sg_table *table,
435 unsigned long chunk_size)
436{
437 __free_pages(sg_page(table->sgl), get_order(chunk_size));
438 sg_free_table(table);
439}
440
441static const char * const _size_to_string(unsigned long size)
442{
443 switch (size) {
444 case SZ_4K:
445 return "4K";
446 case SZ_64K:
447 return "64K";
448 case SZ_2M:
449 return "2M";
450 case SZ_1M * 12:
451 return "12M";
452 case SZ_1M * 20:
453 return "20M";
454 }
455 return "unknown size, please add to _size_to_string";
456}
457
Mitchel Humpherys1c47bb52015-10-02 16:17:57 -0700458#define ITERS_PER_OP 100
459
Mitchel Humpherys020f90f2015-10-02 16:02:31 -0700460static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev,
461 bool secure)
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700462{
463 unsigned long sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12,
464 SZ_1M * 20, 0 };
465 unsigned long *sz;
466 struct iommu_domain *domain;
467 unsigned long iova = 0x10000;
468 phys_addr_t paddr = 0xa000;
Mitchel Humpherys679567c2015-08-28 10:51:24 -0700469 int atomic_domain = 1;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700470
471 domain = iommu_domain_alloc(&platform_bus_type);
472 if (!domain) {
473 seq_puts(s, "Couldn't allocate domain\n");
474 return;
475 }
476
Mitchel Humpherys679567c2015-08-28 10:51:24 -0700477 if (iommu_domain_set_attr(domain, DOMAIN_ATTR_ATOMIC,
478 &atomic_domain)) {
479 seq_printf(s, "Couldn't set atomic_domain to %d\n",
480 atomic_domain);
481 goto out_domain_free;
482 }
483
Mitchel Humpherys020f90f2015-10-02 16:02:31 -0700484 if (secure) {
485 int secure_vmid = VMID_CP_PIXEL;
486
487 if (iommu_domain_set_attr(domain, DOMAIN_ATTR_SECURE_VMID,
488 &secure_vmid)) {
489 seq_printf(s, "Couldn't set secure vmid to %d\n",
490 secure_vmid);
491 goto out_domain_free;
492 }
493 }
494
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700495 if (iommu_attach_device(domain, dev)) {
496 seq_puts(s,
497 "Couldn't attach new domain to device. Is it already attached?\n");
498 goto out_domain_free;
499 }
500
Mitchel Humpherys1c47bb52015-10-02 16:17:57 -0700501 seq_printf(s, "(average over %d iterations)\n", ITERS_PER_OP);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700502 seq_printf(s, "%8s %15s %12s\n", "size", "iommu_map", "iommu_unmap");
503 for (sz = sizes; *sz; ++sz) {
504 unsigned long size = *sz;
505 size_t unmapped;
Mitchel Humpherys1c47bb52015-10-02 16:17:57 -0700506 u64 map_elapsed_us = 0, unmap_elapsed_us = 0;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700507 struct timespec tbefore, tafter, diff;
Mitchel Humpherys1c47bb52015-10-02 16:17:57 -0700508 int i;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700509
Mitchel Humpherys1c47bb52015-10-02 16:17:57 -0700510 for (i = 0; i < ITERS_PER_OP; ++i) {
511 getnstimeofday(&tbefore);
512 if (iommu_map(domain, iova, paddr, size,
513 IOMMU_READ | IOMMU_WRITE)) {
514 seq_puts(s, "Failed to map\n");
515 continue;
516 }
517 getnstimeofday(&tafter);
518 diff = timespec_sub(tafter, tbefore);
519 map_elapsed_us += div_s64(timespec_to_ns(&diff), 1000);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700520
Mitchel Humpherys1c47bb52015-10-02 16:17:57 -0700521 getnstimeofday(&tbefore);
522 unmapped = iommu_unmap(domain, iova, size);
523 if (unmapped != size) {
524 seq_printf(s,
525 "Only unmapped %zx instead of %zx\n",
526 unmapped, size);
527 continue;
528 }
529 getnstimeofday(&tafter);
530 diff = timespec_sub(tafter, tbefore);
531 unmap_elapsed_us += div_s64(timespec_to_ns(&diff),
532 1000);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700533 }
Mitchel Humpherys1c47bb52015-10-02 16:17:57 -0700534
535 map_elapsed_us /= ITERS_PER_OP;
536 unmap_elapsed_us /= ITERS_PER_OP;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700537
538 seq_printf(s, "%8s %12lld us %9lld us\n", _size_to_string(size),
539 map_elapsed_us, unmap_elapsed_us);
540 }
541
542 seq_putc(s, '\n');
543 seq_printf(s, "%8s %15s %12s\n", "size", "iommu_map_sg", "iommu_unmap");
544 for (sz = sizes; *sz; ++sz) {
545 unsigned long size = *sz;
546 size_t unmapped;
Mitchel Humpherys1c47bb52015-10-02 16:17:57 -0700547 u64 map_elapsed_us = 0, unmap_elapsed_us = 0;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700548 struct timespec tbefore, tafter, diff;
549 struct sg_table table;
550 unsigned long chunk_size = SZ_4K;
Mitchel Humpherys1c47bb52015-10-02 16:17:57 -0700551 int i;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700552
553 if (iommu_debug_build_phoney_sg_table(dev, &table, size,
554 chunk_size)) {
555 seq_puts(s,
556 "couldn't build phoney sg table! bailing...\n");
557 goto out_detach;
558 }
559
Mitchel Humpherys1c47bb52015-10-02 16:17:57 -0700560 for (i = 0; i < ITERS_PER_OP; ++i) {
561 getnstimeofday(&tbefore);
562 if (iommu_map_sg(domain, iova, table.sgl, table.nents,
563 IOMMU_READ | IOMMU_WRITE) != size) {
564 seq_puts(s, "Failed to map_sg\n");
565 goto next;
566 }
567 getnstimeofday(&tafter);
568 diff = timespec_sub(tafter, tbefore);
569 map_elapsed_us += div_s64(timespec_to_ns(&diff), 1000);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700570
Mitchel Humpherys1c47bb52015-10-02 16:17:57 -0700571 getnstimeofday(&tbefore);
572 unmapped = iommu_unmap(domain, iova, size);
573 if (unmapped != size) {
574 seq_printf(s,
575 "Only unmapped %zx instead of %zx\n",
576 unmapped, size);
577 goto next;
578 }
579 getnstimeofday(&tafter);
580 diff = timespec_sub(tafter, tbefore);
581 unmap_elapsed_us += div_s64(timespec_to_ns(&diff),
582 1000);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700583 }
Mitchel Humpherys1c47bb52015-10-02 16:17:57 -0700584
585 map_elapsed_us /= ITERS_PER_OP;
586 unmap_elapsed_us /= ITERS_PER_OP;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700587
588 seq_printf(s, "%8s %12lld us %9lld us\n", _size_to_string(size),
589 map_elapsed_us, unmap_elapsed_us);
590
591next:
592 iommu_debug_destroy_phoney_sg_table(dev, &table, chunk_size);
593 }
594
595out_detach:
596 iommu_detach_device(domain, dev);
597out_domain_free:
598 iommu_domain_free(domain);
599}
600
Mitchel Humpherys7cc56e42015-07-06 14:58:23 -0700601static int iommu_debug_profiling_show(struct seq_file *s, void *ignored)
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700602{
603 struct iommu_debug_device *ddev = s->private;
604
Mitchel Humpherys020f90f2015-10-02 16:02:31 -0700605 iommu_debug_device_profiling(s, ddev->dev, false);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700606
607 return 0;
608}
609
Mitchel Humpherys7cc56e42015-07-06 14:58:23 -0700610static int iommu_debug_profiling_open(struct inode *inode, struct file *file)
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700611{
Mitchel Humpherys7cc56e42015-07-06 14:58:23 -0700612 return single_open(file, iommu_debug_profiling_show, inode->i_private);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700613}
614
Mitchel Humpherys7cc56e42015-07-06 14:58:23 -0700615static const struct file_operations iommu_debug_profiling_fops = {
616 .open = iommu_debug_profiling_open,
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700617 .read = seq_read,
618 .llseek = seq_lseek,
619 .release = single_release,
620};
621
Mitchel Humpherys020f90f2015-10-02 16:02:31 -0700622static int iommu_debug_secure_profiling_show(struct seq_file *s, void *ignored)
623{
624 struct iommu_debug_device *ddev = s->private;
625
626 iommu_debug_device_profiling(s, ddev->dev, true);
627
628 return 0;
629}
630
631static int iommu_debug_secure_profiling_open(struct inode *inode,
632 struct file *file)
633{
634 return single_open(file, iommu_debug_secure_profiling_show,
635 inode->i_private);
636}
637
638static const struct file_operations iommu_debug_secure_profiling_fops = {
639 .open = iommu_debug_secure_profiling_open,
640 .read = seq_read,
641 .llseek = seq_lseek,
642 .release = single_release,
643};
644
Mitchel Humpherysac4c38b2015-07-30 19:40:21 -0700645static int iommu_debug_attach_do_attach(struct iommu_debug_device *ddev,
646 int val, bool is_secure)
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700647{
648 ddev->domain = iommu_domain_alloc(&platform_bus_type);
649 if (!ddev->domain) {
650 pr_err("Couldn't allocate domain\n");
651 return -ENOMEM;
652 }
653
Mitchel Humpherysac4c38b2015-07-30 19:40:21 -0700654 if (is_secure && iommu_domain_set_attr(ddev->domain,
655 DOMAIN_ATTR_SECURE_VMID,
656 &val)) {
657 pr_err("Couldn't set secure vmid to %d\n", val);
658 goto out_domain_free;
659 }
660
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700661 if (iommu_attach_device(ddev->domain, ddev->dev)) {
662 pr_err("Couldn't attach new domain to device. Is it already attached?\n");
663 goto out_domain_free;
664 }
665
666 return 0;
667
668out_domain_free:
669 iommu_domain_free(ddev->domain);
670 ddev->domain = NULL;
671 return -EIO;
672}
673
Mitchel Humpherysac4c38b2015-07-30 19:40:21 -0700674static ssize_t __iommu_debug_attach_write(struct file *file,
675 const char __user *ubuf,
676 size_t count, loff_t *offset,
677 bool is_secure)
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700678{
679 struct iommu_debug_device *ddev = file->private_data;
680 ssize_t retval;
Mitchel Humpherysac4c38b2015-07-30 19:40:21 -0700681 int val;
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700682
Mitchel Humpherysac4c38b2015-07-30 19:40:21 -0700683 if (kstrtoint_from_user(ubuf, count, 0, &val)) {
684 pr_err("Invalid format. Expected a hex or decimal integer");
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700685 retval = -EFAULT;
686 goto out;
687 }
688
Mitchel Humpherysac4c38b2015-07-30 19:40:21 -0700689 if (val) {
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700690 if (ddev->domain) {
691 pr_err("Already attached.\n");
692 retval = -EINVAL;
693 goto out;
694 }
695 if (WARN(ddev->dev->archdata.iommu,
696 "Attachment tracking out of sync with device\n")) {
697 retval = -EINVAL;
698 goto out;
699 }
Mitchel Humpherysac4c38b2015-07-30 19:40:21 -0700700 if (iommu_debug_attach_do_attach(ddev, val, is_secure)) {
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700701 retval = -EIO;
702 goto out;
703 }
704 pr_err("Attached\n");
Mitchel Humpherysac4c38b2015-07-30 19:40:21 -0700705 } else {
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700706 if (!ddev->domain) {
707 pr_err("No domain. Did you already attach?\n");
708 retval = -EINVAL;
709 goto out;
710 }
711 iommu_detach_device(ddev->domain, ddev->dev);
712 iommu_domain_free(ddev->domain);
713 ddev->domain = NULL;
714 pr_err("Detached\n");
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700715 }
716
717 retval = count;
718out:
719 return retval;
720}
721
Mitchel Humpherysac4c38b2015-07-30 19:40:21 -0700722static ssize_t iommu_debug_attach_write(struct file *file,
723 const char __user *ubuf,
724 size_t count, loff_t *offset)
725{
726 return __iommu_debug_attach_write(file, ubuf, count, offset,
727 false);
728
729}
730
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700731static ssize_t iommu_debug_attach_read(struct file *file, char __user *ubuf,
732 size_t count, loff_t *offset)
733{
734 struct iommu_debug_device *ddev = file->private_data;
735 char c[2];
736
737 if (*offset)
738 return 0;
739
740 c[0] = ddev->domain ? '1' : '0';
741 c[1] = '\n';
742 if (copy_to_user(ubuf, &c, 2)) {
743 pr_err("copy_to_user failed\n");
744 return -EFAULT;
745 }
746 *offset = 1; /* non-zero means we're done */
747
748 return 2;
749}
750
751static const struct file_operations iommu_debug_attach_fops = {
752 .open = simple_open,
753 .write = iommu_debug_attach_write,
754 .read = iommu_debug_attach_read,
755};
756
Mitchel Humpherysac4c38b2015-07-30 19:40:21 -0700757static ssize_t iommu_debug_attach_write_secure(struct file *file,
758 const char __user *ubuf,
759 size_t count, loff_t *offset)
760{
761 return __iommu_debug_attach_write(file, ubuf, count, offset,
762 true);
763
764}
765
766static const struct file_operations iommu_debug_secure_attach_fops = {
767 .open = simple_open,
768 .write = iommu_debug_attach_write_secure,
769 .read = iommu_debug_attach_read,
770};
771
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700772static ssize_t iommu_debug_atos_write(struct file *file,
773 const char __user *ubuf,
774 size_t count, loff_t *offset)
775{
776 struct iommu_debug_device *ddev = file->private_data;
777 dma_addr_t iova;
778
779 if (kstrtoll_from_user(ubuf, count, 0, &iova)) {
780 pr_err("Invalid format for iova\n");
781 ddev->iova = 0;
782 return -EINVAL;
783 }
784
785 ddev->iova = iova;
786 pr_err("Saved iova=%pa for future ATOS commands\n", &iova);
787 return count;
788}
789
790static ssize_t iommu_debug_atos_read(struct file *file, char __user *ubuf,
791 size_t count, loff_t *offset)
792{
793 struct iommu_debug_device *ddev = file->private_data;
794 phys_addr_t phys;
795 char buf[100];
796 ssize_t retval;
797 size_t buflen;
798
799 if (!ddev->domain) {
800 pr_err("No domain. Did you already attach?\n");
801 return -EINVAL;
802 }
803
804 if (*offset)
805 return 0;
806
807 memset(buf, 0, 100);
808
809 phys = iommu_iova_to_phys_hard(ddev->domain, ddev->iova);
810 if (!phys)
811 strlcpy(buf, "FAIL\n", 100);
812 else
813 snprintf(buf, 100, "%pa\n", &phys);
814
815 buflen = strlen(buf);
816 if (copy_to_user(ubuf, buf, buflen)) {
817 pr_err("Couldn't copy_to_user\n");
818 retval = -EFAULT;
819 } else {
820 *offset = 1; /* non-zero means we're done */
821 retval = buflen;
822 }
823
824 return retval;
825}
826
827static const struct file_operations iommu_debug_atos_fops = {
828 .open = simple_open,
829 .write = iommu_debug_atos_write,
830 .read = iommu_debug_atos_read,
831};
832
833static ssize_t iommu_debug_map_write(struct file *file, const char __user *ubuf,
834 size_t count, loff_t *offset)
835{
836 ssize_t retval;
837 int ret;
838 char *comma1, *comma2, *comma3;
839 char buf[100];
840 dma_addr_t iova;
841 phys_addr_t phys;
842 size_t size;
843 int prot;
844 struct iommu_debug_device *ddev = file->private_data;
845
846 if (count >= 100) {
847 pr_err("Value too large\n");
848 return -EINVAL;
849 }
850
851 if (!ddev->domain) {
852 pr_err("No domain. Did you already attach?\n");
853 return -EINVAL;
854 }
855
856 memset(buf, 0, 100);
857
858 if (copy_from_user(buf, ubuf, count)) {
859 pr_err("Couldn't copy from user\n");
860 retval = -EFAULT;
861 }
862
863 comma1 = strnchr(buf, count, ',');
864 if (!comma1)
865 goto invalid_format;
866
867 comma2 = strnchr(comma1 + 1, count, ',');
868 if (!comma2)
869 goto invalid_format;
870
871 comma3 = strnchr(comma2 + 1, count, ',');
872 if (!comma3)
873 goto invalid_format;
874
875 /* split up the words */
876 *comma1 = *comma2 = *comma3 = '\0';
877
878 if (kstrtou64(buf, 0, &iova))
879 goto invalid_format;
880
881 if (kstrtou64(comma1 + 1, 0, &phys))
882 goto invalid_format;
883
884 if (kstrtoul(comma2 + 1, 0, &size))
885 goto invalid_format;
886
887 if (kstrtoint(comma3 + 1, 0, &prot))
888 goto invalid_format;
889
890 ret = iommu_map(ddev->domain, iova, phys, size, prot);
891 if (ret) {
892 pr_err("iommu_map failed with %d\n", ret);
893 retval = -EIO;
894 goto out;
895 }
896
897 retval = count;
898 pr_err("Mapped %pa to %pa (len=0x%zx, prot=0x%x)\n",
899 &iova, &phys, size, prot);
900out:
901 return retval;
902
903invalid_format:
904 pr_err("Invalid format. Expected: iova,phys,len,prot where `prot' is the bitwise OR of IOMMU_READ, IOMMU_WRITE, etc.\n");
905 return -EINVAL;
906}
907
908static const struct file_operations iommu_debug_map_fops = {
909 .open = simple_open,
910 .write = iommu_debug_map_write,
911};
912
913static ssize_t iommu_debug_unmap_write(struct file *file,
914 const char __user *ubuf,
915 size_t count, loff_t *offset)
916{
917 ssize_t retval = 0;
918 char *comma1;
919 char buf[100];
920 dma_addr_t iova;
921 size_t size;
922 size_t unmapped;
923 struct iommu_debug_device *ddev = file->private_data;
924
925 if (count >= 100) {
926 pr_err("Value too large\n");
927 return -EINVAL;
928 }
929
930 if (!ddev->domain) {
931 pr_err("No domain. Did you already attach?\n");
932 return -EINVAL;
933 }
934
935 memset(buf, 0, 100);
936
937 if (copy_from_user(buf, ubuf, count)) {
938 pr_err("Couldn't copy from user\n");
939 retval = -EFAULT;
940 goto out;
941 }
942
943 comma1 = strnchr(buf, count, ',');
944 if (!comma1)
945 goto invalid_format;
946
947 /* split up the words */
948 *comma1 = '\0';
949
950 if (kstrtou64(buf, 0, &iova))
951 goto invalid_format;
952
Mitchel Humpherys70b75112015-07-29 12:45:29 -0700953 if (kstrtoul(comma1 + 1, 0, &size))
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700954 goto invalid_format;
955
956 unmapped = iommu_unmap(ddev->domain, iova, size);
957 if (unmapped != size) {
958 pr_err("iommu_unmap failed. Expected to unmap: 0x%zx, unmapped: 0x%zx",
959 size, unmapped);
960 return -EIO;
961 }
962
963 retval = count;
964 pr_err("Unmapped %pa (len=0x%zx)\n", &iova, size);
965out:
966 return retval;
967
968invalid_format:
969 pr_err("Invalid format. Expected: iova,len\n");
970 return retval;
971}
972
973static const struct file_operations iommu_debug_unmap_fops = {
974 .open = simple_open,
975 .write = iommu_debug_unmap_write,
976};
977
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700978/*
979 * The following will only work for drivers that implement the generic
980 * device tree bindings described in
981 * Documentation/devicetree/bindings/iommu/iommu.txt
982 */
983static int snarf_iommu_devices(struct device *dev, void *ignored)
984{
985 struct iommu_debug_device *ddev;
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -0700986 struct dentry *dir;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700987
988 if (!of_find_property(dev->of_node, "iommus", NULL))
989 return 0;
990
Mitchel Humpherys89924fd2015-07-09 14:50:22 -0700991 ddev = kzalloc(sizeof(*ddev), GFP_KERNEL);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -0700992 if (!ddev)
993 return -ENODEV;
994 ddev->dev = dev;
995 dir = debugfs_create_dir(dev_name(dev), debugfs_tests_dir);
996 if (!dir) {
997 pr_err("Couldn't create iommu/devices/%s debugfs dir\n",
998 dev_name(dev));
999 goto err;
1000 }
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -07001001
1002 if (!debugfs_create_file("profiling", S_IRUSR, dir, ddev,
1003 &iommu_debug_profiling_fops)) {
Mitchel Humpherys42296fb2015-06-23 16:29:16 -07001004 pr_err("Couldn't create iommu/devices/%s/profiling debugfs file\n",
1005 dev_name(dev));
1006 goto err_rmdir;
1007 }
1008
Mitchel Humpherys020f90f2015-10-02 16:02:31 -07001009 if (!debugfs_create_file("secure_profiling", S_IRUSR, dir, ddev,
1010 &iommu_debug_secure_profiling_fops)) {
1011 pr_err("Couldn't create iommu/devices/%s/secure_profiling debugfs file\n",
1012 dev_name(dev));
1013 goto err_rmdir;
1014 }
1015
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -07001016 if (!debugfs_create_file("attach", S_IRUSR, dir, ddev,
1017 &iommu_debug_attach_fops)) {
1018 pr_err("Couldn't create iommu/devices/%s/attach debugfs file\n",
1019 dev_name(dev));
1020 goto err_rmdir;
1021 }
1022
Mitchel Humpherysac4c38b2015-07-30 19:40:21 -07001023 if (!debugfs_create_file("secure_attach", S_IRUSR, dir, ddev,
1024 &iommu_debug_secure_attach_fops)) {
1025 pr_err("Couldn't create iommu/devices/%s/secure_attach debugfs file\n",
1026 dev_name(dev));
1027 goto err_rmdir;
1028 }
1029
Mitchel Humpherys0fe337d2015-07-06 15:21:24 -07001030 if (!debugfs_create_file("atos", S_IWUSR, dir, ddev,
1031 &iommu_debug_atos_fops)) {
1032 pr_err("Couldn't create iommu/devices/%s/atos debugfs file\n",
1033 dev_name(dev));
1034 goto err_rmdir;
1035 }
1036
1037 if (!debugfs_create_file("map", S_IWUSR, dir, ddev,
1038 &iommu_debug_map_fops)) {
1039 pr_err("Couldn't create iommu/devices/%s/map debugfs file\n",
1040 dev_name(dev));
1041 goto err_rmdir;
1042 }
1043
1044 if (!debugfs_create_file("unmap", S_IWUSR, dir, ddev,
1045 &iommu_debug_unmap_fops)) {
1046 pr_err("Couldn't create iommu/devices/%s/unmap debugfs file\n",
1047 dev_name(dev));
1048 goto err_rmdir;
1049 }
1050
Mitchel Humpherys42296fb2015-06-23 16:29:16 -07001051 list_add(&ddev->list, &iommu_debug_devices);
1052 return 0;
1053
1054err_rmdir:
1055 debugfs_remove_recursive(dir);
1056err:
1057 kfree(ddev);
1058 return 0;
1059}
1060
1061static int iommu_debug_init_tests(void)
1062{
1063 debugfs_tests_dir = debugfs_create_dir("tests",
Mitchel Humpherysc75ae492015-07-15 18:27:36 -07001064 iommu_debugfs_top);
Mitchel Humpherys42296fb2015-06-23 16:29:16 -07001065 if (!debugfs_tests_dir) {
1066 pr_err("Couldn't create iommu/tests debugfs directory\n");
1067 return -ENODEV;
1068 }
1069
1070 return bus_for_each_dev(&platform_bus_type, NULL, NULL,
1071 snarf_iommu_devices);
1072}
Mitchel Humpherysc75ae492015-07-15 18:27:36 -07001073
1074static void iommu_debug_destroy_tests(void)
1075{
1076 debugfs_remove_recursive(debugfs_tests_dir);
1077}
Mitchel Humpherys42296fb2015-06-23 16:29:16 -07001078#else
1079static inline int iommu_debug_init_tests(void) { return 0; }
Mitchel Humpherysc75ae492015-07-15 18:27:36 -07001080static inline void iommu_debug_destroy_tests(void) { }
Mitchel Humpherys42296fb2015-06-23 16:29:16 -07001081#endif
1082
1083static int iommu_debug_init(void)
1084{
Mitchel Humpherys42296fb2015-06-23 16:29:16 -07001085 if (iommu_debug_init_tracking())
Mitchel Humpherysc75ae492015-07-15 18:27:36 -07001086 return -ENODEV;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -07001087
1088 if (iommu_debug_init_tests())
Mitchel Humpherysc75ae492015-07-15 18:27:36 -07001089 return -ENODEV;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -07001090
1091 return 0;
Mitchel Humpherys42296fb2015-06-23 16:29:16 -07001092}
1093
1094static void iommu_debug_exit(void)
1095{
Mitchel Humpherysc75ae492015-07-15 18:27:36 -07001096 iommu_debug_destroy_tracking();
1097 iommu_debug_destroy_tests();
Mitchel Humpherys42296fb2015-06-23 16:29:16 -07001098}
1099
1100module_init(iommu_debug_init);
1101module_exit(iommu_debug_exit);