]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
iommu: Add IOMMU uclass
authorMark Kettenis <kettenis@openbsd.org>
Sat, 23 Oct 2021 14:58:01 +0000 (16:58 +0200)
committerTom Rini <trini@konsulko.com>
Sun, 31 Oct 2021 12:46:44 +0000 (08:46 -0400)
This uclass is intended to manage IOMMUs on systems where the
IOMMUs are not in bypass mode by default.  In that case U-Boot
cannot ignore the IOMMUs if it wants to use devices that need
to do DMA and sit behind such an IOMMU.

This initial IOMMU uclass implementation does not implement and
device ops and is intended for IOMMUs that have a bypass mode
that does not require address translation.  Support for IOMMUs
that do require address translation is planned and device ops
will be defined when support for such IOMMUs will be added.

Signed-off-by: Mark Kettenis <kettenis@openbsd.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
doc/device-tree-bindings/iommu/iommu.txt [new file with mode: 0644]
drivers/Kconfig
drivers/Makefile
drivers/core/device.c
drivers/iommu/Kconfig [new file with mode: 0644]
drivers/iommu/Makefile [new file with mode: 0644]
drivers/iommu/iommu-uclass.c [new file with mode: 0644]
include/dm/uclass-id.h
include/iommu.h [new file with mode: 0644]

