diff options
Diffstat (limited to 'drivers/iommu/iommu.c')
-rw-r--r-- | drivers/iommu/iommu.c | 114 |
1 files changed, 89 insertions, 25 deletions
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 6e6b6a11b3ce..2fb2963df553 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c | |||
@@ -16,6 +16,8 @@ | |||
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
17 | */ | 17 | */ |
18 | 18 | ||
19 | #include <linux/device.h> | ||
20 | #include <linux/kernel.h> | ||
19 | #include <linux/bug.h> | 21 | #include <linux/bug.h> |
20 | #include <linux/types.h> | 22 | #include <linux/types.h> |
21 | #include <linux/module.h> | 23 | #include <linux/module.h> |
@@ -23,32 +25,78 @@ | |||
23 | #include <linux/errno.h> | 25 | #include <linux/errno.h> |
24 | #include <linux/iommu.h> | 26 | #include <linux/iommu.h> |
25 | 27 | ||
26 | static struct iommu_ops *iommu_ops; | 28 | static void iommu_bus_init(struct bus_type *bus, struct iommu_ops *ops) |
29 | { | ||
30 | } | ||
27 | 31 | ||
28 | void register_iommu(struct iommu_ops *ops) | 32 | /** |
33 | * bus_set_iommu - set iommu-callbacks for the bus | ||
34 | * @bus: bus. | ||
35 | * @ops: the callbacks provided by the iommu-driver | ||
36 | * | ||
37 | * This function is called by an iommu driver to set the iommu methods | ||
38 | * used for a particular bus. Drivers for devices on that bus can use | ||
39 | * the iommu-api after these ops are registered. | ||
40 | * This special function is needed because IOMMUs are usually devices on | ||
41 | * the bus itself, so the iommu drivers are not initialized when the bus | ||
42 | * is set up. With this function the iommu-driver can set the iommu-ops | ||
43 | * afterwards. | ||
44 | */ | ||
45 | int bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops) | ||
29 | { | 46 | { |
30 | if (iommu_ops) | 47 | if (bus->iommu_ops != NULL) |
31 | BUG(); | 48 | return -EBUSY; |
49 | |||
50 | bus->iommu_ops = ops; | ||
51 | |||
52 | /* Do IOMMU specific setup for this bus-type */ | ||
53 | iommu_bus_init(bus, ops); | ||
32 | 54 | ||
33 | iommu_ops = ops; | 55 | return 0; |
34 | } | 56 | } |
57 | EXPORT_SYMBOL_GPL(bus_set_iommu); | ||
35 | 58 | ||
36 | bool iommu_found(void) | 59 | bool iommu_present(struct bus_type *bus) |
37 | { | 60 | { |
38 | return iommu_ops != NULL; | 61 | return bus->iommu_ops != NULL; |
39 | } | 62 | } |
40 | EXPORT_SYMBOL_GPL(iommu_found); | 63 | EXPORT_SYMBOL_GPL(iommu_present); |
41 | 64 | ||
42 | struct iommu_domain *iommu_domain_alloc(void) | 65 | /** |
66 | * iommu_set_fault_handler() - set a fault handler for an iommu domain | ||
67 | * @domain: iommu domain | ||
68 | * @handler: fault handler | ||
69 | * | ||
70 | * This function should be used by IOMMU users which want to be notified | ||
71 | * whenever an IOMMU fault happens. | ||
72 | * | ||
73 | * The fault handler itself should return 0 on success, and an appropriate | ||
74 | * error code otherwise. | ||
75 | */ | ||
76 | void iommu_set_fault_handler(struct iommu_domain *domain, | ||
77 | iommu_fault_handler_t handler) | ||
78 | { | ||
79 | BUG_ON(!domain); | ||
80 | |||
81 | domain->handler = handler; | ||
82 | } | ||
83 | EXPORT_SYMBOL_GPL(iommu_set_fault_handler); | ||
84 | |||
85 | struct iommu_domain *iommu_domain_alloc(struct bus_type *bus) | ||
43 | { | 86 | { |
44 | struct iommu_domain *domain; | 87 | struct iommu_domain *domain; |
45 | int ret; | 88 | int ret; |
46 | 89 | ||
90 | if (bus == NULL || bus->iommu_ops == NULL) | ||
91 | return NULL; | ||
92 | |||
47 | domain = kmalloc(sizeof(*domain), GFP_KERNEL); | 93 | domain = kmalloc(sizeof(*domain), GFP_KERNEL); |
48 | if (!domain) | 94 | if (!domain) |
49 | return NULL; | 95 | return NULL; |
50 | 96 | ||
51 | ret = iommu_ops->domain_init(domain); | 97 | domain->ops = bus->iommu_ops; |
98 | |||
99 | ret = domain->ops->domain_init(domain); | ||
52 | if (ret) | 100 | if (ret) |
53 | goto out_free; | 101 | goto out_free; |
54 | 102 | ||
@@ -63,62 +111,78 @@ EXPORT_SYMBOL_GPL(iommu_domain_alloc); | |||
63 | 111 | ||
64 | void iommu_domain_free(struct iommu_domain *domain) | 112 | void iommu_domain_free(struct iommu_domain *domain) |
65 | { | 113 | { |
66 | iommu_ops->domain_destroy(domain); | 114 | if (likely(domain->ops->domain_destroy != NULL)) |
115 | domain->ops->domain_destroy(domain); | ||
116 | |||
67 | kfree(domain); | 117 | kfree(domain); |
68 | } | 118 | } |
69 | EXPORT_SYMBOL_GPL(iommu_domain_free); | 119 | EXPORT_SYMBOL_GPL(iommu_domain_free); |
70 | 120 | ||
71 | int iommu_attach_device(struct iommu_domain *domain, struct device *dev) | 121 | int iommu_attach_device(struct iommu_domain *domain, struct device *dev) |
72 | { | 122 | { |
73 | return iommu_ops->attach_dev(domain, dev); | 123 | if (unlikely(domain->ops->attach_dev == NULL)) |
124 | return -ENODEV; | ||
125 | |||
126 | return domain->ops->attach_dev(domain, dev); | ||
74 | } | 127 | } |
75 | EXPORT_SYMBOL_GPL(iommu_attach_device); | 128 | EXPORT_SYMBOL_GPL(iommu_attach_device); |
76 | 129 | ||
77 | void iommu_detach_device(struct iommu_domain *domain, struct device *dev) | 130 | void iommu_detach_device(struct iommu_domain *domain, struct device *dev) |
78 | { | 131 | { |
79 | iommu_ops->detach_dev(domain, dev); | 132 | if (unlikely(domain->ops->detach_dev == NULL)) |
133 | return; | ||
134 | |||
135 | domain->ops->detach_dev(domain, dev); | ||
80 | } | 136 | } |
81 | EXPORT_SYMBOL_GPL(iommu_detach_device); | 137 | EXPORT_SYMBOL_GPL(iommu_detach_device); |
82 | 138 | ||
83 | phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, | 139 | phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, |
84 | unsigned long iova) | 140 | unsigned long iova) |
85 | { | 141 | { |
86 | return iommu_ops->iova_to_phys(domain, iova); | 142 | if (unlikely(domain->ops->iova_to_phys == NULL)) |
143 | return 0; | ||
144 | |||
145 | return domain->ops->iova_to_phys(domain, iova); | ||
87 | } | 146 | } |
88 | EXPORT_SYMBOL_GPL(iommu_iova_to_phys); | 147 | EXPORT_SYMBOL_GPL(iommu_iova_to_phys); |
89 | 148 | ||
90 | int iommu_domain_has_cap(struct iommu_domain *domain, | 149 | int iommu_domain_has_cap(struct iommu_domain *domain, |
91 | unsigned long cap) | 150 | unsigned long cap) |
92 | { | 151 | { |
93 | return iommu_ops->domain_has_cap(domain, cap); | 152 | if (unlikely(domain->ops->domain_has_cap == NULL)) |
153 | return 0; | ||
154 | |||
155 | return domain->ops->domain_has_cap(domain, cap); | ||
94 | } | 156 | } |
95 | EXPORT_SYMBOL_GPL(iommu_domain_has_cap); | 157 | EXPORT_SYMBOL_GPL(iommu_domain_has_cap); |
96 | 158 | ||
97 | int iommu_map(struct iommu_domain *domain, unsigned long iova, | 159 | int iommu_map(struct iommu_domain *domain, unsigned long iova, |
98 | phys_addr_t paddr, int gfp_order, int prot) | 160 | phys_addr_t paddr, int gfp_order, int prot) |
99 | { | 161 | { |
100 | unsigned long invalid_mask; | ||
101 | size_t size; | 162 | size_t size; |
102 | 163 | ||
103 | size = 0x1000UL << gfp_order; | 164 | if (unlikely(domain->ops->map == NULL)) |
104 | invalid_mask = size - 1; | 165 | return -ENODEV; |
105 | 166 | ||
106 | BUG_ON((iova | paddr) & invalid_mask); | 167 | size = PAGE_SIZE << gfp_order; |
107 | 168 | ||
108 | return iommu_ops->map(domain, iova, paddr, gfp_order, prot); | 169 | BUG_ON(!IS_ALIGNED(iova | paddr, size)); |
170 | |||
171 | return domain->ops->map(domain, iova, paddr, gfp_order, prot); | ||
109 | } | 172 | } |
110 | EXPORT_SYMBOL_GPL(iommu_map); | 173 | EXPORT_SYMBOL_GPL(iommu_map); |
111 | 174 | ||
112 | int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order) | 175 | int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order) |
113 | { | 176 | { |
114 | unsigned long invalid_mask; | ||
115 | size_t size; | 177 | size_t size; |
116 | 178 | ||
117 | size = 0x1000UL << gfp_order; | 179 | if (unlikely(domain->ops->unmap == NULL)) |
118 | invalid_mask = size - 1; | 180 | return -ENODEV; |
181 | |||
182 | size = PAGE_SIZE << gfp_order; | ||
119 | 183 | ||
120 | BUG_ON(iova & invalid_mask); | 184 | BUG_ON(!IS_ALIGNED(iova, size)); |
121 | 185 | ||
122 | return iommu_ops->unmap(domain, iova, gfp_order); | 186 | return domain->ops->unmap(domain, iova, gfp_order); |
123 | } | 187 | } |
124 | EXPORT_SYMBOL_GPL(iommu_unmap); | 188 | EXPORT_SYMBOL_GPL(iommu_unmap); |