Loading drivers/iommu/arm-smmu.c +19 −17 Original line number Diff line number Diff line Loading @@ -1820,23 +1820,18 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, struct arm_smmu_cfg *cfg = &smmu_domain->cfg; unsigned long quirks = 0; struct iommu_group *group; struct io_pgtable *iop; mutex_lock(&smmu_domain->init_mutex); if (smmu_domain->smmu) goto out_unlock; group = iommu_group_get(dev); ret = iommu_logger_register(&smmu_domain->logger, domain, group); iommu_group_put(group); if (ret) goto out_unlock; if (domain->type == IOMMU_DOMAIN_DMA) { ret = arm_smmu_setup_default_domain(dev, domain); if (ret) { dev_err(dev, "%s: default domain setup failed\n", __func__); goto out_logger; goto out_unlock; } } Loading Loading @@ -1893,7 +1888,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, if (cfg->fmt == ARM_SMMU_CTX_FMT_NONE) { ret = -EINVAL; goto out_logger; goto out_unlock; } switch (smmu_domain->stage) { Loading Loading @@ -1941,7 +1936,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, break; default: ret = -EINVAL; goto out_logger; goto out_unlock; } #ifdef CONFIG_IOMMU_IO_PGTABLE_FAST Loading @@ -1955,7 +1950,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, ret = arm_smmu_alloc_cb(domain, smmu, dev); if (ret < 0) goto out_logger; goto out_unlock; cfg->cbndx = ret; Loading Loading @@ -1985,6 +1980,13 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, goto out_clear_smmu; } group = iommu_group_get_for_dev(smmu_domain->dev); iop = container_of(smmu_domain->pgtbl_ops, struct io_pgtable, ops); ret = iommu_logger_register(&smmu_domain->logger, domain, group, iop); iommu_group_put(group); if (ret) goto out_clear_smmu; /* * assign any page table memory that might have been allocated * during alloc_io_pgtable_ops Loading @@ -1998,23 +2000,23 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, domain->geometry.aperture_end = (1UL << ias) - 1; ret = arm_smmu_adjust_domain_geometry(dev, domain); if (ret) goto out_clear_smmu; goto out_logger; domain->geometry.force_aperture = true; if (domain->type == IOMMU_DOMAIN_DMA) { ret = arm_smmu_get_dma_cookie(dev, smmu_domain); if (ret) goto out_clear_smmu; goto out_logger; } /* Assign an asid */ ret = arm_smmu_init_asid(domain, smmu); if (ret) goto out_clear_smmu; goto out_logger; ret = arm_smmu_setup_context_bank(smmu_domain, smmu, dev); if (ret) goto out_clear_smmu; goto out_logger; strlcpy(smmu_domain->domain.name, dev_name(dev), sizeof(smmu_domain->domain.name)); Loading @@ -2022,12 +2024,12 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, return 0; out_clear_smmu: arm_smmu_destroy_domain_context(domain); smmu_domain->smmu = NULL; out_logger: iommu_logger_unregister(smmu_domain->logger); smmu_domain->logger = NULL; out_clear_smmu: arm_smmu_destroy_domain_context(domain); smmu_domain->smmu = NULL; out_unlock: mutex_unlock(&smmu_domain->init_mutex); return ret; Loading drivers/iommu/iommu-logger.c +98 −18 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ * Copyright (c) 2020, The Linux Foundation. All rights reserved. */ #include <linux/bitfield.h> #include <linux/module.h> #include <linux/iommu.h> #include <linux/slab.h> Loading @@ -12,47 +13,126 @@ static DEFINE_MUTEX(iommu_debug_attachments_lock); static LIST_HEAD(iommu_debug_attachments); /* * 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 iommu_group *group; struct list_head list; }; static unsigned int iommu_logger_pgtable_levels(struct iommu_domain *domain, struct io_pgtable *iop) { unsigned int va_bits, pte_size, bits_per_level, pg_shift; unsigned long ias = iop->cfg.ias; switch (iop->fmt) { case ARM_32_LPAE_S1: case ARM_64_LPAE_S1: #ifdef CONFIG_IOMMU_IO_PGTABLE_FAST case ARM_V8L_FAST: #endif pte_size = sizeof(u64); break; default: return 0; } pg_shift = __ffs(domain->pgsize_bitmap); bits_per_level = pg_shift - ilog2(pte_size); va_bits = ias - pg_shift; return DIV_ROUND_UP(va_bits, bits_per_level); } static enum iommu_logger_pgtable_fmt iommu_logger_pgtable_fmt_lut( enum io_pgtable_fmt fmt) { switch (fmt) { case ARM_32_LPAE_S1: return IOMMU_LOGGER_ARM_32_LPAE_S1; case ARM_64_LPAE_S1: #ifdef CONFIG_IOMMU_IO_PGTABLE_FAST case ARM_V8L_FAST: #endif return IOMMU_LOGGER_ARM_64_LPAE_S1; default: return IOMMU_LOGGER_MAX_PGTABLE_FMTS; } } static int iommu_logger_domain_ttbrs(struct io_pgtable *iop, void **ttbr0_ptr, void **ttbr1_ptr) { int ret; u64 ttbr0, ttbr1; switch (iop->fmt) { case ARM_32_LPAE_S1: case ARM_64_LPAE_S1: ttbr0 = iop->cfg.arm_lpae_s1_cfg.ttbr[0]; ttbr1 = iop->cfg.arm_lpae_s1_cfg.ttbr[1]; ret = 0; break; #ifdef CONFIG_IOMMU_IO_PGTABLE_FAST case ARM_V8L_FAST: ttbr0 = iop->cfg.av8l_fast_cfg.ttbr[0]; ttbr1 = iop->cfg.av8l_fast_cfg.ttbr[1]; ret = 0; break; #endif default: ret = -EINVAL; } if (!ret) { *ttbr0_ptr = phys_to_virt(ttbr0); *ttbr1_ptr = ttbr1 ? phys_to_virt(ttbr1) : NULL; } return ret; } static struct iommu_debug_attachment *iommu_logger_init( struct iommu_domain *domain, struct iommu_group *group) struct iommu_group *group, struct io_pgtable *iop) { struct iommu_debug_attachment *logger; unsigned int levels = iommu_logger_pgtable_levels(domain, iop); enum iommu_logger_pgtable_fmt fmt = iommu_logger_pgtable_fmt_lut( iop->fmt); void *ttbr0, *ttbr1; int ret; if (!levels || fmt == IOMMU_LOGGER_MAX_PGTABLE_FMTS) return ERR_PTR(-EINVAL); ret = iommu_logger_domain_ttbrs(iop, &ttbr0, &ttbr1); if (ret) return ERR_PTR(ret); logger = kzalloc(sizeof(*logger), GFP_KERNEL); if (!logger) return NULL; return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&logger->list); logger->domain = domain; logger->group = group; logger->fmt = fmt; logger->levels = levels; logger->ttbr0 = ttbr0; logger->ttbr1 = ttbr1; return logger; } int iommu_logger_register(struct iommu_debug_attachment **logger_out, struct iommu_domain *domain, struct iommu_group *group) struct iommu_group *group, struct io_pgtable *iop) { struct iommu_debug_attachment *logger; if (!logger_out) if (!logger_out || !domain || !group || !iop || iop->fmt >= IO_PGTABLE_NUM_FMTS) return -EINVAL; logger = iommu_logger_init(domain, group); if (!logger) return -ENOMEM; logger = iommu_logger_init(domain, group, iop); if (IS_ERR(logger)) return PTR_ERR(logger); mutex_lock(&iommu_debug_attachments_lock); list_add(&logger->list, &iommu_debug_attachments); Loading drivers/iommu/iommu-logger.h +31 −3 Original line number Diff line number Diff line Loading @@ -4,18 +4,46 @@ #ifndef __LINUX_QTI_IOMMU_LOGGER_H #define __LINUX_QTI_IOMMU_LOGGER_H struct iommu_debug_attachment; #include <linux/io-pgtable.h> enum iommu_logger_pgtable_fmt { IOMMU_LOGGER_ARM_32_LPAE_S1, IOMMU_LOGGER_ARM_64_LPAE_S1, IOMMU_LOGGER_MAX_PGTABLE_FMTS, }; /* * 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 iommu_group *group; enum iommu_logger_pgtable_fmt fmt; unsigned int levels; /* * Virtual addresses of the top-level page tables are stored here, * as they are more useful for debug tools than physical addresses. */ void *ttbr0; void *ttbr1; struct list_head list; }; #if IS_ENABLED(CONFIG_QTI_IOMMU_SUPPORT) int iommu_logger_register(struct iommu_debug_attachment **a, struct iommu_domain *domain, struct iommu_group *group); struct iommu_group *group, struct io_pgtable *iop); void iommu_logger_unregister(struct iommu_debug_attachment *a); #else static inline int iommu_logger_register(struct iommu_debug_attachment **a, struct iommu_domain *domain, struct iommu_group *group) struct iommu_group *group, struct io_pgtable *iop) { return 0; } Loading Loading
drivers/iommu/arm-smmu.c +19 −17 Original line number Diff line number Diff line Loading @@ -1820,23 +1820,18 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, struct arm_smmu_cfg *cfg = &smmu_domain->cfg; unsigned long quirks = 0; struct iommu_group *group; struct io_pgtable *iop; mutex_lock(&smmu_domain->init_mutex); if (smmu_domain->smmu) goto out_unlock; group = iommu_group_get(dev); ret = iommu_logger_register(&smmu_domain->logger, domain, group); iommu_group_put(group); if (ret) goto out_unlock; if (domain->type == IOMMU_DOMAIN_DMA) { ret = arm_smmu_setup_default_domain(dev, domain); if (ret) { dev_err(dev, "%s: default domain setup failed\n", __func__); goto out_logger; goto out_unlock; } } Loading Loading @@ -1893,7 +1888,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, if (cfg->fmt == ARM_SMMU_CTX_FMT_NONE) { ret = -EINVAL; goto out_logger; goto out_unlock; } switch (smmu_domain->stage) { Loading Loading @@ -1941,7 +1936,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, break; default: ret = -EINVAL; goto out_logger; goto out_unlock; } #ifdef CONFIG_IOMMU_IO_PGTABLE_FAST Loading @@ -1955,7 +1950,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, ret = arm_smmu_alloc_cb(domain, smmu, dev); if (ret < 0) goto out_logger; goto out_unlock; cfg->cbndx = ret; Loading Loading @@ -1985,6 +1980,13 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, goto out_clear_smmu; } group = iommu_group_get_for_dev(smmu_domain->dev); iop = container_of(smmu_domain->pgtbl_ops, struct io_pgtable, ops); ret = iommu_logger_register(&smmu_domain->logger, domain, group, iop); iommu_group_put(group); if (ret) goto out_clear_smmu; /* * assign any page table memory that might have been allocated * during alloc_io_pgtable_ops Loading @@ -1998,23 +2000,23 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, domain->geometry.aperture_end = (1UL << ias) - 1; ret = arm_smmu_adjust_domain_geometry(dev, domain); if (ret) goto out_clear_smmu; goto out_logger; domain->geometry.force_aperture = true; if (domain->type == IOMMU_DOMAIN_DMA) { ret = arm_smmu_get_dma_cookie(dev, smmu_domain); if (ret) goto out_clear_smmu; goto out_logger; } /* Assign an asid */ ret = arm_smmu_init_asid(domain, smmu); if (ret) goto out_clear_smmu; goto out_logger; ret = arm_smmu_setup_context_bank(smmu_domain, smmu, dev); if (ret) goto out_clear_smmu; goto out_logger; strlcpy(smmu_domain->domain.name, dev_name(dev), sizeof(smmu_domain->domain.name)); Loading @@ -2022,12 +2024,12 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, return 0; out_clear_smmu: arm_smmu_destroy_domain_context(domain); smmu_domain->smmu = NULL; out_logger: iommu_logger_unregister(smmu_domain->logger); smmu_domain->logger = NULL; out_clear_smmu: arm_smmu_destroy_domain_context(domain); smmu_domain->smmu = NULL; out_unlock: mutex_unlock(&smmu_domain->init_mutex); return ret; Loading
drivers/iommu/iommu-logger.c +98 −18 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ * Copyright (c) 2020, The Linux Foundation. All rights reserved. */ #include <linux/bitfield.h> #include <linux/module.h> #include <linux/iommu.h> #include <linux/slab.h> Loading @@ -12,47 +13,126 @@ static DEFINE_MUTEX(iommu_debug_attachments_lock); static LIST_HEAD(iommu_debug_attachments); /* * 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 iommu_group *group; struct list_head list; }; static unsigned int iommu_logger_pgtable_levels(struct iommu_domain *domain, struct io_pgtable *iop) { unsigned int va_bits, pte_size, bits_per_level, pg_shift; unsigned long ias = iop->cfg.ias; switch (iop->fmt) { case ARM_32_LPAE_S1: case ARM_64_LPAE_S1: #ifdef CONFIG_IOMMU_IO_PGTABLE_FAST case ARM_V8L_FAST: #endif pte_size = sizeof(u64); break; default: return 0; } pg_shift = __ffs(domain->pgsize_bitmap); bits_per_level = pg_shift - ilog2(pte_size); va_bits = ias - pg_shift; return DIV_ROUND_UP(va_bits, bits_per_level); } static enum iommu_logger_pgtable_fmt iommu_logger_pgtable_fmt_lut( enum io_pgtable_fmt fmt) { switch (fmt) { case ARM_32_LPAE_S1: return IOMMU_LOGGER_ARM_32_LPAE_S1; case ARM_64_LPAE_S1: #ifdef CONFIG_IOMMU_IO_PGTABLE_FAST case ARM_V8L_FAST: #endif return IOMMU_LOGGER_ARM_64_LPAE_S1; default: return IOMMU_LOGGER_MAX_PGTABLE_FMTS; } } static int iommu_logger_domain_ttbrs(struct io_pgtable *iop, void **ttbr0_ptr, void **ttbr1_ptr) { int ret; u64 ttbr0, ttbr1; switch (iop->fmt) { case ARM_32_LPAE_S1: case ARM_64_LPAE_S1: ttbr0 = iop->cfg.arm_lpae_s1_cfg.ttbr[0]; ttbr1 = iop->cfg.arm_lpae_s1_cfg.ttbr[1]; ret = 0; break; #ifdef CONFIG_IOMMU_IO_PGTABLE_FAST case ARM_V8L_FAST: ttbr0 = iop->cfg.av8l_fast_cfg.ttbr[0]; ttbr1 = iop->cfg.av8l_fast_cfg.ttbr[1]; ret = 0; break; #endif default: ret = -EINVAL; } if (!ret) { *ttbr0_ptr = phys_to_virt(ttbr0); *ttbr1_ptr = ttbr1 ? phys_to_virt(ttbr1) : NULL; } return ret; } static struct iommu_debug_attachment *iommu_logger_init( struct iommu_domain *domain, struct iommu_group *group) struct iommu_group *group, struct io_pgtable *iop) { struct iommu_debug_attachment *logger; unsigned int levels = iommu_logger_pgtable_levels(domain, iop); enum iommu_logger_pgtable_fmt fmt = iommu_logger_pgtable_fmt_lut( iop->fmt); void *ttbr0, *ttbr1; int ret; if (!levels || fmt == IOMMU_LOGGER_MAX_PGTABLE_FMTS) return ERR_PTR(-EINVAL); ret = iommu_logger_domain_ttbrs(iop, &ttbr0, &ttbr1); if (ret) return ERR_PTR(ret); logger = kzalloc(sizeof(*logger), GFP_KERNEL); if (!logger) return NULL; return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&logger->list); logger->domain = domain; logger->group = group; logger->fmt = fmt; logger->levels = levels; logger->ttbr0 = ttbr0; logger->ttbr1 = ttbr1; return logger; } int iommu_logger_register(struct iommu_debug_attachment **logger_out, struct iommu_domain *domain, struct iommu_group *group) struct iommu_group *group, struct io_pgtable *iop) { struct iommu_debug_attachment *logger; if (!logger_out) if (!logger_out || !domain || !group || !iop || iop->fmt >= IO_PGTABLE_NUM_FMTS) return -EINVAL; logger = iommu_logger_init(domain, group); if (!logger) return -ENOMEM; logger = iommu_logger_init(domain, group, iop); if (IS_ERR(logger)) return PTR_ERR(logger); mutex_lock(&iommu_debug_attachments_lock); list_add(&logger->list, &iommu_debug_attachments); Loading
drivers/iommu/iommu-logger.h +31 −3 Original line number Diff line number Diff line Loading @@ -4,18 +4,46 @@ #ifndef __LINUX_QTI_IOMMU_LOGGER_H #define __LINUX_QTI_IOMMU_LOGGER_H struct iommu_debug_attachment; #include <linux/io-pgtable.h> enum iommu_logger_pgtable_fmt { IOMMU_LOGGER_ARM_32_LPAE_S1, IOMMU_LOGGER_ARM_64_LPAE_S1, IOMMU_LOGGER_MAX_PGTABLE_FMTS, }; /* * 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 iommu_group *group; enum iommu_logger_pgtable_fmt fmt; unsigned int levels; /* * Virtual addresses of the top-level page tables are stored here, * as they are more useful for debug tools than physical addresses. */ void *ttbr0; void *ttbr1; struct list_head list; }; #if IS_ENABLED(CONFIG_QTI_IOMMU_SUPPORT) int iommu_logger_register(struct iommu_debug_attachment **a, struct iommu_domain *domain, struct iommu_group *group); struct iommu_group *group, struct io_pgtable *iop); void iommu_logger_unregister(struct iommu_debug_attachment *a); #else static inline int iommu_logger_register(struct iommu_debug_attachment **a, struct iommu_domain *domain, struct iommu_group *group) struct iommu_group *group, struct io_pgtable *iop) { return 0; } Loading