Skip to content

Commit 57f98d2

Browse files
rmurphy-armwildea01
authored andcommitted
iommu: Introduce iommu_fwspec
Introduce a common structure to hold the per-device firmware data that most IOMMU drivers need to keep track of. This enables us to configure much of that data from common firmware code, and consolidate a lot of the equivalent implementations, device look-up tables, etc. which are currently strewn across IOMMU drivers. This will also be enable us to address the outstanding "multiple IOMMUs on the platform bus" problem by tweaking IOMMU API calls to prefer dev->fwspec->ops before falling back to dev->bus->iommu_ops, and thus gracefully handle those troublesome systems which we currently cannot. As the first user, hook up the OF IOMMU configuration mechanism. The driver-defined nature of DT cells means that we still need the drivers to translate and add the IDs themselves, but future users such as the much less free-form ACPI IORT will be much simpler and self-contained. CC: Greg Kroah-Hartman <[email protected]> Suggested-by: Will Deacon <[email protected]> Signed-off-by: Robin Murphy <[email protected]> Signed-off-by: Will Deacon <[email protected]>
1 parent b996444 commit 57f98d2

File tree

4 files changed

+106
-2
lines changed

4 files changed

+106
-2
lines changed

drivers/iommu/iommu.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <linux/err.h>
3232
#include <linux/pci.h>
3333
#include <linux/bitops.h>
34+
#include <linux/property.h>
3435
#include <trace/events/iommu.h>
3536

3637
static struct kset *iommu_group_kset;
@@ -1613,3 +1614,60 @@ int iommu_request_dm_for_dev(struct device *dev)
16131614

16141615
return ret;
16151616
}
1617+
1618+
int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
1619+
const struct iommu_ops *ops)
1620+
{
1621+
struct iommu_fwspec *fwspec = dev->iommu_fwspec;
1622+
1623+
if (fwspec)
1624+
return ops == fwspec->ops ? 0 : -EINVAL;
1625+
1626+
fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL);
1627+
if (!fwspec)
1628+
return -ENOMEM;
1629+
1630+
of_node_get(to_of_node(iommu_fwnode));
1631+
fwspec->iommu_fwnode = iommu_fwnode;
1632+
fwspec->ops = ops;
1633+
dev->iommu_fwspec = fwspec;
1634+
return 0;
1635+
}
1636+
EXPORT_SYMBOL_GPL(iommu_fwspec_init);
1637+
1638+
void iommu_fwspec_free(struct device *dev)
1639+
{
1640+
struct iommu_fwspec *fwspec = dev->iommu_fwspec;
1641+
1642+
if (fwspec) {
1643+
fwnode_handle_put(fwspec->iommu_fwnode);
1644+
kfree(fwspec);
1645+
dev->iommu_fwspec = NULL;
1646+
}
1647+
}
1648+
EXPORT_SYMBOL_GPL(iommu_fwspec_free);
1649+
1650+
int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
1651+
{
1652+
struct iommu_fwspec *fwspec = dev->iommu_fwspec;
1653+
size_t size;
1654+
int i;
1655+
1656+
if (!fwspec)
1657+
return -EINVAL;
1658+
1659+
size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]);
1660+
if (size > sizeof(*fwspec)) {
1661+
fwspec = krealloc(dev->iommu_fwspec, size, GFP_KERNEL);
1662+
if (!fwspec)
1663+
return -ENOMEM;
1664+
}
1665+
1666+
for (i = 0; i < num_ids; i++)
1667+
fwspec->ids[fwspec->num_ids + i] = ids[i];
1668+
1669+
fwspec->num_ids += num_ids;
1670+
dev->iommu_fwspec = fwspec;
1671+
return 0;
1672+
}
1673+
EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids);

drivers/iommu/of_iommu.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,9 @@ static const struct iommu_ops
167167
return NULL;
168168

