Logo Search packages:      
Sourcecode: linux version File versions  Download package

iommu-debug.c

/*
 * omap iommu: debugfs interface
 *
 * Copyright (C) 2008-2009 Nokia Corporation
 *
 * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/debugfs.h>

#include <mach/iommu.h>
#include <mach/iovmm.h>

#include "iopgtable.h"

#define MAXCOLUMN 100 /* for short messages */

static DEFINE_MUTEX(iommu_debug_lock);

static struct dentry *iommu_debug_root;

static ssize_t debug_read_ver(struct file *file, char __user *userbuf,
                        size_t count, loff_t *ppos)
{
      u32 ver = iommu_arch_version();
      char buf[MAXCOLUMN], *p = buf;

      p += sprintf(p, "H/W version: %d.%d\n", (ver >> 4) & 0xf , ver & 0xf);

      return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
}

static ssize_t debug_read_regs(struct file *file, char __user *userbuf,
                         size_t count, loff_t *ppos)
{
      struct iommu *obj = file->private_data;
      char *p, *buf;
      ssize_t bytes;

      buf = kmalloc(count, GFP_KERNEL);
      if (!buf)
            return -ENOMEM;
      p = buf;

      mutex_lock(&iommu_debug_lock);

      bytes = iommu_dump_ctx(obj, p, count);
      bytes = simple_read_from_buffer(userbuf, count, ppos, buf, bytes);

      mutex_unlock(&iommu_debug_lock);
      kfree(buf);

      return bytes;
}

static ssize_t debug_read_tlb(struct file *file, char __user *userbuf,
                        size_t count, loff_t *ppos)
{
      struct iommu *obj = file->private_data;
      char *p, *buf;
      ssize_t bytes, rest;

      buf = kmalloc(count, GFP_KERNEL);
      if (!buf)
            return -ENOMEM;
      p = buf;

      mutex_lock(&iommu_debug_lock);

      p += sprintf(p, "%8s %8s\n", "cam:", "ram:");
      p += sprintf(p, "-----------------------------------------\n");
      rest = count - (p - buf);
      p += dump_tlb_entries(obj, p, rest);

      bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);

      mutex_unlock(&iommu_debug_lock);
      kfree(buf);

      return bytes;
}

static ssize_t debug_write_pagetable(struct file *file,
                 const char __user *userbuf, size_t count, loff_t *ppos)
{
      struct iotlb_entry e;
      struct cr_regs cr;
      int err;
      struct iommu *obj = file->private_data;
      char buf[MAXCOLUMN], *p = buf;

      count = min(count, sizeof(buf));

      mutex_lock(&iommu_debug_lock);
      if (copy_from_user(p, userbuf, count)) {
            mutex_unlock(&iommu_debug_lock);
            return -EFAULT;
      }

      sscanf(p, "%x %x", &cr.cam, &cr.ram);
      if (!cr.cam || !cr.ram) {
            mutex_unlock(&iommu_debug_lock);
            return -EINVAL;
      }

      iotlb_cr_to_e(&cr, &e);
      err = iopgtable_store_entry(obj, &e);
      if (err)
            dev_err(obj->dev, "%s: fail to store cr\n", __func__);

      mutex_unlock(&iommu_debug_lock);
      return count;
}

#define dump_ioptable_entry_one(lv, da, val)                \
      ({                                        \
            int __err = 0;                            \
            ssize_t bytes;                            \
            const int maxcol = 22;                    \
            const char *str = "%d: %08x %08x\n";            \
            bytes = snprintf(p, maxcol, str, lv, da, val);  \
            p += bytes;                         \
            len -= bytes;                             \
            if (len < maxcol)                   \
                  __err = -ENOMEM;              \
            __err;                                    \
      })

