Skip to content

Commit 512881e

Browse files
LuBaolujoergroedel
authored andcommitted
bus: platform,amba,fsl-mc,PCI: Add device DMA ownership management
The devices on platform/amba/fsl-mc/PCI buses could be bound to drivers with the device DMA managed by kernel drivers or user-space applications. Unfortunately, multiple devices may be placed in the same IOMMU group because they cannot be isolated from each other. The DMA on these devices must either be entirely under kernel control or userspace control, never a mixture. Otherwise the driver integrity is not guaranteed because they could access each other through the peer-to-peer accesses which by-pass the IOMMU protection. This checks and sets the default DMA mode during driver binding, and cleanups during driver unbinding. In the default mode, the device DMA is managed by the device driver which handles DMA operations through the kernel DMA APIs (see Documentation/core-api/dma-api.rst). For cases where the devices are assigned for userspace control through the userspace driver framework(i.e. VFIO), the drivers(for example, vfio_pci/ vfio_platfrom etc.) may set a new flag (driver_managed_dma) to skip this default setting in the assumption that the drivers know what they are doing with the device DMA. Calling iommu_device_use_default_domain() before {of,acpi}_dma_configure is currently a problem. As things stand, the IOMMU driver ignored the initial iommu_probe_device() call when the device was added, since at that point it had no fwspec yet. In this situation, {of,acpi}_iommu_configure() are retriggering iommu_probe_device() after the IOMMU driver has seen the firmware data via .of_xlate to learn that it actually responsible for the given device. As the result, before that gets fixed, iommu_use_default_domain() goes at the end, and calls arch_teardown_dma_ops() if it fails. Cc: Greg Kroah-Hartman <[email protected]> Cc: Bjorn Helgaas <[email protected]> Cc: Stuart Yoder <[email protected]> Cc: Laurentiu Tudor <[email protected]> Signed-off-by: Lu Baolu <[email protected]> Reviewed-by: Greg Kroah-Hartman <[email protected]> Reviewed-by: Jason Gunthorpe <[email protected]> Reviewed-by: Robin Murphy <[email protected]> Tested-by: Eric Auger <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Joerg Roedel <[email protected]>
1 parent 4a6d9dd commit 512881e

File tree

8 files changed

+108
-2
lines changed

8 files changed

+108
-2
lines changed

drivers/amba/bus.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include <linux/of_irq.h>
2323
#include <linux/of_device.h>
2424
#include <linux/acpi.h>
25+
#include <linux/iommu.h>
26+
#include <linux/dma-map-ops.h>
2527

2628
#define to_amba_driver(d) container_of(d, struct amba_driver, drv)
2729

@@ -277,6 +279,7 @@ static void amba_shutdown(struct device *dev)
277279

278280
static int amba_dma_configure(struct device *dev)
279281
{
282+
struct amba_driver *drv = to_amba_driver(dev->driver);
280283
enum dev_dma_attr attr;
281284
int ret = 0;
282285

@@ -287,9 +290,23 @@ static int amba_dma_configure(struct device *dev)
287290
ret = acpi_dma_configure(dev, attr);
288291
}
289292

293+
if (!ret && !drv->driver_managed_dma) {
294+
ret = iommu_device_use_default_domain(dev);
295+
if (ret)
296+
arch_teardown_dma_ops(dev);
297+
}
298+
290299
return ret;
291300
}
292301

