Loading drivers/iommu/iommu-debug.c +54 −351 Original line number Diff line number Diff line /* * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -27,6 +27,7 @@ #include <linux/dma-mapping.h> #include <asm/cacheflush.h> #include <asm/dma-iommu.h> #include "iommu-debug.h" #if defined(CONFIG_IOMMU_DEBUG_TRACKING) || defined(CONFIG_IOMMU_TESTS) Loading Loading @@ -83,380 +84,58 @@ static const char *iommu_debug_attr_to_string(enum iommu_attr attr) static DEFINE_MUTEX(iommu_debug_attachments_lock); static LIST_HEAD(iommu_debug_attachments); static struct dentry *debugfs_attachments_dir; /* * Each group may have more than one domain; but each domain may * only have one group. * Used by debug tools to display the name of the device(s) associated * with a particular domain. */ struct iommu_debug_attachment { struct iommu_domain *domain; struct device *dev; struct dentry *dentry; struct iommu_group *group; struct list_head list; unsigned long reg_offset; }; static int iommu_debug_attachment_info_show(struct seq_file *s, void *ignored) { struct iommu_debug_attachment *attach = s->private; phys_addr_t pt_phys; int coherent_htw_disable; int secure_vmid; seq_printf(s, "Domain: 0x%p\n", attach->domain); if (iommu_domain_get_attr(attach->domain, DOMAIN_ATTR_PT_BASE_ADDR, &pt_phys)) { seq_puts(s, "PT_BASE_ADDR: (Unknown)\n"); } else { void *pt_virt = phys_to_virt(pt_phys); seq_printf(s, "PT_BASE_ADDR: virt=0x%p phys=%pa\n", pt_virt, &pt_phys); } seq_puts(s, "COHERENT_HTW_DISABLE: "); if (iommu_domain_get_attr(attach->domain, DOMAIN_ATTR_COHERENT_HTW_DISABLE, &coherent_htw_disable)) seq_puts(s, "(Unknown)\n"); else seq_printf(s, "%d\n", coherent_htw_disable); seq_puts(s, "SECURE_VMID: "); if (iommu_domain_get_attr(attach->domain, DOMAIN_ATTR_SECURE_VMID, &secure_vmid)) seq_puts(s, "(Unknown)\n"); else seq_printf(s, "%s (0x%x)\n", msm_secure_vmid_to_string(secure_vmid), secure_vmid); return 0; } static int iommu_debug_attachment_info_open(struct inode *inode, struct file *file) { return single_open(file, iommu_debug_attachment_info_show, inode->i_private); } static const struct file_operations iommu_debug_attachment_info_fops = { .open = iommu_debug_attachment_info_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static ssize_t iommu_debug_attachment_trigger_fault_write( struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_attachment *attach = file->private_data; unsigned long flags; if (kstrtoul_from_user(ubuf, count, 0, &flags)) { pr_err("Invalid flags format\n"); return -EFAULT; } iommu_trigger_fault(attach->domain, flags); return count; } static const struct file_operations iommu_debug_attachment_trigger_fault_fops = { .open = simple_open, .write = iommu_debug_attachment_trigger_fault_write, }; static ssize_t iommu_debug_attachment_reg_offset_write( struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_attachment *attach = file->private_data; unsigned long reg_offset; if (kstrtoul_from_user(ubuf, count, 0, ®_offset)) { pr_err("Invalid reg_offset format\n"); return -EFAULT; } attach->reg_offset = reg_offset; return count; } static const struct file_operations iommu_debug_attachment_reg_offset_fops = { .open = simple_open, .write = iommu_debug_attachment_reg_offset_write, }; static ssize_t iommu_debug_attachment_reg_read_read( struct file *file, char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_attachment *attach = file->private_data; unsigned long val; char *val_str; ssize_t val_str_len; if (*offset) return 0; val = iommu_reg_read(attach->domain, attach->reg_offset); val_str = kasprintf(GFP_KERNEL, "0x%lx\n", val); if (!val_str) return -ENOMEM; val_str_len = strlen(val_str); if (copy_to_user(ubuf, val_str, val_str_len)) { pr_err("copy_to_user failed\n"); val_str_len = -EFAULT; goto out; } *offset = 1; /* non-zero means we're done */ out: kfree(val_str); return val_str_len; } static const struct file_operations iommu_debug_attachment_reg_read_fops = { .open = simple_open, .read = iommu_debug_attachment_reg_read_read, }; static ssize_t iommu_debug_attachment_reg_write_write( struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_attachment *attach = file->private_data; unsigned long val; if (kstrtoul_from_user(ubuf, count, 0, &val)) { pr_err("Invalid val format\n"); return -EFAULT; } iommu_reg_write(attach->domain, attach->reg_offset, val); return count; } static const struct file_operations iommu_debug_attachment_reg_write_fops = { .open = simple_open, .write = iommu_debug_attachment_reg_write_write, }; /* should be called with iommu_debug_attachments_lock locked */ static int iommu_debug_attach_add_debugfs( struct iommu_debug_attachment *attach) { const char *attach_name; struct device *dev = attach->dev; struct iommu_domain *domain = attach->domain; int is_dynamic; if (iommu_domain_get_attr(domain, DOMAIN_ATTR_DYNAMIC, &is_dynamic)) is_dynamic = 0; if (is_dynamic) { uuid_le uuid; uuid_le_gen(&uuid); attach_name = kasprintf(GFP_KERNEL, "%s-%pUl", dev_name(dev), uuid.b); if (!attach_name) return -ENOMEM; } else { attach_name = dev_name(dev); } attach->dentry = debugfs_create_dir(attach_name, debugfs_attachments_dir); if (!attach->dentry) { pr_err("Couldn't create iommu/attachments/%s debugfs directory for domain 0x%p\n", attach_name, domain); if (is_dynamic) kfree(attach_name); return -EIO; } if (is_dynamic) kfree(attach_name); if (!debugfs_create_file( "info", S_IRUSR, attach->dentry, attach, &iommu_debug_attachment_info_fops)) { pr_err("Couldn't create iommu/attachments/%s/info debugfs file for domain 0x%p\n", dev_name(dev), domain); goto err_rmdir; } if (!debugfs_create_file( "trigger_fault", S_IRUSR, attach->dentry, attach, &iommu_debug_attachment_trigger_fault_fops)) { pr_err("Couldn't create iommu/attachments/%s/trigger_fault debugfs file for domain 0x%p\n", dev_name(dev), domain); goto err_rmdir; } if (!debugfs_create_file( "reg_offset", S_IRUSR, attach->dentry, attach, &iommu_debug_attachment_reg_offset_fops)) { pr_err("Couldn't create iommu/attachments/%s/reg_offset debugfs file for domain 0x%p\n", dev_name(dev), domain); goto err_rmdir; } if (!debugfs_create_file( "reg_read", S_IRUSR, attach->dentry, attach, &iommu_debug_attachment_reg_read_fops)) { pr_err("Couldn't create iommu/attachments/%s/reg_read debugfs file for domain 0x%p\n", dev_name(dev), domain); goto err_rmdir; } if (!debugfs_create_file( "reg_write", S_IRUSR, attach->dentry, attach, &iommu_debug_attachment_reg_write_fops)) { pr_err("Couldn't create iommu/attachments/%s/reg_write debugfs file for domain 0x%p\n", dev_name(dev), domain); goto err_rmdir; } return 0; err_rmdir: debugfs_remove_recursive(attach->dentry); return -EIO; } void iommu_debug_domain_add(struct iommu_domain *domain) void iommu_debug_attach_device(struct iommu_domain *domain, struct device *dev) { struct iommu_debug_attachment *attach; struct iommu_group *group; mutex_lock(&iommu_debug_attachments_lock); group = iommu_group_get(dev); if (!group) return; attach = kmalloc(sizeof(*attach), GFP_KERNEL); attach = kzalloc(sizeof(*attach), GFP_KERNEL); if (!attach) goto out_unlock; return; attach->domain = domain; attach->dev = NULL; list_add(&attach->list, &iommu_debug_attachments); attach->group = group; INIT_LIST_HEAD(&attach->list); out_unlock: mutex_lock(&iommu_debug_attachments_lock); list_add(&attach->list, &iommu_debug_attachments); mutex_unlock(&iommu_debug_attachments_lock); } void iommu_debug_domain_remove(struct iommu_domain *domain) { struct iommu_debug_attachment *it; struct iommu_debug_attachment *it, *tmp; mutex_lock(&iommu_debug_attachments_lock); list_for_each_entry(it, &iommu_debug_attachments, list) if (it->domain == domain && it->dev == NULL) break; if (&it->list == &iommu_debug_attachments) { WARN(1, "Couldn't find debug attachment for domain=0x%p", domain); } else { list_for_each_entry_safe(it, tmp, &iommu_debug_attachments, list) { if (it->domain != domain) continue; list_del(&it->list); iommu_group_put(it->group); kfree(it); } mutex_unlock(&iommu_debug_attachments_lock); } void iommu_debug_attach_device(struct iommu_domain *domain, struct device *dev) { struct iommu_debug_attachment *attach; mutex_lock(&iommu_debug_attachments_lock); list_for_each_entry(attach, &iommu_debug_attachments, list) if (attach->domain == domain && attach->dev == NULL) break; if (&attach->list == &iommu_debug_attachments) { WARN(1, "Couldn't find debug attachment for domain=0x%p dev=%s", domain, dev_name(dev)); } else { attach->dev = dev; /* * we might not init until after other drivers start calling * iommu_attach_device. Only set up the debugfs nodes if we've * already init'd to avoid polluting the top-level debugfs * directory (by calling debugfs_create_dir with a NULL * parent). These will be flushed out later once we init. */ if (debugfs_attachments_dir) iommu_debug_attach_add_debugfs(attach); } mutex_unlock(&iommu_debug_attachments_lock); } void iommu_debug_detach_device(struct iommu_domain *domain, struct device *dev) { struct iommu_debug_attachment *it; mutex_lock(&iommu_debug_attachments_lock); list_for_each_entry(it, &iommu_debug_attachments, list) if (it->domain == domain && it->dev == dev) break; if (&it->list == &iommu_debug_attachments) { WARN(1, "Couldn't find debug attachment for domain=0x%p dev=%s", domain, dev_name(dev)); } else { /* * Just remove debugfs entry and mark dev as NULL on * iommu_detach call. We would remove the actual * attachment entry from the list only on domain_free call. * This is to ensure we keep track of unattached domains too. */ debugfs_remove_recursive(it->dentry); it->dev = NULL; } mutex_unlock(&iommu_debug_attachments_lock); } static int iommu_debug_init_tracking(void) { int ret = 0; struct iommu_debug_attachment *attach; mutex_lock(&iommu_debug_attachments_lock); debugfs_attachments_dir = debugfs_create_dir("attachments", iommu_debugfs_top); if (!debugfs_attachments_dir) { pr_err("Couldn't create iommu/attachments debugfs directory\n"); ret = -ENODEV; goto out_unlock; } /* set up debugfs entries for attachments made during early boot */ list_for_each_entry(attach, &iommu_debug_attachments, list) if (attach->dev) iommu_debug_attach_add_debugfs(attach); out_unlock: mutex_unlock(&iommu_debug_attachments_lock); return ret; } static void iommu_debug_destroy_tracking(void) { debugfs_remove_recursive(debugfs_attachments_dir); } #else static inline int iommu_debug_init_tracking(void) { return 0; } static inline void iommu_debug_destroy_tracking(void) { } #endif #ifdef CONFIG_IOMMU_TESTS Loading Loading @@ -486,6 +165,8 @@ struct iommu_debug_device { u64 phys; size_t len; struct list_head list; struct mutex clk_lock; unsigned int clk_count; }; static int iommu_debug_build_phoney_sg_table(struct device *dev, Loading Loading @@ -1524,6 +1205,7 @@ static int iommu_debug_attach_do_attach(struct iommu_debug_device *ddev, goto out_domain_free; } val = VMID_CP_CAMERA; if (is_secure && iommu_domain_set_attr(ddev->domain, DOMAIN_ATTR_SECURE_VMID, &val)) { Loading Loading @@ -1669,6 +1351,10 @@ static ssize_t iommu_debug_atos_read(struct file *file, char __user *ubuf, ssize_t retval; size_t buflen; if (kptr_restrict != 0) { pr_err("kptr_restrict needs to be disabled.\n"); return -EPERM; } if (!ddev->domain) { pr_err("No domain. Did you already attach?\n"); return -EINVAL; Loading Loading @@ -1876,20 +1562,34 @@ static ssize_t iommu_debug_config_clocks_write(struct file *file, return -EFAULT; } mutex_lock(&ddev->clk_lock); switch (buf) { case '0': if (ddev->clk_count == 0) { dev_err(dev, "Config clocks already disabled\n"); break; } if (--ddev->clk_count > 0) break; dev_err(dev, "Disabling config clocks\n"); iommu_disable_config_clocks(ddev->domain); break; case '1': if (ddev->clk_count++ > 0) break; dev_err(dev, "Enabling config clocks\n"); if (iommu_enable_config_clocks(ddev->domain)) dev_err(dev, "Failed!\n"); break; default: dev_err(dev, "Invalid value. Should be 0 or 1.\n"); mutex_unlock(&ddev->clk_lock); return -EINVAL; } mutex_unlock(&ddev->clk_lock); return count; } Loading @@ -1915,6 +1615,7 @@ static int snarf_iommu_devices(struct device *dev, const char *name) ddev = kzalloc(sizeof(*ddev), GFP_KERNEL); if (!ddev) return -ENODEV; mutex_init(&ddev->clk_lock); ddev->dev = dev; dir = debugfs_create_dir(name, debugfs_tests_dir); if (!dir) { Loading Loading @@ -2026,6 +1727,9 @@ err: static int pass_iommu_devices(struct device *dev, void *ignored) { if (!of_device_is_compatible(dev->of_node, "iommu-debug-test")) return 0; if (!of_find_property(dev->of_node, "iommus", NULL)) return 0; Loading @@ -2039,6 +1743,9 @@ static int iommu_debug_populate_devices(void) const char *cb_name; for_each_compatible_node(np, NULL, "qcom,msm-smmu-v2-ctx") { if (!of_device_is_compatible(np, "iommu-debug-test")) continue; ret = of_property_read_string(np, "label", &cb_name); if (ret) return ret; Loading Loading @@ -2075,9 +1782,6 @@ static inline void iommu_debug_destroy_tests(void) { } static int iommu_debug_init(void) { if (iommu_debug_init_tracking()) return -ENODEV; if (iommu_debug_init_tests()) return -ENODEV; Loading @@ -2086,7 +1790,6 @@ static int iommu_debug_init(void) static void iommu_debug_exit(void) { iommu_debug_destroy_tracking(); iommu_debug_destroy_tests(); } Loading drivers/iommu/iommu-debug.h +12 −11 Original line number Diff line number Diff line /* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef IOMMU_DEBUG_H #define IOMMU_DEBUG_H #ifdef CONFIG_IOMMU_DEBUG_TRACKING void iommu_debug_attach_device(struct iommu_domain *domain, struct device *dev); void iommu_debug_detach_device(struct iommu_domain *domain, struct device *dev); void iommu_debug_domain_add(struct iommu_domain *domain); void iommu_debug_domain_remove(struct iommu_domain *domain); #else /* !CONFIG_IOMMU_DEBUG_TRACKING */ Loading @@ -15,15 +25,6 @@ static inline void iommu_debug_attach_device(struct iommu_domain *domain, { } static inline void iommu_debug_detach_device(struct iommu_domain *domain, struct device *dev) { } static inline void iommu_debug_domain_add(struct iommu_domain *domain) { } static inline void iommu_debug_domain_remove(struct iommu_domain *domain) { } Loading drivers/iommu/iommu.c +0 −3 Original line number Diff line number Diff line Loading @@ -977,8 +977,6 @@ struct iommu_domain *iommu_domain_alloc(struct bus_type *bus) if (ret) goto out_free; iommu_debug_domain_add(domain); return domain; out_free: Loading Loading @@ -1019,7 +1017,6 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev) if (unlikely(domain->ops->detach_dev == NULL)) return; iommu_debug_detach_device(domain, dev); domain->ops->detach_dev(domain, dev); trace_detach_device_from_domain(dev); } Loading Loading
drivers/iommu/iommu-debug.c +54 −351 Original line number Diff line number Diff line /* * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -27,6 +27,7 @@ #include <linux/dma-mapping.h> #include <asm/cacheflush.h> #include <asm/dma-iommu.h> #include "iommu-debug.h" #if defined(CONFIG_IOMMU_DEBUG_TRACKING) || defined(CONFIG_IOMMU_TESTS) Loading Loading @@ -83,380 +84,58 @@ static const char *iommu_debug_attr_to_string(enum iommu_attr attr) static DEFINE_MUTEX(iommu_debug_attachments_lock); static LIST_HEAD(iommu_debug_attachments); static struct dentry *debugfs_attachments_dir; /* * Each group may have more than one domain; but each domain may * only have one group. * Used by debug tools to display the name of the device(s) associated * with a particular domain. */ struct iommu_debug_attachment { struct iommu_domain *domain; struct device *dev; struct dentry *dentry; struct iommu_group *group; struct list_head list; unsigned long reg_offset; }; static int iommu_debug_attachment_info_show(struct seq_file *s, void *ignored) { struct iommu_debug_attachment *attach = s->private; phys_addr_t pt_phys; int coherent_htw_disable; int secure_vmid; seq_printf(s, "Domain: 0x%p\n", attach->domain); if (iommu_domain_get_attr(attach->domain, DOMAIN_ATTR_PT_BASE_ADDR, &pt_phys)) { seq_puts(s, "PT_BASE_ADDR: (Unknown)\n"); } else { void *pt_virt = phys_to_virt(pt_phys); seq_printf(s, "PT_BASE_ADDR: virt=0x%p phys=%pa\n", pt_virt, &pt_phys); } seq_puts(s, "COHERENT_HTW_DISABLE: "); if (iommu_domain_get_attr(attach->domain, DOMAIN_ATTR_COHERENT_HTW_DISABLE, &coherent_htw_disable)) seq_puts(s, "(Unknown)\n"); else seq_printf(s, "%d\n", coherent_htw_disable); seq_puts(s, "SECURE_VMID: "); if (iommu_domain_get_attr(attach->domain, DOMAIN_ATTR_SECURE_VMID, &secure_vmid)) seq_puts(s, "(Unknown)\n"); else seq_printf(s, "%s (0x%x)\n", msm_secure_vmid_to_string(secure_vmid), secure_vmid); return 0; } static int iommu_debug_attachment_info_open(struct inode *inode, struct file *file) { return single_open(file, iommu_debug_attachment_info_show, inode->i_private); } static const struct file_operations iommu_debug_attachment_info_fops = { .open = iommu_debug_attachment_info_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static ssize_t iommu_debug_attachment_trigger_fault_write( struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_attachment *attach = file->private_data; unsigned long flags; if (kstrtoul_from_user(ubuf, count, 0, &flags)) { pr_err("Invalid flags format\n"); return -EFAULT; } iommu_trigger_fault(attach->domain, flags); return count; } static const struct file_operations iommu_debug_attachment_trigger_fault_fops = { .open = simple_open, .write = iommu_debug_attachment_trigger_fault_write, }; static ssize_t iommu_debug_attachment_reg_offset_write( struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_attachment *attach = file->private_data; unsigned long reg_offset; if (kstrtoul_from_user(ubuf, count, 0, ®_offset)) { pr_err("Invalid reg_offset format\n"); return -EFAULT; } attach->reg_offset = reg_offset; return count; } static const struct file_operations iommu_debug_attachment_reg_offset_fops = { .open = simple_open, .write = iommu_debug_attachment_reg_offset_write, }; static ssize_t iommu_debug_attachment_reg_read_read( struct file *file, char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_attachment *attach = file->private_data; unsigned long val; char *val_str; ssize_t val_str_len; if (*offset) return 0; val = iommu_reg_read(attach->domain, attach->reg_offset); val_str = kasprintf(GFP_KERNEL, "0x%lx\n", val); if (!val_str) return -ENOMEM; val_str_len = strlen(val_str); if (copy_to_user(ubuf, val_str, val_str_len)) { pr_err("copy_to_user failed\n"); val_str_len = -EFAULT; goto out; } *offset = 1; /* non-zero means we're done */ out: kfree(val_str); return val_str_len; } static const struct file_operations iommu_debug_attachment_reg_read_fops = { .open = simple_open, .read = iommu_debug_attachment_reg_read_read, }; static ssize_t iommu_debug_attachment_reg_write_write( struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_attachment *attach = file->private_data; unsigned long val; if (kstrtoul_from_user(ubuf, count, 0, &val)) { pr_err("Invalid val format\n"); return -EFAULT; } iommu_reg_write(attach->domain, attach->reg_offset, val); return count; } static const struct file_operations iommu_debug_attachment_reg_write_fops = { .open = simple_open, .write = iommu_debug_attachment_reg_write_write, }; /* should be called with iommu_debug_attachments_lock locked */ static int iommu_debug_attach_add_debugfs( struct iommu_debug_attachment *attach) { const char *attach_name; struct device *dev = attach->dev; struct iommu_domain *domain = attach->domain; int is_dynamic; if (iommu_domain_get_attr(domain, DOMAIN_ATTR_DYNAMIC, &is_dynamic)) is_dynamic = 0; if (is_dynamic) { uuid_le uuid; uuid_le_gen(&uuid); attach_name = kasprintf(GFP_KERNEL, "%s-%pUl", dev_name(dev), uuid.b); if (!attach_name) return -ENOMEM; } else { attach_name = dev_name(dev); } attach->dentry = debugfs_create_dir(attach_name, debugfs_attachments_dir); if (!attach->dentry) { pr_err("Couldn't create iommu/attachments/%s debugfs directory for domain 0x%p\n", attach_name, domain); if (is_dynamic) kfree(attach_name); return -EIO; } if (is_dynamic) kfree(attach_name); if (!debugfs_create_file( "info", S_IRUSR, attach->dentry, attach, &iommu_debug_attachment_info_fops)) { pr_err("Couldn't create iommu/attachments/%s/info debugfs file for domain 0x%p\n", dev_name(dev), domain); goto err_rmdir; } if (!debugfs_create_file( "trigger_fault", S_IRUSR, attach->dentry, attach, &iommu_debug_attachment_trigger_fault_fops)) { pr_err("Couldn't create iommu/attachments/%s/trigger_fault debugfs file for domain 0x%p\n", dev_name(dev), domain); goto err_rmdir; } if (!debugfs_create_file( "reg_offset", S_IRUSR, attach->dentry, attach, &iommu_debug_attachment_reg_offset_fops)) { pr_err("Couldn't create iommu/attachments/%s/reg_offset debugfs file for domain 0x%p\n", dev_name(dev), domain); goto err_rmdir; } if (!debugfs_create_file( "reg_read", S_IRUSR, attach->dentry, attach, &iommu_debug_attachment_reg_read_fops)) { pr_err("Couldn't create iommu/attachments/%s/reg_read debugfs file for domain 0x%p\n", dev_name(dev), domain); goto err_rmdir; } if (!debugfs_create_file( "reg_write", S_IRUSR, attach->dentry, attach, &iommu_debug_attachment_reg_write_fops)) { pr_err("Couldn't create iommu/attachments/%s/reg_write debugfs file for domain 0x%p\n", dev_name(dev), domain); goto err_rmdir; } return 0; err_rmdir: debugfs_remove_recursive(attach->dentry); return -EIO; } void iommu_debug_domain_add(struct iommu_domain *domain) void iommu_debug_attach_device(struct iommu_domain *domain, struct device *dev) { struct iommu_debug_attachment *attach; struct iommu_group *group; mutex_lock(&iommu_debug_attachments_lock); group = iommu_group_get(dev); if (!group) return; attach = kmalloc(sizeof(*attach), GFP_KERNEL); attach = kzalloc(sizeof(*attach), GFP_KERNEL); if (!attach) goto out_unlock; return; attach->domain = domain; attach->dev = NULL; list_add(&attach->list, &iommu_debug_attachments); attach->group = group; INIT_LIST_HEAD(&attach->list); out_unlock: mutex_lock(&iommu_debug_attachments_lock); list_add(&attach->list, &iommu_debug_attachments); mutex_unlock(&iommu_debug_attachments_lock); } void iommu_debug_domain_remove(struct iommu_domain *domain) { struct iommu_debug_attachment *it; struct iommu_debug_attachment *it, *tmp; mutex_lock(&iommu_debug_attachments_lock); list_for_each_entry(it, &iommu_debug_attachments, list) if (it->domain == domain && it->dev == NULL) break; if (&it->list == &iommu_debug_attachments) { WARN(1, "Couldn't find debug attachment for domain=0x%p", domain); } else { list_for_each_entry_safe(it, tmp, &iommu_debug_attachments, list) { if (it->domain != domain) continue; list_del(&it->list); iommu_group_put(it->group); kfree(it); } mutex_unlock(&iommu_debug_attachments_lock); } void iommu_debug_attach_device(struct iommu_domain *domain, struct device *dev) { struct iommu_debug_attachment *attach; mutex_lock(&iommu_debug_attachments_lock); list_for_each_entry(attach, &iommu_debug_attachments, list) if (attach->domain == domain && attach->dev == NULL) break; if (&attach->list == &iommu_debug_attachments) { WARN(1, "Couldn't find debug attachment for domain=0x%p dev=%s", domain, dev_name(dev)); } else { attach->dev = dev; /* * we might not init until after other drivers start calling * iommu_attach_device. Only set up the debugfs nodes if we've * already init'd to avoid polluting the top-level debugfs * directory (by calling debugfs_create_dir with a NULL * parent). These will be flushed out later once we init. */ if (debugfs_attachments_dir) iommu_debug_attach_add_debugfs(attach); } mutex_unlock(&iommu_debug_attachments_lock); } void iommu_debug_detach_device(struct iommu_domain *domain, struct device *dev) { struct iommu_debug_attachment *it; mutex_lock(&iommu_debug_attachments_lock); list_for_each_entry(it, &iommu_debug_attachments, list) if (it->domain == domain && it->dev == dev) break; if (&it->list == &iommu_debug_attachments) { WARN(1, "Couldn't find debug attachment for domain=0x%p dev=%s", domain, dev_name(dev)); } else { /* * Just remove debugfs entry and mark dev as NULL on * iommu_detach call. We would remove the actual * attachment entry from the list only on domain_free call. * This is to ensure we keep track of unattached domains too. */ debugfs_remove_recursive(it->dentry); it->dev = NULL; } mutex_unlock(&iommu_debug_attachments_lock); } static int iommu_debug_init_tracking(void) { int ret = 0; struct iommu_debug_attachment *attach; mutex_lock(&iommu_debug_attachments_lock); debugfs_attachments_dir = debugfs_create_dir("attachments", iommu_debugfs_top); if (!debugfs_attachments_dir) { pr_err("Couldn't create iommu/attachments debugfs directory\n"); ret = -ENODEV; goto out_unlock; } /* set up debugfs entries for attachments made during early boot */ list_for_each_entry(attach, &iommu_debug_attachments, list) if (attach->dev) iommu_debug_attach_add_debugfs(attach); out_unlock: mutex_unlock(&iommu_debug_attachments_lock); return ret; } static void iommu_debug_destroy_tracking(void) { debugfs_remove_recursive(debugfs_attachments_dir); } #else static inline int iommu_debug_init_tracking(void) { return 0; } static inline void iommu_debug_destroy_tracking(void) { } #endif #ifdef CONFIG_IOMMU_TESTS Loading Loading @@ -486,6 +165,8 @@ struct iommu_debug_device { u64 phys; size_t len; struct list_head list; struct mutex clk_lock; unsigned int clk_count; }; static int iommu_debug_build_phoney_sg_table(struct device *dev, Loading Loading @@ -1524,6 +1205,7 @@ static int iommu_debug_attach_do_attach(struct iommu_debug_device *ddev, goto out_domain_free; } val = VMID_CP_CAMERA; if (is_secure && iommu_domain_set_attr(ddev->domain, DOMAIN_ATTR_SECURE_VMID, &val)) { Loading Loading @@ -1669,6 +1351,10 @@ static ssize_t iommu_debug_atos_read(struct file *file, char __user *ubuf, ssize_t retval; size_t buflen; if (kptr_restrict != 0) { pr_err("kptr_restrict needs to be disabled.\n"); return -EPERM; } if (!ddev->domain) { pr_err("No domain. Did you already attach?\n"); return -EINVAL; Loading Loading @@ -1876,20 +1562,34 @@ static ssize_t iommu_debug_config_clocks_write(struct file *file, return -EFAULT; } mutex_lock(&ddev->clk_lock); switch (buf) { case '0': if (ddev->clk_count == 0) { dev_err(dev, "Config clocks already disabled\n"); break; } if (--ddev->clk_count > 0) break; dev_err(dev, "Disabling config clocks\n"); iommu_disable_config_clocks(ddev->domain); break; case '1': if (ddev->clk_count++ > 0) break; dev_err(dev, "Enabling config clocks\n"); if (iommu_enable_config_clocks(ddev->domain)) dev_err(dev, "Failed!\n"); break; default: dev_err(dev, "Invalid value. Should be 0 or 1.\n"); mutex_unlock(&ddev->clk_lock); return -EINVAL; } mutex_unlock(&ddev->clk_lock); return count; } Loading @@ -1915,6 +1615,7 @@ static int snarf_iommu_devices(struct device *dev, const char *name) ddev = kzalloc(sizeof(*ddev), GFP_KERNEL); if (!ddev) return -ENODEV; mutex_init(&ddev->clk_lock); ddev->dev = dev; dir = debugfs_create_dir(name, debugfs_tests_dir); if (!dir) { Loading Loading @@ -2026,6 +1727,9 @@ err: static int pass_iommu_devices(struct device *dev, void *ignored) { if (!of_device_is_compatible(dev->of_node, "iommu-debug-test")) return 0; if (!of_find_property(dev->of_node, "iommus", NULL)) return 0; Loading @@ -2039,6 +1743,9 @@ static int iommu_debug_populate_devices(void) const char *cb_name; for_each_compatible_node(np, NULL, "qcom,msm-smmu-v2-ctx") { if (!of_device_is_compatible(np, "iommu-debug-test")) continue; ret = of_property_read_string(np, "label", &cb_name); if (ret) return ret; Loading Loading @@ -2075,9 +1782,6 @@ static inline void iommu_debug_destroy_tests(void) { } static int iommu_debug_init(void) { if (iommu_debug_init_tracking()) return -ENODEV; if (iommu_debug_init_tests()) return -ENODEV; Loading @@ -2086,7 +1790,6 @@ static int iommu_debug_init(void) static void iommu_debug_exit(void) { iommu_debug_destroy_tracking(); iommu_debug_destroy_tests(); } Loading
drivers/iommu/iommu-debug.h +12 −11 Original line number Diff line number Diff line /* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef IOMMU_DEBUG_H #define IOMMU_DEBUG_H #ifdef CONFIG_IOMMU_DEBUG_TRACKING void iommu_debug_attach_device(struct iommu_domain *domain, struct device *dev); void iommu_debug_detach_device(struct iommu_domain *domain, struct device *dev); void iommu_debug_domain_add(struct iommu_domain *domain); void iommu_debug_domain_remove(struct iommu_domain *domain); #else /* !CONFIG_IOMMU_DEBUG_TRACKING */ Loading @@ -15,15 +25,6 @@ static inline void iommu_debug_attach_device(struct iommu_domain *domain, { } static inline void iommu_debug_detach_device(struct iommu_domain *domain, struct device *dev) { } static inline void iommu_debug_domain_add(struct iommu_domain *domain) { } static inline void iommu_debug_domain_remove(struct iommu_domain *domain) { } Loading
drivers/iommu/iommu.c +0 −3 Original line number Diff line number Diff line Loading @@ -977,8 +977,6 @@ struct iommu_domain *iommu_domain_alloc(struct bus_type *bus) if (ret) goto out_free; iommu_debug_domain_add(domain); return domain; out_free: Loading Loading @@ -1019,7 +1017,6 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev) if (unlikely(domain->ops->detach_dev == NULL)) return; iommu_debug_detach_device(domain, dev); domain->ops->detach_dev(domain, dev); trace_detach_device_from_domain(dev); } Loading