169169
ops = of_iommu_get_ops(iommu_spec.np);
170-
if (!ops || !ops->of_xlate || ops->of_xlate(&pdev->dev, &iommu_spec))
170+
if (!ops || !ops->of_xlate ||
171+
iommu_fwspec_init(&pdev->dev, &iommu_spec.np->fwnode, ops) ||
172+
ops->of_xlate(&pdev->dev, &iommu_spec))
171173
ops = NULL;
172174

173175
of_node_put(iommu_spec.np);
@@ -196,7 +198,9 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
196198
np = iommu_spec.np;
197199
ops = of_iommu_get_ops(np);
198200

199-
if (!ops || !ops->of_xlate || ops->of_xlate(dev, &iommu_spec))
201+
if (!ops || !ops->of_xlate ||
202+
iommu_fwspec_init(dev, &np->fwnode, ops) ||
203+
ops->of_xlate(dev, &iommu_spec))
200204
goto err_put_node;
201205

202206
of_node_put(np);

include/linux/device.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ struct device_node;
4141
struct fwnode_handle;
4242
struct iommu_ops;
4343
struct iommu_group;
44+
struct iommu_fwspec;
4445

4546
struct bus_attribute {
4647
struct attribute attr;
@@ -765,6 +766,7 @@ struct device_dma_parameters {
765766
* gone away. This should be set by the allocator of the
766767
* device (i.e. the bus driver that discovered the device).
767768
* @iommu_group: IOMMU group the device belongs to.
769+
* @iommu_fwspec: IOMMU-specific properties supplied by firmware.
768770
*
769771
* @offline_disabled: If set, the device is permanently online.
770772
* @offline: Set after successful invocation of bus type's .offline().
@@ -849,6 +851,7 @@ struct device {
849851

850852
void (*release)(struct device *dev);
851853
struct iommu_group *iommu_group;
854+
struct iommu_fwspec *iommu_fwspec;
852855

853856
bool offline_disabled:1;
854857
bool offline:1;

include/linux/iommu.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,10 +331,32 @@ extern struct iommu_group *pci_device_group(struct device *dev);
331331
/* Generic device grouping function */
332332
extern struct iommu_group *generic_device_group(struct device *dev);
333333

334+
/**
335+
* struct iommu_fwspec - per-device IOMMU instance data
336+
* @ops: ops for this device's IOMMU
337+
* @iommu_fwnode: firmware handle for this device's IOMMU
338+
* @iommu_priv: IOMMU driver private data for this device
339+
* @num_ids: number of associated device IDs
340+
* @ids: IDs which this device may present to the IOMMU
341+
*/
342+
struct iommu_fwspec {
343+
const struct iommu_ops *ops;
344+
struct fwnode_handle *iommu_fwnode;
345+
void *iommu_priv;
346+
unsigned int num_ids;
347+
u32 ids[1];
348+
};
349+
350+
int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
351+
const struct iommu_ops *ops);
352+
void iommu_fwspec_free(struct device *dev);
353+
int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
354+
334355
#else /* CONFIG_IOMMU_API */
335356

336357
struct iommu_ops {};
337358
struct iommu_group {};
359+
struct iommu_fwspec {};
338360

339361
static inline bool iommu_present(struct bus_type *bus)
340362
{
@@ -541,6 +563,23 @@ static inline void iommu_device_unlink(struct device *dev, struct device *link)
541563
{
542564
}
543565

566+
static inline int iommu_fwspec_init(struct device *dev,
567+
struct fwnode_handle *iommu_fwnode,
568+
const struct iommu_ops *ops)
569+
{
570+
return -ENODEV;
571+
}
572+
573+
static inline void iommu_fwspec_free(struct device *dev)
574+
{
575+
}
576+
577+
static inline int iommu_fwspec_add_ids(struct device *dev, u32 *ids,
578+
int num_ids)
579+
{
580+
return -ENODEV;
581+
}
582+
544583
#endif /* CONFIG_IOMMU_API */
545584

546585
#endif /* __LINUX_IOMMU_H */

0 commit comments

Comments
 (0)