diff --git a/doc/device-tree-bindings/iommu/iommu.txt b/doc/device-tree-bindings/iommu/iommu.txt
new file mode 100644 (file)
index 0000000..26ba9e5
--- /dev/null
@@ -0,0 +1,206 @@
+This document describes the generic device tree binding for IOMMUs and their
+master(s).
+
+
+IOMMU device node:
+==================
+
+An IOMMU can provide the following services:
+
+* Remap address space to allow devices to access physical memory ranges that
+  they otherwise wouldn't be capable of accessing.
+
+  Example: 32-bit DMA to 64-bit physical addresses
+
+* Implement scatter-gather at page level granularity so that the device does
+  not have to.
+
+* Provide system protection against "rogue" DMA by forcing all accesses to go
+  through the IOMMU and faulting when encountering accesses to unmapped
+  address regions.
+
+* Provide address space isolation between multiple contexts.
+
+  Example: Virtualization
+
+Device nodes compatible with this binding represent hardware with some of the
+above capabilities.
+
+IOMMUs can be single-master or multiple-master. Single-master IOMMU devices
+typically have a fixed association to the master device, whereas multiple-
+master IOMMU devices can translate accesses from more than one master.
+
+The device tree node of the IOMMU device's parent bus must contain a valid
+"dma-ranges" property that describes how the physical address space of the
+IOMMU maps to memory. An empty "dma-ranges" property means that there is a
+1:1 mapping from IOMMU to memory.
+
+Required properties:
+--------------------
+- #iommu-cells: The number of cells in an IOMMU specifier needed to encode an
+  address.
+
+The meaning of the IOMMU specifier is defined by the device tree binding of
+the specific IOMMU. Below are a few examples of typical use-cases:
+
+- #iommu-cells = <0>: Single master IOMMU devices are not configurable and
+  therefore no additional information needs to be encoded in the specifier.
+  This may also apply to multiple master IOMMU devices that do not allow the
+  association of masters to be configured. Note that an IOMMU can by design
+  be multi-master yet only expose a single master in a given configuration.
+  In such cases the number of cells will usually be 1 as in the next case.
+- #iommu-cells = <1>: Multiple master IOMMU devices may need to be configured
+  in order to enable translation for a given master. In such cases the single
+  address cell corresponds to the master device's ID. In some cases more than
+  one cell can be required to represent a single master ID.
+- #iommu-cells = <4>: Some IOMMU devices allow the DMA window for masters to
+  be configured. The first cell of the address in this may contain the master
+  device's ID for example, while the second cell could contain the start of
+  the DMA window for the given device. The length of the DMA window is given
+  by the third and fourth cells.
+
+Note that these are merely examples and real-world use-cases may use different
+definitions to represent their individual needs. Always refer to the specific
+IOMMU binding for the exact meaning of the cells that make up the specifier.
+
+
+IOMMU master node:
+==================
+
+Devices that access memory through an IOMMU are called masters. A device can
+have multiple master interfaces (to one or more IOMMU devices).
+
+Required properties:
+--------------------
+- iommus: A list of phandle and IOMMU specifier pairs that describe the IOMMU
+  master interfaces of the device. One entry in the list describes one master
+  interface of the device.
+
+When an "iommus" property is specified in a device tree node, the IOMMU will
+be used for address translation. If a "dma-ranges" property exists in the
+device's parent node it will be ignored. An exception to this rule is if the
+referenced IOMMU is disabled, in which case the "dma-ranges" property of the
+parent shall take effect. Note that merely disabling a device tree node does
+not guarantee that the IOMMU is really disabled since the hardware may not
+have a means to turn off translation. But it is invalid in such cases to
+disable the IOMMU's device tree node in the first place because it would
+prevent any driver from properly setting up the translations.
+
+Optional properties:
+--------------------
+- pasid-num-bits: Some masters support multiple address spaces for DMA, by
+  tagging DMA transactions with an address space identifier. By default,
+  this is 0, which means that the device only has one address space.
+
+- dma-can-stall: When present, the master can wait for a transaction to
+  complete for an indefinite amount of time. Upon translation fault some
+  IOMMUs, instead of aborting the translation immediately, may first
+  notify the driver and keep the transaction in flight. This allows the OS
+  to inspect the fault and, for example, make physical pages resident
+  before updating the mappings and completing the transaction. Such IOMMU
+  accepts a limited number of simultaneous stalled transactions before
+  having to either put back-pressure on the master, or abort new faulting
+  transactions.
+
+  Firmware has to opt-in stalling, because most buses and masters don't
+  support it. In particular it isn't compatible with PCI, where
+  transactions have to complete before a time limit. More generally it
+  won't work in systems and masters that haven't been designed for
+  stalling. For example the OS, in order to handle a stalled transaction,
+  may attempt to retrieve pages from secondary storage in a stalled
+  domain, leading to a deadlock.
+
+
+Notes:
+======
+
+One possible extension to the above is to use an "iommus" property along with
+a "dma-ranges" property in a bus device node (such as PCI host bridges). This
+can be useful to describe how children on the bus relate to the IOMMU if they
+are not explicitly listed in the device tree (e.g. PCI devices). However, the
+requirements of that use-case haven't been fully determined yet. Implementing
+this is therefore not recommended without further discussion and extension of
+this binding.
+
+
+Examples:
+=========
+
+Single-master IOMMU:
+--------------------
+
+       iommu {
+               #iommu-cells = <0>;
+       };
+
+       master {
+               iommus = <&{/iommu}>;
+       };
+
+Multiple-master IOMMU with fixed associations:
+----------------------------------------------
+
+       /* multiple-master IOMMU */
+       iommu {
+               /*
+                * Masters are statically associated with this IOMMU and share
+                * the same address translations because the IOMMU does not
+                * have sufficient information to distinguish between masters.
+                *
+                * Consequently address translation is always on or off for
+                * all masters at any given point in time.
+                */
+               #iommu-cells = <0>;
+       };
+
+       /* static association with IOMMU */
+       master@1 {
+               reg = <1>;
+               iommus = <&{/iommu}>;
+       };
+
+       /* static association with IOMMU */
+       master@2 {
+               reg = <2>;
+               iommus = <&{/iommu}>;
+       };
+
+Multiple-master IOMMU:
+----------------------
+
+       iommu {
+               /* the specifier represents the ID of the master */
+               #iommu-cells = <1>;
+       };
+
+       master@1 {
+               /* device has master ID 42 in the IOMMU */
+               iommus = <&{/iommu} 42>;
+       };
+
+       master@2 {
+               /* device has master IDs 23 and 24 in the IOMMU */
+               iommus = <&{/iommu} 23>, <&{/iommu} 24>;
+       };
+
+Multiple-master IOMMU with configurable DMA window:
+---------------------------------------------------
+
+       / {
+               iommu {
+                       /*
+                        * One cell for the master ID and one cell for the
+                        * address of the DMA window. The length of the DMA
+                        * window is encoded in two cells.
+                        *
+                        * The DMA window is the range addressable by the
+                        * master (i.e. the I/O virtual address space).
+                        */
+                       #iommu-cells = <4>;
+               };
+
+               master {
+                       /* master ID 42, 4 GiB DMA window starting at 0 */
+                       iommus = <&{/iommu}  42  0  0x1 0x0>;
+               };
+       };
index 417d6f88c29f3e086c02c87ac578d6bf50d31cf9..b26ca8cf70c9ca0a672b47559e2fe595b39b0018 100644 (file)
@@ -50,6 +50,8 @@ source "drivers/i2c/Kconfig"
 
 source "drivers/input/Kconfig"
 
+source "drivers/iommu/Kconfig"
+
 source "drivers/led/Kconfig"
 
 source "drivers/mailbox/Kconfig"
