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