xref: /linux/drivers/iommu/omap-iommu-debug.c (revision a67ff6a54095e27093ea501fb143fefe51a536c2)
1 /*
2  * omap iommu: debugfs interface
3  *
4  * Copyright (C) 2008-2009 Nokia Corporation
5  *
6  * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
12 
13 #include <linux/err.h>
14 #include <linux/clk.h>
15 #include <linux/io.h>
16 #include <linux/slab.h>
17 #include <linux/uaccess.h>
18 #include <linux/platform_device.h>
19 #include <linux/debugfs.h>
20 
21 #include <plat/iommu.h>
22 #include <plat/iovmm.h>
23 
24 #include <plat/iopgtable.h>
25 
26 #define MAXCOLUMN 100 /* for short messages */
27 
28 static DEFINE_MUTEX(iommu_debug_lock);
29 
30 static struct dentry *iommu_debug_root;
31 
32 static ssize_t debug_read_ver(struct file *file, char __user *userbuf,
33 			      size_t count, loff_t *ppos)
34 {
35 	u32 ver = omap_iommu_arch_version();
36 	char buf[MAXCOLUMN], *p = buf;
37 
38 	p += sprintf(p, "H/W version: %d.%d\n", (ver >> 4) & 0xf , ver & 0xf);
39 
40 	return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
41 }
42 
43 static ssize_t debug_read_regs(struct file *file, char __user *userbuf,
44 			       size_t count, loff_t *ppos)
45 {
46 	struct omap_iommu *obj = file->private_data;
47 	char *p, *buf;
48 	ssize_t bytes;
49 
50 	buf = kmalloc(count, GFP_KERNEL);
51 	if (!buf)
52 		return -ENOMEM;
53 	p = buf;
54 
55 	mutex_lock(&iommu_debug_lock);
56 
57 	bytes = omap_iommu_dump_ctx(obj, p, count);
58 	bytes = simple_read_from_buffer(userbuf, count, ppos, buf, bytes);
59 
60 	mutex_unlock(&iommu_debug_lock);
61 	kfree(buf);
62 
63 	return bytes;
64 }
65 
66 static ssize_t debug_read_tlb(struct file *file, char __user *userbuf,
67 			      size_t count, loff_t *ppos)
68 {
69 	struct omap_iommu *obj = file->private_data;
70 	char *p, *buf;
71 	ssize_t bytes, rest;
72 
73 	buf = kmalloc(count, GFP_KERNEL);
74 	if (!buf)
75 		return -ENOMEM;
76 	p = buf;
77 
78 	mutex_lock(&iommu_debug_lock);
79 
80 	p += sprintf(p, "%8s %8s\n", "cam:", "ram:");
81 	p += sprintf(p, "-----------------------------------------\n");
82 	rest = count - (p - buf);
83 	p += omap_dump_tlb_entries(obj, p, rest);
84 
85 	bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
86 
87 	mutex_unlock(&iommu_debug_lock);
88 	kfree(buf);
89 
90 	return bytes;
91 }
92 
93 static ssize_t debug_write_pagetable(struct file *file,
94 		     const char __user *userbuf, size_t count, loff_t *ppos)
95 {
96 	struct iotlb_entry e;
97 	struct cr_regs cr;
98 	int err;
99 	struct omap_iommu *obj = file->private_data;
100 	char buf[MAXCOLUMN], *p = buf;
101 
102 	count = min(count, sizeof(buf));
103 
104 	mutex_lock(&iommu_debug_lock);
105 	if (copy_from_user(p, userbuf, count)) {
106 		mutex_unlock(&iommu_debug_lock);
107 		return -EFAULT;
108 	}
109 
110 	sscanf(p, "%x %x", &cr.cam, &cr.ram);
111 	if (!cr.cam || !cr.ram) {
112 		mutex_unlock(&iommu_debug_lock);
113 		return -EINVAL;
114 	}
115 
116 	omap_iotlb_cr_to_e(&cr, &e);
117 	err = omap_iopgtable_store_entry(obj, &e);
118 	if (err)
119 		dev_err(obj->dev, "%s: fail to store cr\n", __func__);
120 
121 	mutex_unlock(&iommu_debug_lock);
122 	return count;
123 }
124 
125 #define dump_ioptable_entry_one(lv, da, val)			\
126 	({							\
127 		int __err = 0;					\
128 		ssize_t bytes;					\
129 		const int maxcol = 22;				\
130 		const char *str = "%d: %08x %08x\n";		\
131 		bytes = snprintf(p, maxcol, str, lv, da, val);	\
132 		p += bytes;					\
133 		len -= bytes;					\
134 		if (len < maxcol)				\
135 			__err = -ENOMEM;			\
136 		__err;						\
137 	})
138 
139 static ssize_t dump_ioptable(struct omap_iommu *obj, char *buf, ssize_t len)
140 {
141 	int i;
142 	u32 *iopgd;
143 	char *p = buf;
144 
145 	spin_lock(&obj->page_table_lock);
146 
147 	iopgd = iopgd_offset(obj, 0);
148 	for (i = 0; i < PTRS_PER_IOPGD; i++, iopgd++) {
149 		int j, err;
150 		u32 *iopte;
151 		u32 da;
152 
153 		if (!*iopgd)
154 			continue;
155 
156 		if (!(*iopgd & IOPGD_TABLE)) {
157 			da = i << IOPGD_SHIFT;
158 
159 			err = dump_ioptable_entry_one(1, da, *iopgd);
160 			if (err)
161 				goto out;
162 			continue;
163 		}
164 
165 		iopte = iopte_offset(iopgd, 0);
166 
167 		for (j = 0; j < PTRS_PER_IOPTE; j++, iopte++) {
168 			if (!*iopte)
169 				continue;
170 
171 			da = (i << IOPGD_SHIFT) + (j << IOPTE_SHIFT);
172 			err = dump_ioptable_entry_one(2, da, *iopgd);
173 			if (err)
174 				goto out;
175 		}
176 	}
177 out:
178 	spin_unlock(&obj->page_table_lock);
179 
180 	return p - buf;
181 }
182 
183 static ssize_t debug_read_pagetable(struct file *file, char __user *userbuf,
184 				    size_t count, loff_t *ppos)
185 {
186 	struct omap_iommu *obj = file->private_data;
187 	char *p, *buf;
188 	size_t bytes;
189 
190 	buf = (char *)__get_free_page(GFP_KERNEL);
191 	if (!buf)
192 		return -ENOMEM;
193 	p = buf;
194 
195 	p += sprintf(p, "L: %8s %8s\n", "da:", "pa:");
196 	p += sprintf(p, "-----------------------------------------\n");
197 
198 	mutex_lock(&iommu_debug_lock);
199 
200 	bytes = PAGE_SIZE - (p - buf);
201 	p += dump_ioptable(obj, p, bytes);
202 
203 	bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
204 
205 	mutex_unlock(&iommu_debug_lock);
206 	free_page((unsigned long)buf);
207 
208 	return bytes;
209 }
210 
211 static ssize_t debug_read_mmap(struct file *file, char __user *userbuf,
212 			       size_t count, loff_t *ppos)
213 {
214 	struct omap_iommu *obj = file->private_data;
215 	char *p, *buf;
216 	struct iovm_struct *tmp;
217 	int uninitialized_var(i);
218 	ssize_t bytes;
219 
220 	buf = (char *)__get_free_page(GFP_KERNEL);
221 	if (!buf)
222 		return -ENOMEM;
223 	p = buf;
224 
225 	p += sprintf(p, "%-3s %-8s %-8s %6s %8s\n",
226 		     "No", "start", "end", "size", "flags");
227 	p += sprintf(p, "-------------------------------------------------\n");
228 
229 	mutex_lock(&iommu_debug_lock);
230 
231 	list_for_each_entry(tmp, &obj->mmap, list) {
232 		size_t len;
233 		const char *str = "%3d %08x-%08x %6x %8x\n";
234 		const int maxcol = 39;
235 
236 		len = tmp->da_end - tmp->da_start;
237 		p += snprintf(p, maxcol, str,
238 			      i, tmp->da_start, tmp->da_end, len, tmp->flags);
239 
240 		if (PAGE_SIZE - (p - buf) < maxcol)
241 			break;
242 		i++;
243 	}
244 
245 	bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
246 
247 	mutex_unlock(&iommu_debug_lock);
248 	free_page((unsigned long)buf);
249 
250 	return bytes;
251 }
252 
253 static ssize_t debug_read_mem(struct file *file, char __user *userbuf,
254 			      size_t count, loff_t *ppos)
255 {
256 	struct omap_iommu *obj = file->private_data;
257 	char *p, *buf;
258 	struct iovm_struct *area;
259 	ssize_t bytes;
260 
261 	count = min_t(ssize_t, count, PAGE_SIZE);
262 
263 	buf = (char *)__get_free_page(GFP_KERNEL);
264 	if (!buf)
265 		return -ENOMEM;
266 	p = buf;
267 
268 	mutex_lock(&iommu_debug_lock);
269 
270 	area = omap_find_iovm_area(obj, (u32)ppos);
271 	if (IS_ERR(area)) {
272 		bytes = -EINVAL;
273 		goto err_out;
274 	}
275 	memcpy(p, area->va, count);
276 	p += count;
277 
278 	bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
279 err_out:
280 	mutex_unlock(&iommu_debug_lock);
281 	free_page((unsigned long)buf);
282 
283 	return bytes;
284 }
285 
286 static ssize_t debug_write_mem(struct file *file, const char __user *userbuf,
287 			       size_t count, loff_t *ppos)
288 {
289 	struct omap_iommu *obj = file->private_data;
290 	struct iovm_struct *area;
291 	char *p, *buf;
292 
293 	count = min_t(size_t, count, PAGE_SIZE);
294 
295 	buf = (char *)__get_free_page(GFP_KERNEL);
296 	if (!buf)
297 		return -ENOMEM;
298 	p = buf;
299 
300 	mutex_lock(&iommu_debug_lock);
301 
302 	if (copy_from_user(p, userbuf, count)) {
303 		count =  -EFAULT;
304 		goto err_out;
305 	}
306 
307 	area = omap_find_iovm_area(obj, (u32)ppos);
308 	if (IS_ERR(area)) {
309 		count = -EINVAL;
310 		goto err_out;
311 	}
312 	memcpy(area->va, p, count);
313 err_out:
314 	mutex_unlock(&iommu_debug_lock);
315 	free_page((unsigned long)buf);
316 
317 	return count;
318 }
319 
320 static int debug_open_generic(struct inode *inode, struct file *file)
321 {
322 	file->private_data = inode->i_private;
323 	return 0;
324 }
325 
326 #define DEBUG_FOPS(name)						\
327 	static const struct file_operations debug_##name##_fops = {	\
328 		.open = debug_open_generic,				\
329 		.read = debug_read_##name,				\
330 		.write = debug_write_##name,				\
331 		.llseek = generic_file_llseek,				\
332 	};
333 
334 #define DEBUG_FOPS_RO(name)						\
335 	static const struct file_operations debug_##name##_fops = {	\
336 		.open = debug_open_generic,				\
337 		.read = debug_read_##name,				\
338 		.llseek = generic_file_llseek,				\
339 	};
340 
341 DEBUG_FOPS_RO(ver);
342 DEBUG_FOPS_RO(regs);
343 DEBUG_FOPS_RO(tlb);
344 DEBUG_FOPS(pagetable);
345 DEBUG_FOPS_RO(mmap);
346 DEBUG_FOPS(mem);
347 
348 #define __DEBUG_ADD_FILE(attr, mode)					\
349 	{								\
350 		struct dentry *dent;					\
351 		dent = debugfs_create_file(#attr, mode, parent,		\
352 					   obj, &debug_##attr##_fops);	\
353 		if (!dent)						\
354 			return -ENOMEM;					\
355 	}
356 
357 #define DEBUG_ADD_FILE(name) __DEBUG_ADD_FILE(name, 600)
358 #define DEBUG_ADD_FILE_RO(name) __DEBUG_ADD_FILE(name, 400)
359 
360 static int iommu_debug_register(struct device *dev, void *data)
361 {
362 	struct platform_device *pdev = to_platform_device(dev);
363 	struct omap_iommu *obj = platform_get_drvdata(pdev);
364 	struct dentry *d, *parent;
365 
366 	if (!obj || !obj->dev)
367 		return -EINVAL;
368 
369 	d = debugfs_create_dir(obj->name, iommu_debug_root);
370 	if (!d)
371 		return -ENOMEM;
372 	parent = d;
373 
374 	d = debugfs_create_u8("nr_tlb_entries", 400, parent,
375 			      (u8 *)&obj->nr_tlb_entries);
376 	if (!d)
377 		return -ENOMEM;
378 
379 	DEBUG_ADD_FILE_RO(ver);
380 	DEBUG_ADD_FILE_RO(regs);
381 	DEBUG_ADD_FILE_RO(tlb);
382 	DEBUG_ADD_FILE(pagetable);
383 	DEBUG_ADD_FILE_RO(mmap);
384 	DEBUG_ADD_FILE(mem);
385 
386 	return 0;
387 }
388 
389 static int __init iommu_debug_init(void)
390 {
391 	struct dentry *d;
392 	int err;
393 
394 	d = debugfs_create_dir("iommu", NULL);
395 	if (!d)
396 		return -ENOMEM;
397 	iommu_debug_root = d;
398 
399 	err = omap_foreach_iommu_device(d, iommu_debug_register);
400 	if (err)
401 		goto err_out;
402 	return 0;
403 
404 err_out:
405 	debugfs_remove_recursive(iommu_debug_root);
406 	return err;
407 }
408 module_init(iommu_debug_init)
409 
410 static void __exit iommu_debugfs_exit(void)
411 {
412 	debugfs_remove_recursive(iommu_debug_root);
413 }
414 module_exit(iommu_debugfs_exit)
415 
416 MODULE_DESCRIPTION("omap iommu: debugfs interface");
417 MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>");
418 MODULE_LICENSE("GPL v2");
419