index 4cbc40787db3243b0e720584f4cc8c35b9052260..4e7cf284405a6a20a61317edfca8129be824f314 100644 (file)
@@ -102,6 +102,7 @@ obj-y += mtd/
 obj-y += pwm/
 obj-y += reset/
 obj-y += input/
+obj-y += iommu/
 # SOC specific infrastructure drivers.
 obj-y += smem/
 obj-y += thermal/
index d7a778a2413e0aa8985140cb944fd4622527631a..efd07176e37485b4cb910a2dda231e50dda7aa8e 100644 (file)
@@ -28,6 +28,7 @@
 #include <dm/uclass.h>
 #include <dm/uclass-internal.h>
 #include <dm/util.h>
+#include <iommu.h>
 #include <linux/err.h>
 #include <linux/list.h>
 #include <power-domain.h>
@@ -543,6 +544,13 @@ int device_probe(struct udevice *dev)
                        goto fail;
        }
 
+       if (CONFIG_IS_ENABLED(IOMMU) && dev->parent &&
+           (device_get_uclass_id(dev) != UCLASS_IOMMU)) {
+               ret = dev_iommu_enable(dev);
+               if (ret)
+                       goto fail;
+       }
+
        ret = device_get_dma_constraints(dev);
        if (ret)
                goto fail;
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
new file mode 100644 (file)
index 0000000..3e35ce8
--- /dev/null
@@ -0,0 +1,17 @@
+#
+# IOMMU devices
+#
+
+menu "IOMMU device drivers"
+
+config IOMMU
+       bool "Enable Driver Model for IOMMU drivers"
+       depends on DM
+       help
+         Enable driver model for IOMMU devices. An IOMMU maps device
+         virtiual memory addresses to physical addresses. Devices
+         that sit behind an IOMMU can typically only access physical
+         memory if the IOMMU has been programmed to allow access to
+         that memory.
+
+endmenu
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
new file mode 100644 (file)
index 0000000..f1ceb10
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-$(CONFIG_IOMMU) += iommu-uclass.o
diff --git a/drivers/iommu/iommu-uclass.c b/drivers/iommu/iommu-uclass.c
new file mode 100644 (file)
index 0000000..ed917b3
--- /dev/null
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 Mark Kettenis <kettenis@openbsd.org>
+ */
+
+#define LOG_CATEGORY UCLASS_IOMMU
+
+#include <common.h>
+#include <dm.h>
+
+#if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA))
+int dev_iommu_enable(struct udevice *dev)
+{
+       struct ofnode_phandle_args args;
+       struct udevice *dev_iommu;
+       int i, count, ret = 0;
+
+       count = dev_count_phandle_with_args(dev, "iommus",
+                                           "#iommu-cells", 0);
+       for (i = 0; i < count; i++) {
+               ret = dev_read_phandle_with_args(dev, "iommus",
+                                                "#iommu-cells", 0, i, &args);
+               if (ret) {
+                       debug("%s: dev_read_phandle_with_args failed: %d\n",
+                             __func__, ret);
+                       return ret;
+               }
+
+               ret = uclass_get_device_by_ofnode(UCLASS_IOMMU, args.node,
+                                                 &dev_iommu);
+               if (ret) {
+                       debug("%s: uclass_get_device_by_ofnode failed: %d\n",
+                             __func__, ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+#endif
+
+UCLASS_DRIVER(iommu) = {
+       .id             = UCLASS_IOMMU,
+       .name           = "iommu",
+};
index 3768432b680b020eb40c679106dade99933ffdb6..fd139b9b2a087b21ee21587baaea5c10c4a5b2fd 100644 (file)
@@ -62,6 +62,7 @@ enum uclass_id {
        UCLASS_I2C_MUX,         /* I2C multiplexer */
        UCLASS_I2S,             /* I2S bus */
        UCLASS_IDE,             /* IDE device */
+       UCLASS_IOMMU,           /* IOMMU */
        UCLASS_IRQ,             /* Interrupt controller */
        UCLASS_KEYBOARD,        /* Keyboard input device */
        UCLASS_LED,             /* Light-emitting diode (LED) */
diff --git a/include/iommu.h b/include/iommu.h
new file mode 100644 (file)
index 0000000..6c46adf
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _IOMMU_H
+#define _IOMMU_H
+
+struct udevice;
+
+#if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) && \
+       CONFIG_IS_ENABLED(IOMMU)
+int dev_iommu_enable(struct udevice *dev);
+#else
+static inline int dev_iommu_enable(struct udevice *dev)
+{
+       return 0;
+}
+#endif
+
+#endif