302+
static void amba_dma_cleanup(struct device *dev)
303+
{
304+
struct amba_driver *drv = to_amba_driver(dev->driver);
305+
306+
if (!drv->driver_managed_dma)
307+
iommu_device_unuse_default_domain(dev);
308+
}
309+
293310
#ifdef CONFIG_PM
294311
/*
295312
* Hooks to provide runtime PM of the pclk (bus clock). It is safe to
@@ -359,6 +376,7 @@ struct bus_type amba_bustype = {
359376
.remove = amba_remove,
360377
.shutdown = amba_shutdown,
361378
.dma_configure = amba_dma_configure,
379+
.dma_cleanup = amba_dma_cleanup,
362380
.pm = &amba_pm,
363381
};
364382
EXPORT_SYMBOL_GPL(amba_bustype);

drivers/base/platform.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include <linux/property.h>
3131
#include <linux/kmemleak.h>
3232
#include <linux/types.h>
33+
#include <linux/iommu.h>
34+
#include <linux/dma-map-ops.h>
3335

3436
#include "base.h"
3537
#include "power/power.h"
@@ -1456,6 +1458,7 @@ static void platform_shutdown(struct device *_dev)
14561458

14571459
static int platform_dma_configure(struct device *dev)
14581460
{
1461+
struct platform_driver *drv = to_platform_driver(dev->driver);
14591462
enum dev_dma_attr attr;
14601463
int ret = 0;
14611464

@@ -1466,9 +1469,23 @@ static int platform_dma_configure(struct device *dev)
14661469
ret = acpi_dma_configure(dev, attr);
14671470
}
14681471

1472+
if (!ret && !drv->driver_managed_dma) {
1473+
ret = iommu_device_use_default_domain(dev);
1474+
if (ret)
1475+
arch_teardown_dma_ops(dev);
1476+
}
1477+
14691478
return ret;
14701479
}
14711480

1481+
static void platform_dma_cleanup(struct device *dev)
1482+
{
1483+
struct platform_driver *drv = to_platform_driver(dev->driver);
1484+
1485+
if (!drv->driver_managed_dma)
1486+
iommu_device_unuse_default_domain(dev);
1487+
}
1488+
14721489
static const struct dev_pm_ops platform_dev_pm_ops = {
14731490
SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend, pm_generic_runtime_resume, NULL)
14741491
USE_PLATFORM_PM_SLEEP_OPS
@@ -1483,6 +1500,7 @@ struct bus_type platform_bus_type = {
14831500
.remove = platform_remove,
14841501
.shutdown = platform_shutdown,
14851502
.dma_configure = platform_dma_configure,
1503+
.dma_cleanup = platform_dma_cleanup,
14861504
.pm = &platform_dev_pm_ops,
14871505
};
14881506
EXPORT_SYMBOL_GPL(platform_bus_type);

drivers/bus/fsl-mc/fsl-mc-bus.c

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <linux/dma-mapping.h>
2222
#include <linux/acpi.h>
2323
#include <linux/iommu.h>
24+
#include <linux/dma-map-ops.h>
2425

2526
#include "fsl-mc-private.h"
2627

@@ -140,15 +141,33 @@ static int fsl_mc_dma_configure(struct device *dev)
140141
{
141142
struct device *dma_dev = dev;
142143
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
144+
struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
143145
u32 input_id = mc_dev->icid;
146+
int ret;
144147

145148
while (dev_is_fsl_mc(dma_dev))
146149
dma_dev = dma_dev->parent;
147150

148151
if (dev_of_node(dma_dev))
149-
return of_dma_configure_id(dev, dma_dev->of_node, 0, &input_id);
152+
ret = of_dma_configure_id(dev, dma_dev->of_node, 0, &input_id);
153+
else
154+
ret = acpi_dma_configure_id(dev, DEV_DMA_COHERENT, &input_id);
155+
156+
if (!ret && !mc_drv->driver_managed_dma) {
157+
ret = iommu_device_use_default_domain(dev);
158+
if (ret)
159+
arch_teardown_dma_ops(dev);
160+
}
161+
162+
return ret;
163+
}
164+
165+
static void fsl_mc_dma_cleanup(struct device *dev)
166+
{
167+
struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
150168

151-
return acpi_dma_configure_id(dev, DEV_DMA_COHERENT, &input_id);
169+
if (!mc_drv->driver_managed_dma)
170+
iommu_device_unuse_default_domain(dev);
152171
}
153172

154173
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
@@ -312,6 +331,7 @@ struct bus_type fsl_mc_bus_type = {
312331
.match = fsl_mc_bus_match,
313332
.uevent = fsl_mc_bus_uevent,
314333
.dma_configure = fsl_mc_dma_configure,
334+
.dma_cleanup = fsl_mc_dma_cleanup,
315335
.dev_groups = fsl_mc_dev_groups,
316336
.bus_groups = fsl_mc_bus_groups,
317337
};

drivers/pci/pci-driver.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/of_device.h>
2121
#include <linux/acpi.h>
2222
#include <linux/dma-map-ops.h>
23+
#include <linux/iommu.h>
2324
#include "pci.h"
2425
#include "pcie/portdrv.h"
2526

@@ -1601,6 +1602,7 @@ static int pci_bus_num_vf(struct device *dev)
16011602
*/
16021603
static int pci_dma_configure(struct device *dev)
16031604
{
1605+
struct pci_driver *driver = to_pci_driver(dev->driver);
16041606
struct device *bridge;
16051607
int ret = 0;
16061608

@@ -1616,9 +1618,24 @@ static int pci_dma_configure(struct device *dev)
16161618
}
16171619