static ssize_t dump_ioptable(struct iommu *obj, char *buf, ssize_t len)
{
      int i;
      u32 *iopgd;
      char *p = buf;

      spin_lock(&obj->page_table_lock);

      iopgd = iopgd_offset(obj, 0);
      for (i = 0; i < PTRS_PER_IOPGD; i++, iopgd++) {
            int j, err;
            u32 *iopte;
            u32 da;

            if (!*iopgd)
                  continue;

            if (!(*iopgd & IOPGD_TABLE)) {
                  da = i << IOPGD_SHIFT;

                  err = dump_ioptable_entry_one(1, da, *iopgd);
                  if (err)
                        goto out;
                  continue;
            }

            iopte = iopte_offset(iopgd, 0);

            for (j = 0; j < PTRS_PER_IOPTE; j++, iopte++) {
                  if (!*iopte)
                        continue;

                  da = (i << IOPGD_SHIFT) + (j << IOPTE_SHIFT);
                  err = dump_ioptable_entry_one(2, da, *iopgd);
                  if (err)
                        goto out;
            }
      }
out:
      spin_unlock(&obj->page_table_lock);

      return p - buf;
}

static ssize_t debug_read_pagetable(struct file *file, char __user *userbuf,
                            size_t count, loff_t *ppos)
{
      struct iommu *obj = file->private_data;
      char *p, *buf;
      size_t bytes;

      buf = (char *)__get_free_page(GFP_KERNEL);
      if (!buf)
            return -ENOMEM;
      p = buf;

      p += sprintf(p, "L: %8s %8s\n", "da:", "pa:");
      p += sprintf(p, "-----------------------------------------\n");

      mutex_lock(&iommu_debug_lock);

      bytes = PAGE_SIZE - (p - buf);
      p += dump_ioptable(obj, p, bytes);

      bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);

      mutex_unlock(&iommu_debug_lock);
      free_page((unsigned long)buf);

      return bytes;
}

static ssize_t debug_read_mmap(struct file *file, char __user *userbuf,
                         size_t count, loff_t *ppos)
{
      struct iommu *obj = file->private_data;
      char *p, *buf;
      struct iovm_struct *tmp;
      int uninitialized_var(i);
      ssize_t bytes;

      buf = (char *)__get_free_page(GFP_KERNEL);
      if (!buf)
            return -ENOMEM;
      p = buf;

      p += sprintf(p, "%-3s %-8s %-8s %6s %8s\n",
                 "No", "start", "end", "size", "flags");
      p += sprintf(p, "-------------------------------------------------\n");

      mutex_lock(&iommu_debug_lock);

      list_for_each_entry(tmp, &obj->mmap, list) {
            size_t len;
            const char *str = "%3d %08x-%08x %6x %8x\n";
            const int maxcol = 39;

            len = tmp->da_end - tmp->da_start;
            p += snprintf(p, maxcol, str,
                        i, tmp->da_start, tmp->da_end, len, tmp->flags);

            if (PAGE_SIZE - (p - buf) < maxcol)
                  break;
            i++;
      }

      bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);

      mutex_unlock(&iommu_debug_lock);
      free_page((unsigned long)buf);

      return bytes;
}

static ssize_t debug_read_mem(struct file *file, char __user *userbuf,
                        size_t count, loff_t *ppos)
{
      struct iommu *obj = file->private_data;
      char *p, *buf;
      struct iovm_struct *area;
      ssize_t bytes;

      count = min_t(ssize_t, count, PAGE_SIZE);

      buf = (char *)__get_free_page(GFP_KERNEL);
      if (!buf)
            return -ENOMEM;
      p = buf;

      mutex_lock(&iommu_debug_lock);

      area = find_iovm_area(obj, (u32)ppos);
      if (IS_ERR(area)) {
            bytes = -EINVAL;
            goto err_out;
      }
      memcpy(p, area->va, count);
      p += count;

      bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
err_out:
      mutex_unlock(&iommu_debug_lock);
      free_page((unsigned long)buf);

      return bytes;
}

