Loading drivers/iommu/arm-smmu.c +21 −0 Original line number Diff line number Diff line Loading @@ -548,6 +548,9 @@ static void arm_smmu_unassign_table(struct arm_smmu_domain *smmu_domain); static int arm_smmu_arch_init(struct arm_smmu_device *smmu); static void arm_smmu_arch_device_reset(struct arm_smmu_device *smmu); static uint64_t arm_smmu_iova_to_pte(struct iommu_domain *domain, dma_addr_t iova); static int arm_smmu_enable_s1_translations(struct arm_smmu_domain *smmu_domain); static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom) Loading Loading @@ -2189,6 +2192,23 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, return ret; } static uint64_t arm_smmu_iova_to_pte(struct iommu_domain *domain, dma_addr_t iova) { uint64_t ret; unsigned long flags; struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops; if (!ops) return 0; spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags); ret = ops->iova_to_pte(ops, iova); spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags); return ret; } static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) { Loading Loading @@ -2976,6 +2996,7 @@ static struct iommu_ops arm_smmu_ops = { .enable_config_clocks = arm_smmu_enable_config_clocks, .disable_config_clocks = arm_smmu_disable_config_clocks, .is_iova_coherent = arm_smmu_is_iova_coherent, .iova_to_pte = arm_smmu_iova_to_pte, }; #define IMPL_DEF1_MICRO_MMU_CTRL 0 Loading drivers/iommu/io-pgtable-arm.c +14 −0 Original line number Diff line number Diff line Loading @@ -854,6 +854,19 @@ static int arm_lpae_iova_to_pte(struct arm_lpae_io_pgtable *data, return 0; } static uint64_t arm_lpae_iova_get_pte(struct io_pgtable_ops *ops, unsigned long iova) { struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); arm_lpae_iopte pte; int lvl; if (!arm_lpae_iova_to_pte(data, iova, &lvl, &pte)) return pte; return 0; } static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops, unsigned long iova) { Loading Loading @@ -983,6 +996,7 @@ arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg) .unmap = arm_lpae_unmap, .iova_to_phys = arm_lpae_iova_to_phys, .is_iova_coherent = arm_lpae_is_iova_coherent, .iova_to_pte = arm_lpae_iova_get_pte, }; return data; Loading drivers/iommu/io-pgtable.h +2 −0 Original line number Diff line number Diff line Loading @@ -149,6 +149,8 @@ struct io_pgtable_ops { unsigned long iova); bool (*is_iova_coherent)(struct io_pgtable_ops *ops, unsigned long iova); uint64_t (*iova_to_pte)(struct io_pgtable_ops *ops, unsigned long iova); }; Loading drivers/iommu/iommu-debug.c +536 −0 Original line number Diff line number Diff line Loading @@ -154,6 +154,7 @@ void iommu_debug_domain_remove(struct iommu_domain *domain) static LIST_HEAD(iommu_debug_devices); static struct dentry *debugfs_tests_dir; static u32 iters_per_op = 1; static void *test_virt_addr; struct iommu_debug_device { struct device *dev; Loading Loading @@ -1207,6 +1208,68 @@ static int iommu_debug_attach_do_attach(struct iommu_debug_device *ddev, return -EIO; } static ssize_t __iommu_debug_dma_attach_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_device *ddev = file->private_data; struct device *dev = ddev->dev; struct dma_iommu_mapping *dma_mapping; ssize_t retval = -EINVAL; int val; if (kstrtoint_from_user(ubuf, count, 0, &val)) { pr_err("Invalid format. Expected a hex or decimal integer"); retval = -EFAULT; goto out; } if (val) { if (dev->archdata.mapping) if (dev->archdata.mapping->domain) { pr_err("Already attached.\n"); retval = -EINVAL; goto out; } if (WARN(dev->archdata.iommu, "Attachment tracking out of sync with device\n")) { retval = -EINVAL; goto out; } dma_mapping = arm_iommu_create_mapping(&platform_bus_type, 0, (SZ_1G * 4ULL)); if (!dma_mapping) goto out; if (arm_iommu_attach_device(dev, dma_mapping)) goto out_release_mapping; pr_err("Attached\n"); } else { if (!dev->archdata.mapping) { pr_err("No mapping. Did you already attach?\n"); retval = -EINVAL; goto out; } if (!dev->archdata.mapping->domain) { pr_err("No domain. Did you already attach?\n"); retval = -EINVAL; goto out; } arm_iommu_detach_device(dev); arm_iommu_release_mapping(dev->archdata.mapping); pr_err("Detached\n"); } retval = count; return retval; out_release_mapping: arm_iommu_release_mapping(dma_mapping); out: return retval; } static ssize_t __iommu_debug_attach_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset, Loading Loading @@ -1260,6 +1323,81 @@ static ssize_t __iommu_debug_attach_write(struct file *file, return retval; } static ssize_t iommu_debug_dma_attach_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { return __iommu_debug_dma_attach_write(file, ubuf, count, offset); } static ssize_t iommu_debug_dma_attach_read(struct file *file, char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_device *ddev = file->private_data; struct device *dev = ddev->dev; char c[2]; if (*offset) return 0; if (!dev->archdata.mapping) c[0] = '0'; else c[0] = dev->archdata.mapping->domain ? '1' : '0'; c[1] = '\n'; if (copy_to_user(ubuf, &c, 2)) { pr_err("copy_to_user failed\n"); return -EFAULT; } *offset = 1; /* non-zero means we're done */ return 2; } static const struct file_operations iommu_debug_dma_attach_fops = { .open = simple_open, .write = iommu_debug_dma_attach_write, .read = iommu_debug_dma_attach_read, }; static ssize_t iommu_debug_test_virt_addr_read(struct file *file, char __user *ubuf, size_t count, loff_t *offset) { char buf[100]; ssize_t retval; size_t buflen; int buf_len = sizeof(buf); if (*offset) return 0; memset(buf, 0, buf_len); if (!test_virt_addr) strlcpy(buf, "FAIL\n", buf_len); else snprintf(buf, buf_len, "0x%pK\n", test_virt_addr); buflen = strlen(buf); if (copy_to_user(ubuf, buf, buflen)) { pr_err("Couldn't copy_to_user\n"); retval = -EFAULT; } else { *offset = 1; /* non-zero means we're done */ retval = buflen; } return retval; } static const struct file_operations iommu_debug_test_virt_addr_fops = { .open = simple_open, .read = iommu_debug_test_virt_addr_read, }; static ssize_t iommu_debug_attach_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) Loading Loading @@ -1309,6 +1447,75 @@ static const struct file_operations iommu_debug_secure_attach_fops = { .read = iommu_debug_attach_read, }; static ssize_t iommu_debug_pte_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_device *ddev = file->private_data; dma_addr_t iova; if (kstrtox_from_user(ubuf, count, 0, &iova)) { pr_err("Invalid format for iova\n"); ddev->iova = 0; return -EINVAL; } ddev->iova = iova; pr_err("Saved iova=%pa for future PTE commands\n", &iova); return count; } static ssize_t iommu_debug_pte_read(struct file *file, char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_device *ddev = file->private_data; struct device *dev = ddev->dev; uint64_t pte; char buf[100]; ssize_t retval; size_t buflen; if (!dev->archdata.mapping) { pr_err("No mapping. Did you already attach?\n"); return -EINVAL; } if (!dev->archdata.mapping->domain) { pr_err("No domain. Did you already attach?\n"); return -EINVAL; } if (*offset) return 0; memset(buf, 0, sizeof(buf)); pte = iommu_iova_to_pte(dev->archdata.mapping->domain, ddev->iova); if (!pte) strlcpy(buf, "FAIL\n", sizeof(buf)); else snprintf(buf, sizeof(buf), "pte=%016llx\n", pte); buflen = strlen(buf); if (copy_to_user(ubuf, buf, buflen)) { pr_err("Couldn't copy_to_user\n"); retval = -EFAULT; } else { *offset = 1; /* non-zero means we're done */ retval = buflen; } return retval; } static const struct file_operations iommu_debug_pte_fops = { .open = simple_open, .write = iommu_debug_pte_write, .read = iommu_debug_pte_read, }; static ssize_t iommu_debug_atos_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) Loading Loading @@ -1370,6 +1577,55 @@ static const struct file_operations iommu_debug_atos_fops = { .read = iommu_debug_atos_read, }; static ssize_t iommu_debug_dma_atos_read(struct file *file, char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_device *ddev = file->private_data; struct device *dev = ddev->dev; phys_addr_t phys; char buf[100]; ssize_t retval; size_t buflen; if (!dev->archdata.mapping) { pr_err("No mapping. Did you already attach?\n"); return -EINVAL; } if (!dev->archdata.mapping->domain) { pr_err("No domain. Did you already attach?\n"); return -EINVAL; } if (*offset) return 0; memset(buf, 0, sizeof(buf)); phys = iommu_iova_to_phys_hard(dev->archdata.mapping->domain, ddev->iova); if (!phys) strlcpy(buf, "FAIL\n", sizeof(buf)); else snprintf(buf, sizeof(buf), "%pa\n", &phys); buflen = strlen(buf); if (copy_to_user(ubuf, buf, buflen)) { pr_err("Couldn't copy_to_user\n"); retval = -EFAULT; } else { *offset = 1; /* non-zero means we're done */ retval = buflen; } return retval; } static const struct file_operations iommu_debug_dma_atos_fops = { .open = simple_open, .write = iommu_debug_atos_write, .read = iommu_debug_dma_atos_read, }; static ssize_t iommu_debug_map_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { Loading Loading @@ -1450,6 +1706,156 @@ static const struct file_operations iommu_debug_map_fops = { .write = iommu_debug_map_write, }; /* * Performs DMA mapping of a given virtual address and size to an iova address. * User input format: (addr,len,dma attr) where dma attr is: * 0: normal mapping * 1: force coherent mapping * 2: force non-cohernet mapping */ static ssize_t iommu_debug_dma_map_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { ssize_t retval = -EINVAL; int ret; char *comma1, *comma2; char buf[100]; unsigned long addr; void *v_addr; dma_addr_t iova; size_t size; unsigned int attr; unsigned long dma_attrs; struct iommu_debug_device *ddev = file->private_data; struct device *dev = ddev->dev; if (count >= sizeof(buf)) { pr_err("Value too large\n"); return -EINVAL; } if (!dev->archdata.mapping) { pr_err("No mapping. Did you already attach?\n"); retval = -EINVAL; goto out; } if (!dev->archdata.mapping->domain) { pr_err("No domain. Did you already attach?\n"); retval = -EINVAL; goto out; } memset(buf, 0, sizeof(buf)); if (copy_from_user(buf, ubuf, count)) { pr_err("Couldn't copy from user\n"); retval = -EFAULT; goto out; } comma1 = strnchr(buf, count, ','); if (!comma1) goto invalid_format; comma2 = strnchr(comma1 + 1, count, ','); if (!comma2) goto invalid_format; *comma1 = *comma2 = '\0'; if (kstrtoul(buf, 0, &addr)) goto invalid_format; v_addr = (void *)addr; if (kstrtosize_t(comma1 + 1, 0, &size)) goto invalid_format; if (kstrtouint(comma2 + 1, 0, &attr)) goto invalid_format; if (v_addr < test_virt_addr || v_addr > (test_virt_addr + SZ_1M - 1)) goto invalid_addr; if (attr == 0) dma_attrs = 0; else if (attr == 1) dma_attrs = DMA_ATTR_FORCE_COHERENT; else if (attr == 2) dma_attrs = DMA_ATTR_FORCE_NON_COHERENT; else goto invalid_format; iova = dma_map_single_attrs(dev, v_addr, size, DMA_TO_DEVICE, dma_attrs); if (dma_mapping_error(dev, iova)) { pr_err("Failed to perform dma_map_single\n"); ret = -EINVAL; goto out; } retval = count; pr_err("Mapped 0x%p to %pa (len=0x%zx)\n", v_addr, &iova, size); ddev->iova = iova; pr_err("Saved iova=%pa for future PTE commands\n", &iova); out: return retval; invalid_format: pr_err("Invalid format. Expected: addr,len,dma attr where 'dma attr' is\n0: normal mapping\n1: force coherent\n2: force non-cohernet\n"); return retval; invalid_addr: pr_err("Invalid addr given! Address should be within 1MB size from start addr returned by doing 'cat test_virt_addr'.\n"); return retval; } static ssize_t iommu_debug_dma_map_read(struct file *file, char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_device *ddev = file->private_data; struct device *dev = ddev->dev; char buf[100]; ssize_t retval; size_t buflen; dma_addr_t iova; if (!dev->archdata.mapping) { pr_err("No mapping. Did you already attach?\n"); return -EINVAL; } if (!dev->archdata.mapping->domain) { pr_err("No domain. Did you already attach?\n"); return -EINVAL; } if (*offset) return 0; memset(buf, 0, sizeof(buf)); iova = ddev->iova; snprintf(buf, sizeof(buf), "%pa\n", &iova); buflen = strlen(buf); if (copy_to_user(ubuf, buf, buflen)) { pr_err("Couldn't copy_to_user\n"); retval = -EFAULT; } else { *offset = 1; /* non-zero means we're done */ retval = buflen; } return retval; } static const struct file_operations iommu_debug_dma_map_fops = { .open = simple_open, .write = iommu_debug_dma_map_write, .read = iommu_debug_dma_map_read, }; static ssize_t iommu_debug_unmap_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) Loading Loading @@ -1515,6 +1921,89 @@ static const struct file_operations iommu_debug_unmap_fops = { .write = iommu_debug_unmap_write, }; static ssize_t iommu_debug_dma_unmap_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { ssize_t retval = 0; char *comma1, *comma2; char buf[100]; size_t size; unsigned int attr; dma_addr_t iova; unsigned long dma_attrs; struct iommu_debug_device *ddev = file->private_data; struct device *dev = ddev->dev; if (count >= sizeof(buf)) { pr_err("Value too large\n"); return -EINVAL; } if (!dev->archdata.mapping) { pr_err("No mapping. Did you already attach?\n"); retval = -EINVAL; goto out; } if (!dev->archdata.mapping->domain) { pr_err("No domain. Did you already attach?\n"); retval = -EINVAL; goto out; } memset(buf, 0, sizeof(buf)); if (copy_from_user(buf, ubuf, count)) { pr_err("Couldn't copy from user\n"); retval = -EFAULT; goto out; } comma1 = strnchr(buf, count, ','); if (!comma1) goto invalid_format; comma2 = strnchr(comma1 + 1, count, ','); if (!comma2) goto invalid_format; *comma1 = *comma2 = '\0'; if (kstrtoux(buf, 0, &iova)) goto invalid_format; if (kstrtosize_t(comma1 + 1, 0, &size)) goto invalid_format; if (kstrtouint(comma2 + 1, 0, &attr)) goto invalid_format; if (attr == 0) dma_attrs = 0; else if (attr == 1) dma_attrs = DMA_ATTR_FORCE_COHERENT; else if (attr == 2) dma_attrs = DMA_ATTR_FORCE_NON_COHERENT; else goto invalid_format; dma_unmap_single_attrs(dev, iova, size, DMA_TO_DEVICE, dma_attrs); retval = count; pr_err("Unmapped %pa (len=0x%zx)\n", &iova, size); out: return retval; invalid_format: pr_err("Invalid format. Expected: iova,len, dma attr\n"); return retval; } static const struct file_operations iommu_debug_dma_unmap_fops = { .open = simple_open, .write = iommu_debug_dma_unmap_write, }; static ssize_t iommu_debug_config_clocks_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) Loading Loading @@ -1624,6 +2113,13 @@ static int snarf_iommu_devices(struct device *dev, void *ignored) goto err_rmdir; } if (!debugfs_create_file("test_virt_addr", 0400, dir, ddev, &iommu_debug_test_virt_addr_fops)) { pr_err("Couldn't create iommu/devices/%s/test_virt_addr debugfs file\n", dev_name(dev)); goto err_rmdir; } if (!debugfs_create_file("profiling", S_IRUSR, dir, ddev, &iommu_debug_profiling_fops)) { pr_err("Couldn't create iommu/devices/%s/profiling debugfs file\n", Loading Loading @@ -1666,6 +2162,13 @@ static int snarf_iommu_devices(struct device *dev, void *ignored) goto err_rmdir; } if (!debugfs_create_file("dma_attach", 0600, dir, ddev, &iommu_debug_dma_attach_fops)) { pr_err("Couldn't create iommu/devices/%s/dma_attach debugfs file\n", dev_name(dev)); goto err_rmdir; } if (!debugfs_create_file("attach", S_IRUSR, dir, ddev, &iommu_debug_attach_fops)) { pr_err("Couldn't create iommu/devices/%s/attach debugfs file\n", Loading @@ -1687,6 +2190,13 @@ static int snarf_iommu_devices(struct device *dev, void *ignored) goto err_rmdir; } if (!debugfs_create_file("dma_atos", 0600, dir, ddev, &iommu_debug_dma_atos_fops)) { pr_err("Couldn't create iommu/devices/%s/dma_atos debugfs file\n", dev_name(dev)); goto err_rmdir; } if (!debugfs_create_file("map", S_IWUSR, dir, ddev, &iommu_debug_map_fops)) { pr_err("Couldn't create iommu/devices/%s/map debugfs file\n", Loading @@ -1694,6 +2204,13 @@ static int snarf_iommu_devices(struct device *dev, void *ignored) goto err_rmdir; } if (!debugfs_create_file("dma_map", 0600, dir, ddev, &iommu_debug_dma_map_fops)) { pr_err("Couldn't create iommu/devices/%s/dma_map debugfs file\n", dev_name(dev)); goto err_rmdir; } if (!debugfs_create_file("unmap", S_IWUSR, dir, ddev, &iommu_debug_unmap_fops)) { pr_err("Couldn't create iommu/devices/%s/unmap debugfs file\n", Loading @@ -1701,6 +2218,20 @@ static int snarf_iommu_devices(struct device *dev, void *ignored) goto err_rmdir; } if (!debugfs_create_file("dma_unmap", 0200, dir, ddev, &iommu_debug_dma_unmap_fops)) { pr_err("Couldn't create iommu/devices/%s/dma_unmap debugfs file\n", dev_name(dev)); goto err_rmdir; } if (!debugfs_create_file("pte", 0600, dir, ddev, &iommu_debug_pte_fops)) { pr_err("Couldn't create iommu/devices/%s/pte debugfs file\n", dev_name(dev)); goto err_rmdir; } if (!debugfs_create_file("config_clocks", S_IWUSR, dir, ddev, &iommu_debug_config_clocks_fops)) { pr_err("Couldn't create iommu/devices/%s/config_clocks debugfs file\n", Loading Loading @@ -1734,6 +2265,11 @@ static int iommu_debug_init_tests(void) return -ENODEV; } test_virt_addr = kzalloc(SZ_1M, GFP_KERNEL); if (!test_virt_addr) return -ENOMEM; return bus_for_each_dev(&platform_bus_type, NULL, NULL, snarf_iommu_devices); } Loading drivers/iommu/iommu.c +9 −0 Original line number Diff line number Diff line Loading @@ -1282,6 +1282,15 @@ phys_addr_t iommu_iova_to_phys_hard(struct iommu_domain *domain, return domain->ops->iova_to_phys_hard(domain, iova); } uint64_t iommu_iova_to_pte(struct iommu_domain *domain, dma_addr_t iova) { if (unlikely(domain->ops->iova_to_pte == NULL)) return 0; return domain->ops->iova_to_pte(domain, iova); } bool iommu_is_iova_coherent(struct iommu_domain *domain, dma_addr_t iova) { if (unlikely(domain->ops->is_iova_coherent == NULL)) Loading Loading
drivers/iommu/arm-smmu.c +21 −0 Original line number Diff line number Diff line Loading @@ -548,6 +548,9 @@ static void arm_smmu_unassign_table(struct arm_smmu_domain *smmu_domain); static int arm_smmu_arch_init(struct arm_smmu_device *smmu); static void arm_smmu_arch_device_reset(struct arm_smmu_device *smmu); static uint64_t arm_smmu_iova_to_pte(struct iommu_domain *domain, dma_addr_t iova); static int arm_smmu_enable_s1_translations(struct arm_smmu_domain *smmu_domain); static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom) Loading Loading @@ -2189,6 +2192,23 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, return ret; } static uint64_t arm_smmu_iova_to_pte(struct iommu_domain *domain, dma_addr_t iova) { uint64_t ret; unsigned long flags; struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops; if (!ops) return 0; spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags); ret = ops->iova_to_pte(ops, iova); spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags); return ret; } static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) { Loading Loading @@ -2976,6 +2996,7 @@ static struct iommu_ops arm_smmu_ops = { .enable_config_clocks = arm_smmu_enable_config_clocks, .disable_config_clocks = arm_smmu_disable_config_clocks, .is_iova_coherent = arm_smmu_is_iova_coherent, .iova_to_pte = arm_smmu_iova_to_pte, }; #define IMPL_DEF1_MICRO_MMU_CTRL 0 Loading
drivers/iommu/io-pgtable-arm.c +14 −0 Original line number Diff line number Diff line Loading @@ -854,6 +854,19 @@ static int arm_lpae_iova_to_pte(struct arm_lpae_io_pgtable *data, return 0; } static uint64_t arm_lpae_iova_get_pte(struct io_pgtable_ops *ops, unsigned long iova) { struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); arm_lpae_iopte pte; int lvl; if (!arm_lpae_iova_to_pte(data, iova, &lvl, &pte)) return pte; return 0; } static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops, unsigned long iova) { Loading Loading @@ -983,6 +996,7 @@ arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg) .unmap = arm_lpae_unmap, .iova_to_phys = arm_lpae_iova_to_phys, .is_iova_coherent = arm_lpae_is_iova_coherent, .iova_to_pte = arm_lpae_iova_get_pte, }; return data; Loading
drivers/iommu/io-pgtable.h +2 −0 Original line number Diff line number Diff line Loading @@ -149,6 +149,8 @@ struct io_pgtable_ops { unsigned long iova); bool (*is_iova_coherent)(struct io_pgtable_ops *ops, unsigned long iova); uint64_t (*iova_to_pte)(struct io_pgtable_ops *ops, unsigned long iova); }; Loading
drivers/iommu/iommu-debug.c +536 −0 Original line number Diff line number Diff line Loading @@ -154,6 +154,7 @@ void iommu_debug_domain_remove(struct iommu_domain *domain) static LIST_HEAD(iommu_debug_devices); static struct dentry *debugfs_tests_dir; static u32 iters_per_op = 1; static void *test_virt_addr; struct iommu_debug_device { struct device *dev; Loading Loading @@ -1207,6 +1208,68 @@ static int iommu_debug_attach_do_attach(struct iommu_debug_device *ddev, return -EIO; } static ssize_t __iommu_debug_dma_attach_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_device *ddev = file->private_data; struct device *dev = ddev->dev; struct dma_iommu_mapping *dma_mapping; ssize_t retval = -EINVAL; int val; if (kstrtoint_from_user(ubuf, count, 0, &val)) { pr_err("Invalid format. Expected a hex or decimal integer"); retval = -EFAULT; goto out; } if (val) { if (dev->archdata.mapping) if (dev->archdata.mapping->domain) { pr_err("Already attached.\n"); retval = -EINVAL; goto out; } if (WARN(dev->archdata.iommu, "Attachment tracking out of sync with device\n")) { retval = -EINVAL; goto out; } dma_mapping = arm_iommu_create_mapping(&platform_bus_type, 0, (SZ_1G * 4ULL)); if (!dma_mapping) goto out; if (arm_iommu_attach_device(dev, dma_mapping)) goto out_release_mapping; pr_err("Attached\n"); } else { if (!dev->archdata.mapping) { pr_err("No mapping. Did you already attach?\n"); retval = -EINVAL; goto out; } if (!dev->archdata.mapping->domain) { pr_err("No domain. Did you already attach?\n"); retval = -EINVAL; goto out; } arm_iommu_detach_device(dev); arm_iommu_release_mapping(dev->archdata.mapping); pr_err("Detached\n"); } retval = count; return retval; out_release_mapping: arm_iommu_release_mapping(dma_mapping); out: return retval; } static ssize_t __iommu_debug_attach_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset, Loading Loading @@ -1260,6 +1323,81 @@ static ssize_t __iommu_debug_attach_write(struct file *file, return retval; } static ssize_t iommu_debug_dma_attach_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { return __iommu_debug_dma_attach_write(file, ubuf, count, offset); } static ssize_t iommu_debug_dma_attach_read(struct file *file, char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_device *ddev = file->private_data; struct device *dev = ddev->dev; char c[2]; if (*offset) return 0; if (!dev->archdata.mapping) c[0] = '0'; else c[0] = dev->archdata.mapping->domain ? '1' : '0'; c[1] = '\n'; if (copy_to_user(ubuf, &c, 2)) { pr_err("copy_to_user failed\n"); return -EFAULT; } *offset = 1; /* non-zero means we're done */ return 2; } static const struct file_operations iommu_debug_dma_attach_fops = { .open = simple_open, .write = iommu_debug_dma_attach_write, .read = iommu_debug_dma_attach_read, }; static ssize_t iommu_debug_test_virt_addr_read(struct file *file, char __user *ubuf, size_t count, loff_t *offset) { char buf[100]; ssize_t retval; size_t buflen; int buf_len = sizeof(buf); if (*offset) return 0; memset(buf, 0, buf_len); if (!test_virt_addr) strlcpy(buf, "FAIL\n", buf_len); else snprintf(buf, buf_len, "0x%pK\n", test_virt_addr); buflen = strlen(buf); if (copy_to_user(ubuf, buf, buflen)) { pr_err("Couldn't copy_to_user\n"); retval = -EFAULT; } else { *offset = 1; /* non-zero means we're done */ retval = buflen; } return retval; } static const struct file_operations iommu_debug_test_virt_addr_fops = { .open = simple_open, .read = iommu_debug_test_virt_addr_read, }; static ssize_t iommu_debug_attach_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) Loading Loading @@ -1309,6 +1447,75 @@ static const struct file_operations iommu_debug_secure_attach_fops = { .read = iommu_debug_attach_read, }; static ssize_t iommu_debug_pte_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_device *ddev = file->private_data; dma_addr_t iova; if (kstrtox_from_user(ubuf, count, 0, &iova)) { pr_err("Invalid format for iova\n"); ddev->iova = 0; return -EINVAL; } ddev->iova = iova; pr_err("Saved iova=%pa for future PTE commands\n", &iova); return count; } static ssize_t iommu_debug_pte_read(struct file *file, char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_device *ddev = file->private_data; struct device *dev = ddev->dev; uint64_t pte; char buf[100]; ssize_t retval; size_t buflen; if (!dev->archdata.mapping) { pr_err("No mapping. Did you already attach?\n"); return -EINVAL; } if (!dev->archdata.mapping->domain) { pr_err("No domain. Did you already attach?\n"); return -EINVAL; } if (*offset) return 0; memset(buf, 0, sizeof(buf)); pte = iommu_iova_to_pte(dev->archdata.mapping->domain, ddev->iova); if (!pte) strlcpy(buf, "FAIL\n", sizeof(buf)); else snprintf(buf, sizeof(buf), "pte=%016llx\n", pte); buflen = strlen(buf); if (copy_to_user(ubuf, buf, buflen)) { pr_err("Couldn't copy_to_user\n"); retval = -EFAULT; } else { *offset = 1; /* non-zero means we're done */ retval = buflen; } return retval; } static const struct file_operations iommu_debug_pte_fops = { .open = simple_open, .write = iommu_debug_pte_write, .read = iommu_debug_pte_read, }; static ssize_t iommu_debug_atos_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) Loading Loading @@ -1370,6 +1577,55 @@ static const struct file_operations iommu_debug_atos_fops = { .read = iommu_debug_atos_read, }; static ssize_t iommu_debug_dma_atos_read(struct file *file, char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_device *ddev = file->private_data; struct device *dev = ddev->dev; phys_addr_t phys; char buf[100]; ssize_t retval; size_t buflen; if (!dev->archdata.mapping) { pr_err("No mapping. Did you already attach?\n"); return -EINVAL; } if (!dev->archdata.mapping->domain) { pr_err("No domain. Did you already attach?\n"); return -EINVAL; } if (*offset) return 0; memset(buf, 0, sizeof(buf)); phys = iommu_iova_to_phys_hard(dev->archdata.mapping->domain, ddev->iova); if (!phys) strlcpy(buf, "FAIL\n", sizeof(buf)); else snprintf(buf, sizeof(buf), "%pa\n", &phys); buflen = strlen(buf); if (copy_to_user(ubuf, buf, buflen)) { pr_err("Couldn't copy_to_user\n"); retval = -EFAULT; } else { *offset = 1; /* non-zero means we're done */ retval = buflen; } return retval; } static const struct file_operations iommu_debug_dma_atos_fops = { .open = simple_open, .write = iommu_debug_atos_write, .read = iommu_debug_dma_atos_read, }; static ssize_t iommu_debug_map_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { Loading Loading @@ -1450,6 +1706,156 @@ static const struct file_operations iommu_debug_map_fops = { .write = iommu_debug_map_write, }; /* * Performs DMA mapping of a given virtual address and size to an iova address. * User input format: (addr,len,dma attr) where dma attr is: * 0: normal mapping * 1: force coherent mapping * 2: force non-cohernet mapping */ static ssize_t iommu_debug_dma_map_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { ssize_t retval = -EINVAL; int ret; char *comma1, *comma2; char buf[100]; unsigned long addr; void *v_addr; dma_addr_t iova; size_t size; unsigned int attr; unsigned long dma_attrs; struct iommu_debug_device *ddev = file->private_data; struct device *dev = ddev->dev; if (count >= sizeof(buf)) { pr_err("Value too large\n"); return -EINVAL; } if (!dev->archdata.mapping) { pr_err("No mapping. Did you already attach?\n"); retval = -EINVAL; goto out; } if (!dev->archdata.mapping->domain) { pr_err("No domain. Did you already attach?\n"); retval = -EINVAL; goto out; } memset(buf, 0, sizeof(buf)); if (copy_from_user(buf, ubuf, count)) { pr_err("Couldn't copy from user\n"); retval = -EFAULT; goto out; } comma1 = strnchr(buf, count, ','); if (!comma1) goto invalid_format; comma2 = strnchr(comma1 + 1, count, ','); if (!comma2) goto invalid_format; *comma1 = *comma2 = '\0'; if (kstrtoul(buf, 0, &addr)) goto invalid_format; v_addr = (void *)addr; if (kstrtosize_t(comma1 + 1, 0, &size)) goto invalid_format; if (kstrtouint(comma2 + 1, 0, &attr)) goto invalid_format; if (v_addr < test_virt_addr || v_addr > (test_virt_addr + SZ_1M - 1)) goto invalid_addr; if (attr == 0) dma_attrs = 0; else if (attr == 1) dma_attrs = DMA_ATTR_FORCE_COHERENT; else if (attr == 2) dma_attrs = DMA_ATTR_FORCE_NON_COHERENT; else goto invalid_format; iova = dma_map_single_attrs(dev, v_addr, size, DMA_TO_DEVICE, dma_attrs); if (dma_mapping_error(dev, iova)) { pr_err("Failed to perform dma_map_single\n"); ret = -EINVAL; goto out; } retval = count; pr_err("Mapped 0x%p to %pa (len=0x%zx)\n", v_addr, &iova, size); ddev->iova = iova; pr_err("Saved iova=%pa for future PTE commands\n", &iova); out: return retval; invalid_format: pr_err("Invalid format. Expected: addr,len,dma attr where 'dma attr' is\n0: normal mapping\n1: force coherent\n2: force non-cohernet\n"); return retval; invalid_addr: pr_err("Invalid addr given! Address should be within 1MB size from start addr returned by doing 'cat test_virt_addr'.\n"); return retval; } static ssize_t iommu_debug_dma_map_read(struct file *file, char __user *ubuf, size_t count, loff_t *offset) { struct iommu_debug_device *ddev = file->private_data; struct device *dev = ddev->dev; char buf[100]; ssize_t retval; size_t buflen; dma_addr_t iova; if (!dev->archdata.mapping) { pr_err("No mapping. Did you already attach?\n"); return -EINVAL; } if (!dev->archdata.mapping->domain) { pr_err("No domain. Did you already attach?\n"); return -EINVAL; } if (*offset) return 0; memset(buf, 0, sizeof(buf)); iova = ddev->iova; snprintf(buf, sizeof(buf), "%pa\n", &iova); buflen = strlen(buf); if (copy_to_user(ubuf, buf, buflen)) { pr_err("Couldn't copy_to_user\n"); retval = -EFAULT; } else { *offset = 1; /* non-zero means we're done */ retval = buflen; } return retval; } static const struct file_operations iommu_debug_dma_map_fops = { .open = simple_open, .write = iommu_debug_dma_map_write, .read = iommu_debug_dma_map_read, }; static ssize_t iommu_debug_unmap_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) Loading Loading @@ -1515,6 +1921,89 @@ static const struct file_operations iommu_debug_unmap_fops = { .write = iommu_debug_unmap_write, }; static ssize_t iommu_debug_dma_unmap_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { ssize_t retval = 0; char *comma1, *comma2; char buf[100]; size_t size; unsigned int attr; dma_addr_t iova; unsigned long dma_attrs; struct iommu_debug_device *ddev = file->private_data; struct device *dev = ddev->dev; if (count >= sizeof(buf)) { pr_err("Value too large\n"); return -EINVAL; } if (!dev->archdata.mapping) { pr_err("No mapping. Did you already attach?\n"); retval = -EINVAL; goto out; } if (!dev->archdata.mapping->domain) { pr_err("No domain. Did you already attach?\n"); retval = -EINVAL; goto out; } memset(buf, 0, sizeof(buf)); if (copy_from_user(buf, ubuf, count)) { pr_err("Couldn't copy from user\n"); retval = -EFAULT; goto out; } comma1 = strnchr(buf, count, ','); if (!comma1) goto invalid_format; comma2 = strnchr(comma1 + 1, count, ','); if (!comma2) goto invalid_format; *comma1 = *comma2 = '\0'; if (kstrtoux(buf, 0, &iova)) goto invalid_format; if (kstrtosize_t(comma1 + 1, 0, &size)) goto invalid_format; if (kstrtouint(comma2 + 1, 0, &attr)) goto invalid_format; if (attr == 0) dma_attrs = 0; else if (attr == 1) dma_attrs = DMA_ATTR_FORCE_COHERENT; else if (attr == 2) dma_attrs = DMA_ATTR_FORCE_NON_COHERENT; else goto invalid_format; dma_unmap_single_attrs(dev, iova, size, DMA_TO_DEVICE, dma_attrs); retval = count; pr_err("Unmapped %pa (len=0x%zx)\n", &iova, size); out: return retval; invalid_format: pr_err("Invalid format. Expected: iova,len, dma attr\n"); return retval; } static const struct file_operations iommu_debug_dma_unmap_fops = { .open = simple_open, .write = iommu_debug_dma_unmap_write, }; static ssize_t iommu_debug_config_clocks_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) Loading Loading @@ -1624,6 +2113,13 @@ static int snarf_iommu_devices(struct device *dev, void *ignored) goto err_rmdir; } if (!debugfs_create_file("test_virt_addr", 0400, dir, ddev, &iommu_debug_test_virt_addr_fops)) { pr_err("Couldn't create iommu/devices/%s/test_virt_addr debugfs file\n", dev_name(dev)); goto err_rmdir; } if (!debugfs_create_file("profiling", S_IRUSR, dir, ddev, &iommu_debug_profiling_fops)) { pr_err("Couldn't create iommu/devices/%s/profiling debugfs file\n", Loading Loading @@ -1666,6 +2162,13 @@ static int snarf_iommu_devices(struct device *dev, void *ignored) goto err_rmdir; } if (!debugfs_create_file("dma_attach", 0600, dir, ddev, &iommu_debug_dma_attach_fops)) { pr_err("Couldn't create iommu/devices/%s/dma_attach debugfs file\n", dev_name(dev)); goto err_rmdir; } if (!debugfs_create_file("attach", S_IRUSR, dir, ddev, &iommu_debug_attach_fops)) { pr_err("Couldn't create iommu/devices/%s/attach debugfs file\n", Loading @@ -1687,6 +2190,13 @@ static int snarf_iommu_devices(struct device *dev, void *ignored) goto err_rmdir; } if (!debugfs_create_file("dma_atos", 0600, dir, ddev, &iommu_debug_dma_atos_fops)) { pr_err("Couldn't create iommu/devices/%s/dma_atos debugfs file\n", dev_name(dev)); goto err_rmdir; } if (!debugfs_create_file("map", S_IWUSR, dir, ddev, &iommu_debug_map_fops)) { pr_err("Couldn't create iommu/devices/%s/map debugfs file\n", Loading @@ -1694,6 +2204,13 @@ static int snarf_iommu_devices(struct device *dev, void *ignored) goto err_rmdir; } if (!debugfs_create_file("dma_map", 0600, dir, ddev, &iommu_debug_dma_map_fops)) { pr_err("Couldn't create iommu/devices/%s/dma_map debugfs file\n", dev_name(dev)); goto err_rmdir; } if (!debugfs_create_file("unmap", S_IWUSR, dir, ddev, &iommu_debug_unmap_fops)) { pr_err("Couldn't create iommu/devices/%s/unmap debugfs file\n", Loading @@ -1701,6 +2218,20 @@ static int snarf_iommu_devices(struct device *dev, void *ignored) goto err_rmdir; } if (!debugfs_create_file("dma_unmap", 0200, dir, ddev, &iommu_debug_dma_unmap_fops)) { pr_err("Couldn't create iommu/devices/%s/dma_unmap debugfs file\n", dev_name(dev)); goto err_rmdir; } if (!debugfs_create_file("pte", 0600, dir, ddev, &iommu_debug_pte_fops)) { pr_err("Couldn't create iommu/devices/%s/pte debugfs file\n", dev_name(dev)); goto err_rmdir; } if (!debugfs_create_file("config_clocks", S_IWUSR, dir, ddev, &iommu_debug_config_clocks_fops)) { pr_err("Couldn't create iommu/devices/%s/config_clocks debugfs file\n", Loading Loading @@ -1734,6 +2265,11 @@ static int iommu_debug_init_tests(void) return -ENODEV; } test_virt_addr = kzalloc(SZ_1M, GFP_KERNEL); if (!test_virt_addr) return -ENOMEM; return bus_for_each_dev(&platform_bus_type, NULL, NULL, snarf_iommu_devices); } Loading
drivers/iommu/iommu.c +9 −0 Original line number Diff line number Diff line Loading @@ -1282,6 +1282,15 @@ phys_addr_t iommu_iova_to_phys_hard(struct iommu_domain *domain, return domain->ops->iova_to_phys_hard(domain, iova); } uint64_t iommu_iova_to_pte(struct iommu_domain *domain, dma_addr_t iova) { if (unlikely(domain->ops->iova_to_pte == NULL)) return 0; return domain->ops->iova_to_pte(domain, iova); } bool iommu_is_iova_coherent(struct iommu_domain *domain, dma_addr_t iova) { if (unlikely(domain->ops->is_iova_coherent == NULL)) Loading