16181620
pci_put_host_bridge_device(bridge);
1621+
1622+
if (!ret && !driver->driver_managed_dma) {
1623+
ret = iommu_device_use_default_domain(dev);
1624+
if (ret)
1625+
arch_teardown_dma_ops(dev);
1626+
}
1627+
16191628
return ret;
16201629
}
16211630

1631+
static void pci_dma_cleanup(struct device *dev)
1632+
{
1633+
struct pci_driver *driver = to_pci_driver(dev->driver);
1634+
1635+
if (!driver->driver_managed_dma)
1636+
iommu_device_unuse_default_domain(dev);
1637+
}
1638+
16221639
struct bus_type pci_bus_type = {
16231640
.name = "pci",
16241641
.match = pci_bus_match,
@@ -1632,6 +1649,7 @@ struct bus_type pci_bus_type = {
16321649
.pm = PCI_PM_OPS_PTR,
16331650
.num_vf = pci_bus_num_vf,
16341651
.dma_configure = pci_dma_configure,
1652+
.dma_cleanup = pci_dma_cleanup,
16351653
};
16361654
EXPORT_SYMBOL(pci_bus_type);
16371655

include/linux/amba/bus.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ struct amba_driver {
7979
void (*remove)(struct amba_device *);
8080
void (*shutdown)(struct amba_device *);
8181
const struct amba_id *id_table;
82+
/*
83+
* For most device drivers, no need to care about this flag as long as
84+
* all DMAs are handled through the kernel DMA API. For some special
85+
* ones, for example VFIO drivers, they know how to manage the DMA
86+
* themselves and set this flag so that the IOMMU layer will allow them
87+
* to setup and manage their own I/O address space.
88+
*/
89+
bool driver_managed_dma;
8290
};
8391

8492
/*

include/linux/fsl/mc.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ struct fsl_mc_io;
3232
* @shutdown: Function called at shutdown time to quiesce the device
3333
* @suspend: Function called when a device is stopped
3434
* @resume: Function called when a device is resumed
35+
* @driver_managed_dma: Device driver doesn't use kernel DMA API for DMA.
36+
* For most device drivers, no need to care about this flag
37+
* as long as all DMAs are handled through the kernel DMA API.
38+
* For some special ones, for example VFIO drivers, they know
39+
* how to manage the DMA themselves and set this flag so that
40+
* the IOMMU layer will allow them to setup and manage their
41+
* own I/O address space.
3542
*
3643
* Generic DPAA device driver object for device drivers that are registered
3744
* with a DPRC bus. This structure is to be embedded in each device-specific
@@ -45,6 +52,7 @@ struct fsl_mc_driver {
4552
void (*shutdown)(struct fsl_mc_device *dev);
4653
int (*suspend)(struct fsl_mc_device *dev, pm_message_t state);
4754
int (*resume)(struct fsl_mc_device *dev);
55+
bool driver_managed_dma;
4856
};
4957

5058
#define to_fsl_mc_driver(_drv) \

include/linux/pci.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,13 @@ struct module;
895895
* created once it is bound to the driver.
896896
* @driver: Driver model structure.
897897
* @dynids: List of dynamically added device IDs.
898+
* @driver_managed_dma: Device driver doesn't use kernel DMA API for DMA.
899+
* For most device drivers, no need to care about this flag
900+
* as long as all DMAs are handled through the kernel DMA API.
901+
* For some special ones, for example VFIO drivers, they know
902+
* how to manage the DMA themselves and set this flag so that
903+
* the IOMMU layer will allow them to setup and manage their
904+
* own I/O address space.
898905
*/
899906
struct pci_driver {
900907
struct list_head node;
@@ -913,6 +920,7 @@ struct pci_driver {
913920
const struct attribute_group **dev_groups;
914921
struct device_driver driver;
915922
struct pci_dynids dynids;
923+
bool driver_managed_dma;
916924
};
917925

918926
static inline struct pci_driver *to_pci_driver(struct device_driver *drv)

include/linux/platform_device.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,14 @@ struct platform_driver {
210210
struct device_driver driver;
211211
const struct platform_device_id *id_table;
212212
bool prevent_deferred_probe;
213+
/*
214+
* For most device drivers, no need to care about this flag as long as
215+
* all DMAs are handled through the kernel DMA API. For some special
216+
* ones, for example VFIO drivers, they know how to manage the DMA
217+
* themselves and set this flag so that the IOMMU layer will allow them
218+
* to setup and manage their own I/O address space.
219+
*/
220+
bool driver_managed_dma;
213221
};
214222

215223
#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \

0 commit comments

Comments
 (0)