diff options
Diffstat (limited to 'drivers/iommu/iommu.c')
| -rw-r--r-- | drivers/iommu/iommu.c | 113 |
1 files changed, 58 insertions, 55 deletions
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index edbdf5d6962c..3ed4db334341 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c | |||
| @@ -22,7 +22,8 @@ | |||
| 22 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
| 23 | #include <linux/bug.h> | 23 | #include <linux/bug.h> |
| 24 | #include <linux/types.h> | 24 | #include <linux/types.h> |
| 25 | #include <linux/module.h> | 25 | #include <linux/init.h> |
| 26 | #include <linux/export.h> | ||
| 26 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
| 27 | #include <linux/errno.h> | 28 | #include <linux/errno.h> |
| 28 | #include <linux/iommu.h> | 29 | #include <linux/iommu.h> |
| @@ -110,6 +111,27 @@ void iommu_device_unregister(struct iommu_device *iommu) | |||
| 110 | spin_unlock(&iommu_device_lock); | 111 | spin_unlock(&iommu_device_lock); |
| 111 | } | 112 | } |
| 112 | 113 | ||
| 114 | int iommu_probe_device(struct device *dev) | ||
| 115 | { | ||
| 116 | const struct iommu_ops *ops = dev->bus->iommu_ops; | ||
| 117 | int ret = -EINVAL; | ||
| 118 | |||
| 119 | WARN_ON(dev->iommu_group); | ||
| 120 | |||
| 121 | if (ops) | ||
| 122 | ret = ops->add_device(dev); | ||
| 123 | |||
| 124 | return ret; | ||
| 125 | } | ||
| 126 | |||
| 127 | void iommu_release_device(struct device *dev) | ||
| 128 | { | ||
| 129 | const struct iommu_ops *ops = dev->bus->iommu_ops; | ||
| 130 | |||
| 131 | if (dev->iommu_group) | ||
| 132 | ops->remove_device(dev); | ||
| 133 | } | ||
| 134 | |||
| 113 | static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus, | 135 | static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus, |
| 114 | unsigned type); | 136 | unsigned type); |
| 115 | static int __iommu_attach_device(struct iommu_domain *domain, | 137 | static int __iommu_attach_device(struct iommu_domain *domain, |
| @@ -1117,16 +1139,7 @@ struct iommu_domain *iommu_group_default_domain(struct iommu_group *group) | |||
| 1117 | 1139 | ||
| 1118 | static int add_iommu_group(struct device *dev, void *data) | 1140 | static int add_iommu_group(struct device *dev, void *data) |
| 1119 | { | 1141 | { |
| 1120 | struct iommu_callback_data *cb = data; | 1142 | int ret = iommu_probe_device(dev); |
| 1121 | const struct iommu_ops *ops = cb->ops; | ||
| 1122 | int ret; | ||
| 1123 | |||
| 1124 | if (!ops->add_device) | ||
| 1125 | return 0; | ||
| 1126 | |||
| 1127 | WARN_ON(dev->iommu_group); | ||
| 1128 | |||
| 1129 | ret = ops->add_device(dev); | ||
| 1130 | 1143 | ||
| 1131 | /* | 1144 | /* |
| 1132 | * We ignore -ENODEV errors for now, as they just mean that the | 1145 | * We ignore -ENODEV errors for now, as they just mean that the |
| @@ -1141,11 +1154,7 @@ static int add_iommu_group(struct device *dev, void *data) | |||
| 1141 | 1154 | ||
| 1142 | static int remove_iommu_group(struct device *dev, void *data) | 1155 | static int remove_iommu_group(struct device *dev, void *data) |
| 1143 | { | 1156 | { |
| 1144 | struct iommu_callback_data *cb = data; | 1157 | iommu_release_device(dev); |
| 1145 | const struct iommu_ops *ops = cb->ops; | ||
| 1146 | |||
| 1147 | if (ops->remove_device && dev->iommu_group) | ||
| 1148 | ops->remove_device(dev); | ||
| 1149 | 1158 | ||
| 1150 | return 0; | 1159 | return 0; |
| 1151 | } | 1160 | } |
| @@ -1153,27 +1162,22 @@ static int remove_iommu_group(struct device *dev, void *data) | |||
| 1153 | static int iommu_bus_notifier(struct notifier_block *nb, | 1162 | static int iommu_bus_notifier(struct notifier_block *nb, |
| 1154 | unsigned long action, void *data) | 1163 | unsigned long action, void *data) |
| 1155 | { | 1164 | { |
| 1165 | unsigned long group_action = 0; | ||
| 1156 | struct device *dev = data; | 1166 | struct device *dev = data; |
| 1157 | const struct iommu_ops *ops = dev->bus->iommu_ops; | ||
| 1158 | struct iommu_group *group; | 1167 | struct iommu_group *group; |
| 1159 | unsigned long group_action = 0; | ||
| 1160 | 1168 | ||
| 1161 | /* | 1169 | /* |
| 1162 | * ADD/DEL call into iommu driver ops if provided, which may | 1170 | * ADD/DEL call into iommu driver ops if provided, which may |
| 1163 | * result in ADD/DEL notifiers to group->notifier | 1171 | * result in ADD/DEL notifiers to group->notifier |
| 1164 | */ | 1172 | */ |
| 1165 | if (action == BUS_NOTIFY_ADD_DEVICE) { | 1173 | if (action == BUS_NOTIFY_ADD_DEVICE) { |
| 1166 | if (ops->add_device) { | 1174 | int ret; |
| 1167 | int ret; | ||
| 1168 | 1175 | ||
| 1169 | ret = ops->add_device(dev); | 1176 | ret = iommu_probe_device(dev); |
| 1170 | return (ret) ? NOTIFY_DONE : NOTIFY_OK; | 1177 | return (ret) ? NOTIFY_DONE : NOTIFY_OK; |
| 1171 | } | ||
| 1172 | } else if (action == BUS_NOTIFY_REMOVED_DEVICE) { | 1178 | } else if (action == BUS_NOTIFY_REMOVED_DEVICE) { |
| 1173 | if (ops->remove_device && dev->iommu_group) { | 1179 | iommu_release_device(dev); |
| 1174 | ops->remove_device(dev); | 1180 | return NOTIFY_OK; |
| 1175 | return 0; | ||
| 1176 | } | ||
| 1177 | } | 1181 | } |
| 1178 | 1182 | ||
| 1179 | /* | 1183 | /* |
| @@ -1712,33 +1716,32 @@ EXPORT_SYMBOL_GPL(iommu_unmap_fast); | |||
| 1712 | size_t iommu_map_sg(struct iommu_domain *domain, unsigned long iova, | 1716 | size_t iommu_map_sg(struct iommu_domain *domain, unsigned long iova, |
| 1713 | struct scatterlist *sg, unsigned int nents, int prot) | 1717 | struct scatterlist *sg, unsigned int nents, int prot) |
| 1714 | { | 1718 | { |
| 1715 | struct scatterlist *s; | 1719 | size_t len = 0, mapped = 0; |
| 1716 | size_t mapped = 0; | 1720 | phys_addr_t start; |
| 1717 | unsigned int i, min_pagesz; | 1721 | unsigned int i = 0; |
| 1718 | int ret; | 1722 | int ret; |
| 1719 | 1723 | ||
| 1720 | if (unlikely(domain->pgsize_bitmap == 0UL)) | 1724 | while (i <= nents) { |
| 1721 | return 0; | 1725 | phys_addr_t s_phys = sg_phys(sg); |
| 1722 | 1726 | ||
| 1723 | min_pagesz = 1 << __ffs(domain->pgsize_bitmap); | 1727 | if (len && s_phys != start + len) { |
| 1724 | 1728 | ret = iommu_map(domain, iova + mapped, start, len, prot); | |
| 1725 | for_each_sg(sg, s, nents, i) { | 1729 | if (ret) |
| 1726 | phys_addr_t phys = page_to_phys(sg_page(s)) + s->offset; | 1730 | goto out_err; |
| 1727 | 1731 | ||
| 1728 | /* | 1732 | mapped += len; |
| 1729 | * We are mapping on IOMMU page boundaries, so offset within | 1733 | len = 0; |
| 1730 | * the page must be 0. However, the IOMMU may support pages | 1734 | } |
| 1731 | * smaller than PAGE_SIZE, so s->offset may still represent | ||
| 1732 | * an offset of that boundary within the CPU page. | ||
| 1733 | */ | ||
| 1734 | if (!IS_ALIGNED(s->offset, min_pagesz)) | ||
| 1735 | goto out_err; | ||
| 1736 | 1735 | ||
| 1737 | ret = iommu_map(domain, iova + mapped, phys, s->length, prot); | 1736 | if (len) { |
| 1738 | if (ret) | 1737 | len += sg->length; |
| 1739 | goto out_err; | 1738 | } else { |
| 1739 | len = sg->length; | ||
| 1740 | start = s_phys; | ||
| 1741 | } | ||
| 1740 | 1742 | ||
| 1741 | mapped += s->length; | 1743 | if (++i < nents) |
| 1744 | sg = sg_next(sg); | ||
| 1742 | } | 1745 | } |
| 1743 | 1746 | ||
| 1744 | return mapped; | 1747 | return mapped; |
| @@ -1976,7 +1979,7 @@ const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode) | |||
| 1976 | int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode, | 1979 | int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode, |
| 1977 | const struct iommu_ops *ops) | 1980 | const struct iommu_ops *ops) |
| 1978 | { | 1981 | { |
| 1979 | struct iommu_fwspec *fwspec = dev->iommu_fwspec; | 1982 | struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); |
| 1980 | 1983 | ||
| 1981 | if (fwspec) | 1984 | if (fwspec) |
| 1982 | return ops == fwspec->ops ? 0 : -EINVAL; | 1985 | return ops == fwspec->ops ? 0 : -EINVAL; |
| @@ -1988,26 +1991,26 @@ int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode, | |||
| 1988 | of_node_get(to_of_node(iommu_fwnode)); | 1991 | of_node_get(to_of_node(iommu_fwnode)); |
| 1989 | fwspec->iommu_fwnode = iommu_fwnode; | 1992 | fwspec->iommu_fwnode = iommu_fwnode; |
| 1990 | fwspec->ops = ops; | 1993 | fwspec->ops = ops; |
| 1991 | dev->iommu_fwspec = fwspec; | 1994 | dev_iommu_fwspec_set(dev, fwspec); |
| 1992 | return 0; | 1995 | return 0; |
| 1993 | } | 1996 | } |
| 1994 | EXPORT_SYMBOL_GPL(iommu_fwspec_init); | 1997 | EXPORT_SYMBOL_GPL(iommu_fwspec_init); |
| 1995 | 1998 | ||
| 1996 | void iommu_fwspec_free(struct device *dev) | 1999 | void iommu_fwspec_free(struct device *dev) |
| 1997 | { | 2000 | { |
| 1998 | struct iommu_fwspec *fwspec = dev->iommu_fwspec; | 2001 | struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); |
| 1999 | 2002 | ||
| 2000 | if (fwspec) { | 2003 | if (fwspec) { |
| 2001 | fwnode_handle_put(fwspec->iommu_fwnode); | 2004 | fwnode_handle_put(fwspec->iommu_fwnode); |
| 2002 | kfree(fwspec); | 2005 | kfree(fwspec); |
| 2003 | dev->iommu_fwspec = NULL; | 2006 | dev_iommu_fwspec_set(dev, NULL); |
| 2004 | } | 2007 | } |
| 2005 | } | 2008 | } |
| 2006 | EXPORT_SYMBOL_GPL(iommu_fwspec_free); | 2009 | EXPORT_SYMBOL_GPL(iommu_fwspec_free); |
| 2007 | 2010 | ||
| 2008 | int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids) | 2011 | int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids) |
| 2009 | { | 2012 | { |
| 2010 | struct iommu_fwspec *fwspec = dev->iommu_fwspec; | 2013 | struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); |
| 2011 | size_t size; | 2014 | size_t size; |
| 2012 | int i; | 2015 | int i; |
| 2013 | 2016 | ||
| @@ -2016,11 +2019,11 @@ int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids) | |||
| 2016 | 2019 | ||
| 2017 | size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]); | 2020 | size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]); |
| 2018 | if (size > sizeof(*fwspec)) { | 2021 | if (size > sizeof(*fwspec)) { |
| 2019 | fwspec = krealloc(dev->iommu_fwspec, size, GFP_KERNEL); | 2022 | fwspec = krealloc(fwspec, size, GFP_KERNEL); |
| 2020 | if (!fwspec) | 2023 | if (!fwspec) |
| 2021 | return -ENOMEM; | 2024 | return -ENOMEM; |
| 2022 | 2025 | ||
| 2023 | dev->iommu_fwspec = fwspec; | 2026 | dev_iommu_fwspec_set(dev, fwspec); |
| 2024 | } | 2027 | } |
| 2025 | 2028 | ||
| 2026 | for (i = 0; i < num_ids; i++) | 2029 | for (i = 0; i < num_ids; i++) |