static ssize_t debug_write_mem(struct file *file, const char __user *userbuf,
                         size_t count, loff_t *ppos)
{
      struct iommu *obj = file->private_data;
      struct iovm_struct *area;
      char *p, *buf;

      count = min_t(size_t, count, PAGE_SIZE);

      buf = (char *)__get_free_page(GFP_KERNEL);
      if (!buf)
            return -ENOMEM;
      p = buf;

      mutex_lock(&iommu_debug_lock);

      if (copy_from_user(p, userbuf, count)) {
            count =  -EFAULT;
            goto err_out;
      }

      area = find_iovm_area(obj, (u32)ppos);
      if (IS_ERR(area)) {
            count = -EINVAL;
            goto err_out;
      }
      memcpy(area->va, p, count);
err_out:
      mutex_unlock(&iommu_debug_lock);
      free_page((unsigned long)buf);

      return count;
}

static int debug_open_generic(struct inode *inode, struct file *file)
{
      file->private_data = inode->i_private;
      return 0;
}

#define DEBUG_FOPS(name)                                    \
      static const struct file_operations debug_##name##_fops = { \
            .open = debug_open_generic,                     \
            .read = debug_read_##name,                      \
            .write = debug_write_##name,                    \
      };

#define DEBUG_FOPS_RO(name)                                 \
      static const struct file_operations debug_##name##_fops = { \
            .open = debug_open_generic,                     \
            .read = debug_read_##name,                      \
      };

DEBUG_FOPS_RO(ver);
DEBUG_FOPS_RO(regs);
DEBUG_FOPS_RO(tlb);
DEBUG_FOPS(pagetable);
DEBUG_FOPS_RO(mmap);
DEBUG_FOPS(mem);

#define __DEBUG_ADD_FILE(attr, mode)                              \
      {                                               \
            struct dentry *dent;                            \
            dent = debugfs_create_file(#attr, mode, parent,       \
                                 obj, &debug_##attr##_fops);      \
            if (!dent)                                \
                  return -ENOMEM;                           \
      }

#define DEBUG_ADD_FILE(name) __DEBUG_ADD_FILE(name, 600)
#define DEBUG_ADD_FILE_RO(name) __DEBUG_ADD_FILE(name, 400)

static int iommu_debug_register(struct device *dev, void *data)
{
      struct platform_device *pdev = to_platform_device(dev);
      struct iommu *obj = platform_get_drvdata(pdev);
      struct dentry *d, *parent;

      if (!obj || !obj->dev)
            return -EINVAL;

      d = debugfs_create_dir(obj->name, iommu_debug_root);
      if (!d)
            return -ENOMEM;
      parent = d;

      d = debugfs_create_u8("nr_tlb_entries", 400, parent,
                        (u8 *)&obj->nr_tlb_entries);
      if (!d)
            return -ENOMEM;

      DEBUG_ADD_FILE_RO(ver);
      DEBUG_ADD_FILE_RO(regs);
      DEBUG_ADD_FILE_RO(tlb);
      DEBUG_ADD_FILE(pagetable);
      DEBUG_ADD_FILE_RO(mmap);
      DEBUG_ADD_FILE(mem);

      return 0;
}

static int __init iommu_debug_init(void)
{
      struct dentry *d;
      int err;

      d = debugfs_create_dir("iommu", NULL);
      if (!d)
            return -ENOMEM;
      iommu_debug_root = d;

      err = foreach_iommu_device(d, iommu_debug_register);
      if (err)
            goto err_out;
      return 0;

err_out:
      debugfs_remove_recursive(iommu_debug_root);
      return err;
}
module_init(iommu_debug_init)

static void __exit iommu_debugfs_exit(void)
{
      debugfs_remove_recursive(iommu_debug_root);
}
module_exit(iommu_debugfs_exit)

MODULE_DESCRIPTION("omap iommu: debugfs interface");
MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>");
MODULE_LICENSE("GPL v2");

Generated by  Doxygen 1.6.0   Back to index