mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
Merge tag 'iommu-updates-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux
Pull iommu updates from Will Deacon:
"Core:
- Support for the "ats-supported" device-tree property
- Removal of the 'ops' field from 'struct iommu_fwspec'
- Introduction of iommu_paging_domain_alloc() and partial conversion
of existing users
- Introduce 'struct iommu_attach_handle' and provide corresponding
IOMMU interfaces which will be used by the IOMMUFD subsystem
- Remove stale documentation
- Add missing MODULE_DESCRIPTION() macro
- Misc cleanups
Allwinner Sun50i:
- Ensure bypass mode is disabled on H616 SoCs
- Ensure page-tables are allocated below 4GiB for the 32-bit
page-table walker
- Add new device-tree compatible strings
AMD Vi:
- Use try_cmpxchg64() instead of cmpxchg64() when updating pte
Arm SMMUv2:
- Print much more useful information on context faults
- Fix Qualcomm TBU probing when CONFIG_ARM_SMMU_QCOM_DEBUG=n
- Add new Qualcomm device-tree bindings
Arm SMMUv3:
- Support for hardware update of access/dirty bits and reporting via
IOMMUFD
- More driver rework from Jason, this time updating the PASID/SVA
support to prepare for full IOMMUFD support
- Add missing MODULE_DESCRIPTION() macro
- Minor fixes and cleanups
NVIDIA Tegra:
- Fix for benign fwspec initialisation issue exposed by rework on the
core branch
Intel VT-d:
- Use try_cmpxchg64() instead of cmpxchg64() when updating pte
- Use READ_ONCE() to read volatile descriptor status
- Remove support for handling Execute-Requested requests
- Avoid calling iommu_domain_alloc()
- Minor fixes and refactoring
Qualcomm MSM:
- Updates to the device-tree bindings"
* tag 'iommu-updates-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux: (72 commits)
iommu/tegra-smmu: Pass correct fwnode to iommu_fwspec_init()
iommu/vt-d: Fix identity map bounds in si_domain_init()
iommu: Move IOMMU_DIRTY_NO_CLEAR define
dt-bindings: iommu: Convert msm,iommu-v0 to yaml
iommu/vt-d: Fix aligned pages in calculate_psi_aligned_address()
iommu/vt-d: Limit max address mask to MAX_AGAW_PFN_WIDTH
docs: iommu: Remove outdated Documentation/userspace-api/iommu.rst
arm64: dts: fvp: Enable PCIe ATS for Base RevC FVP
iommu/of: Support ats-supported device-tree property
dt-bindings: PCI: generic: Add ats-supported property
iommu: Remove iommu_fwspec ops
OF: Simplify of_iommu_configure()
ACPI: Retire acpi_iommu_fwspec_ops()
iommu: Resolve fwspec ops automatically
iommu/mediatek-v1: Clean up redundant fwspec checks
RDMA/usnic: Use iommu_paging_domain_alloc()
wifi: ath11k: Use iommu_paging_domain_alloc()
wifi: ath10k: Use iommu_paging_domain_alloc()
drm/msm: Use iommu_paging_domain_alloc()
vhost-vdpa: Use iommu_paging_domain_alloc()
...
This commit is contained in:
@@ -17,7 +17,12 @@ properties:
|
||||
The content of the cell is the master ID.
|
||||
|
||||
compatible:
|
||||
const: allwinner,sun50i-h6-iommu
|
||||
oneOf:
|
||||
- const: allwinner,sun50i-h6-iommu
|
||||
- const: allwinner,sun50i-h616-iommu
|
||||
- items:
|
||||
- const: allwinner,sun55i-a523-iommu
|
||||
- const: allwinner,sun50i-h616-iommu
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
@@ -86,6 +86,7 @@ properties:
|
||||
- qcom,qcm2290-smmu-500
|
||||
- qcom,sa8775p-smmu-500
|
||||
- qcom,sc7280-smmu-500
|
||||
- qcom,sc8180x-smmu-500
|
||||
- qcom,sc8280xp-smmu-500
|
||||
- qcom,sm6115-smmu-500
|
||||
- qcom,sm6125-smmu-500
|
||||
@@ -95,6 +96,7 @@ properties:
|
||||
- qcom,sm8450-smmu-500
|
||||
- qcom,sm8550-smmu-500
|
||||
- qcom,sm8650-smmu-500
|
||||
- qcom,x1e80100-smmu-500
|
||||
- const: qcom,adreno-smmu
|
||||
- const: qcom,smmu-500
|
||||
- const: arm,mmu-500
|
||||
@@ -415,6 +417,7 @@ allOf:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8180x-smmu-500
|
||||
- qcom,sm6350-smmu-v2
|
||||
- qcom,sm7150-smmu-v2
|
||||
- qcom,sm8150-smmu-500
|
||||
@@ -520,6 +523,7 @@ allOf:
|
||||
- enum:
|
||||
- qcom,sm8550-smmu-500
|
||||
- qcom,sm8650-smmu-500
|
||||
- qcom,x1e80100-smmu-500
|
||||
- const: qcom,adreno-smmu
|
||||
- const: qcom,smmu-500
|
||||
- const: arm,mmu-500
|
||||
@@ -550,14 +554,12 @@ allOf:
|
||||
- nvidia,smmu-500
|
||||
- qcom,qdu1000-smmu-500
|
||||
- qcom,sc7180-smmu-500
|
||||
- qcom,sc8180x-smmu-500
|
||||
- qcom,sdm670-smmu-500
|
||||
- qcom,sdm845-smmu-500
|
||||
- qcom,sdx55-smmu-500
|
||||
- qcom,sdx65-smmu-500
|
||||
- qcom,sm6350-smmu-500
|
||||
- qcom,sm6375-smmu-500
|
||||
- qcom,x1e80100-smmu-500
|
||||
then:
|
||||
properties:
|
||||
clock-names: false
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
* QCOM IOMMU
|
||||
|
||||
The MSM IOMMU is an implementation compatible with the ARM VMSA short
|
||||
descriptor page tables. It provides address translation for bus masters outside
|
||||
of the CPU, each connected to the IOMMU through a port called micro-TLB.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: Must contain "qcom,apq8064-iommu".
|
||||
- reg: Base address and size of the IOMMU registers.
|
||||
- interrupts: Specifiers for the MMU fault interrupts. For instances that
|
||||
support secure mode two interrupts must be specified, for non-secure and
|
||||
secure mode, in that order. For instances that don't support secure mode a
|
||||
single interrupt must be specified.
|
||||
- #iommu-cells: The number of cells needed to specify the stream id. This
|
||||
is always 1.
|
||||
- qcom,ncb: The total number of context banks in the IOMMU.
|
||||
- clocks : List of clocks to be used during SMMU register access. See
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
for information about the format. For each clock specified
|
||||
here, there must be a corresponding entry in clock-names
|
||||
(see below).
|
||||
|
||||
- clock-names : List of clock names corresponding to the clocks specified in
|
||||
the "clocks" property (above).
|
||||
Should be "smmu_pclk" for specifying the interface clock
|
||||
required for iommu's register accesses.
|
||||
Should be "smmu_clk" for specifying the functional clock
|
||||
required by iommu for bus accesses.
|
||||
|
||||
Each bus master connected to an IOMMU must reference the IOMMU in its device
|
||||
node with the following property:
|
||||
|
||||
- iommus: A reference to the IOMMU in multiple cells. The first cell is a
|
||||
phandle to the IOMMU and the second cell is the stream id.
|
||||
A single master device can be connected to more than one iommu
|
||||
and multiple contexts in each of the iommu. So multiple entries
|
||||
are required to list all the iommus and the stream ids that the
|
||||
master is connected to.
|
||||
|
||||
Example: mdp iommu and its bus master
|
||||
|
||||
mdp_port0: iommu@7500000 {
|
||||
compatible = "qcom,apq8064-iommu";
|
||||
#iommu-cells = <1>;
|
||||
clock-names =
|
||||
"smmu_pclk",
|
||||
"smmu_clk";
|
||||
clocks =
|
||||
<&mmcc SMMU_AHB_CLK>,
|
||||
<&mmcc MDP_AXI_CLK>;
|
||||
reg = <0x07500000 0x100000>;
|
||||
interrupts =
|
||||
<GIC_SPI 63 0>,
|
||||
<GIC_SPI 64 0>;
|
||||
qcom,ncb = <2>;
|
||||
};
|
||||
|
||||
mdp: qcom,mdp@5100000 {
|
||||
compatible = "qcom,mdp";
|
||||
...
|
||||
iommus = <&mdp_port0 0
|
||||
&mdp_port0 2>;
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
|
||||
$id: http://devicetree.org/schemas/iommu/qcom,apq8064-iommu.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm APQ8064 IOMMU
|
||||
|
||||
maintainers:
|
||||
- David Heidelberg <david@ixit.cz>
|
||||
|
||||
description:
|
||||
The MSM IOMMU is an implementation compatible with the ARM VMSA short
|
||||
descriptor page tables. It provides address translation for bus masters
|
||||
outside of the CPU, each connected to the IOMMU through a port called micro-TLB.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,apq8064-iommu
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: interface clock for register accesses
|
||||
- description: functional clock for bus accesses
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: smmu_pclk
|
||||
- const: iommu_clk
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
description: Specifiers for the MMU fault interrupts.
|
||||
minItems: 1
|
||||
items:
|
||||
- description: non-secure mode interrupt
|
||||
- description: secure mode interrupt (for instances which supports it)
|
||||
|
||||
"#iommu-cells":
|
||||
const: 1
|
||||
description: Each IOMMU specifier describes a single Stream ID.
|
||||
|
||||
qcom,ncb:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: The total number of context banks in the IOMMU.
|
||||
minimum: 1
|
||||
maximum: 4
|
||||
|
||||
required:
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- qcom,ncb
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,mmcc-msm8960.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
iommu@7500000 {
|
||||
compatible = "qcom,apq8064-iommu";
|
||||
reg = <0x07500000 0x100000>;
|
||||
interrupts = <GIC_SPI 63 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clk SMMU_AHB_CLK>,
|
||||
<&clk MDP_AXI_CLK>;
|
||||
clock-names = "smmu_pclk",
|
||||
"iommu_clk";
|
||||
#iommu-cells = <1>;
|
||||
qcom,ncb = <2>;
|
||||
};
|
||||
@@ -25,6 +25,7 @@ properties:
|
||||
- const: qcom,msm-iommu-v1
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,msm8953-iommu
|
||||
- qcom,msm8976-iommu
|
||||
- const: qcom,msm-iommu-v2
|
||||
|
||||
|
||||
@@ -110,6 +110,12 @@ properties:
|
||||
iommu-map-mask: true
|
||||
msi-parent: true
|
||||
|
||||
ats-supported:
|
||||
description:
|
||||
Indicates that a PCIe host controller supports ATS, and can handle Memory
|
||||
Requests with Address Type (AT).
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
@@ -45,7 +45,6 @@ Devices and I/O
|
||||
accelerators/ocxl
|
||||
dma-buf-alloc-exchange
|
||||
gpio/index
|
||||
iommu
|
||||
iommufd
|
||||
media/index
|
||||
dcdbas
|
||||
|
||||
@@ -1,209 +0,0 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
.. iommu:
|
||||
|
||||
=====================================
|
||||
IOMMU Userspace API
|
||||
=====================================
|
||||
|
||||
IOMMU UAPI is used for virtualization cases where communications are
|
||||
needed between physical and virtual IOMMU drivers. For baremetal
|
||||
usage, the IOMMU is a system device which does not need to communicate
|
||||
with userspace directly.
|
||||
|
||||
The primary use cases are guest Shared Virtual Address (SVA) and
|
||||
guest IO virtual address (IOVA), wherein the vIOMMU implementation
|
||||
relies on the physical IOMMU and for this reason requires interactions
|
||||
with the host driver.
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Functionalities
|
||||
===============
|
||||
Communications of user and kernel involve both directions. The
|
||||
supported user-kernel APIs are as follows:
|
||||
|
||||
1. Bind/Unbind guest PASID (e.g. Intel VT-d)
|
||||
2. Bind/Unbind guest PASID table (e.g. ARM SMMU)
|
||||
3. Invalidate IOMMU caches upon guest requests
|
||||
4. Report errors to the guest and serve page requests
|
||||
|
||||
Requirements
|
||||
============
|
||||
The IOMMU UAPIs are generic and extensible to meet the following
|
||||
requirements:
|
||||
|
||||
1. Emulated and para-virtualised vIOMMUs
|
||||
2. Multiple vendors (Intel VT-d, ARM SMMU, etc.)
|
||||
3. Extensions to the UAPI shall not break existing userspace
|
||||
|
||||
Interfaces
|
||||
==========
|
||||
Although the data structures defined in IOMMU UAPI are self-contained,
|
||||
there are no user API functions introduced. Instead, IOMMU UAPI is
|
||||
designed to work with existing user driver frameworks such as VFIO.
|
||||
|
||||
Extension Rules & Precautions
|
||||
-----------------------------
|
||||
When IOMMU UAPI gets extended, the data structures can *only* be
|
||||
modified in two ways:
|
||||
|
||||
1. Adding new fields by re-purposing the padding[] field. No size change.
|
||||
2. Adding new union members at the end. May increase the structure sizes.
|
||||
|
||||
No new fields can be added *after* the variable sized union in that it
|
||||
will break backward compatibility when offset moves. A new flag must
|
||||
be introduced whenever a change affects the structure using either
|
||||
method. The IOMMU driver processes the data based on flags which
|
||||
ensures backward compatibility.
|
||||
|
||||
Version field is only reserved for the unlikely event of UAPI upgrade
|
||||
at its entirety.
|
||||
|
||||
It's *always* the caller's responsibility to indicate the size of the
|
||||
structure passed by setting argsz appropriately.
|
||||
Though at the same time, argsz is user provided data which is not
|
||||
trusted. The argsz field allows the user app to indicate how much data
|
||||
it is providing; it's still the kernel's responsibility to validate
|
||||
whether it's correct and sufficient for the requested operation.
|
||||
|
||||
Compatibility Checking
|
||||
----------------------
|
||||
When IOMMU UAPI extension results in some structure size increase,
|
||||
IOMMU UAPI code shall handle the following cases:
|
||||
|
||||
1. User and kernel has exact size match
|
||||
2. An older user with older kernel header (smaller UAPI size) running on a
|
||||
newer kernel (larger UAPI size)
|
||||
3. A newer user with newer kernel header (larger UAPI size) running
|
||||
on an older kernel.
|
||||
4. A malicious/misbehaving user passing illegal/invalid size but within
|
||||
range. The data may contain garbage.
|
||||
|
||||
Feature Checking
|
||||
----------------
|
||||
While launching a guest with vIOMMU, it is strongly advised to check
|
||||
the compatibility upfront, as some subsequent errors happening during
|
||||
vIOMMU operation, such as cache invalidation failures cannot be nicely
|
||||
escalated to the guest due to IOMMU specifications. This can lead to
|
||||
catastrophic failures for the users.
|
||||
|
||||
User applications such as QEMU are expected to import kernel UAPI
|
||||
headers. Backward compatibility is supported per feature flags.
|
||||
For example, an older QEMU (with older kernel header) can run on newer
|
||||
kernel. Newer QEMU (with new kernel header) may refuse to initialize
|
||||
on an older kernel if new feature flags are not supported by older
|
||||
kernel. Simply recompiling existing code with newer kernel header should
|
||||
not be an issue in that only existing flags are used.
|
||||
|
||||
IOMMU vendor driver should report the below features to IOMMU UAPI
|
||||
consumers (e.g. via VFIO).
|
||||
|
||||
1. IOMMU_NESTING_FEAT_SYSWIDE_PASID
|
||||
2. IOMMU_NESTING_FEAT_BIND_PGTBL
|
||||
3. IOMMU_NESTING_FEAT_BIND_PASID_TABLE
|
||||
4. IOMMU_NESTING_FEAT_CACHE_INVLD
|
||||
5. IOMMU_NESTING_FEAT_PAGE_REQUEST
|
||||
|
||||
Take VFIO as example, upon request from VFIO userspace (e.g. QEMU),
|
||||
VFIO kernel code shall query IOMMU vendor driver for the support of
|
||||
the above features. Query result can then be reported back to the
|
||||
userspace caller. Details can be found in
|
||||
Documentation/driver-api/vfio.rst.
|
||||
|
||||
|
||||
Data Passing Example with VFIO
|
||||
------------------------------
|
||||
As the ubiquitous userspace driver framework, VFIO is already IOMMU
|
||||
aware and shares many key concepts such as device model, group, and
|
||||
protection domain. Other user driver frameworks can also be extended
|
||||
to support IOMMU UAPI but it is outside the scope of this document.
|
||||
|
||||
In this tight-knit VFIO-IOMMU interface, the ultimate consumer of the
|
||||
IOMMU UAPI data is the host IOMMU driver. VFIO facilitates user-kernel
|
||||
transport, capability checking, security, and life cycle management of
|
||||
process address space ID (PASID).
|
||||
|
||||
VFIO layer conveys the data structures down to the IOMMU driver. It
|
||||
follows the pattern below::
|
||||
|
||||
struct {
|
||||
__u32 argsz;
|
||||
__u32 flags;
|
||||
__u8 data[];
|
||||
};
|
||||
|
||||
Here data[] contains the IOMMU UAPI data structures. VFIO has the
|
||||
freedom to bundle the data as well as parse data size based on its own flags.
|
||||
|
||||
In order to determine the size and feature set of the user data, argsz
|
||||
and flags (or the equivalent) are also embedded in the IOMMU UAPI data
|
||||
structures.
|
||||
|
||||
A "__u32 argsz" field is *always* at the beginning of each structure.
|
||||
|
||||
For example:
|
||||
::
|
||||
|
||||
struct iommu_cache_invalidate_info {
|
||||
__u32 argsz;
|
||||
#define IOMMU_CACHE_INVALIDATE_INFO_VERSION_1 1
|
||||
__u32 version;
|
||||
/* IOMMU paging structure cache */
|
||||
#define IOMMU_CACHE_INV_TYPE_IOTLB (1 << 0) /* IOMMU IOTLB */
|
||||
#define IOMMU_CACHE_INV_TYPE_DEV_IOTLB (1 << 1) /* Device IOTLB */
|
||||
#define IOMMU_CACHE_INV_TYPE_PASID (1 << 2) /* PASID cache */
|
||||
#define IOMMU_CACHE_INV_TYPE_NR (3)
|
||||
__u8 cache;
|
||||
__u8 granularity;
|
||||
__u8 padding[6];
|
||||
union {
|
||||
struct iommu_inv_pasid_info pasid_info;
|
||||
struct iommu_inv_addr_info addr_info;
|
||||
} granu;
|
||||
};
|
||||
|
||||
VFIO is responsible for checking its own argsz and flags. It then
|
||||
invokes appropriate IOMMU UAPI functions. The user pointers are passed
|
||||
to the IOMMU layer for further processing. The responsibilities are
|
||||
divided as follows:
|
||||
|
||||
- Generic IOMMU layer checks argsz range based on UAPI data in the
|
||||
current kernel version.
|
||||
|
||||
- Generic IOMMU layer checks content of the UAPI data for non-zero
|
||||
reserved bits in flags, padding fields, and unsupported version.
|
||||
This is to ensure not breaking userspace in the future when these
|
||||
fields or flags are used.
|
||||
|
||||
- Vendor IOMMU driver checks argsz based on vendor flags. UAPI data
|
||||
is consumed based on flags. Vendor driver has access to
|
||||
unadulterated argsz value in case of vendor specific future
|
||||
extensions. Currently, it does not perform the copy_from_user()
|
||||
itself. A __user pointer can be provided in some future scenarios
|
||||
where there's vendor data outside of the structure definition.
|
||||
|
||||
IOMMU code treats UAPI data in two categories:
|
||||
|
||||
- structure contains vendor data
|
||||
(Example: iommu_uapi_cache_invalidate())
|
||||
|
||||
- structure contains only generic data
|
||||
(Example: iommu_uapi_sva_bind_gpasid())
|
||||
|
||||
|
||||
|
||||
Sharing UAPI with in-kernel users
|
||||
---------------------------------
|
||||
For UAPIs that are shared with in-kernel users, a wrapper function is
|
||||
provided to distinguish the callers. For example,
|
||||
|
||||
Userspace caller ::
|
||||
|
||||
int iommu_uapi_sva_unbind_gpasid(struct iommu_domain *domain,
|
||||
struct device *dev,
|
||||
void __user *udata)
|
||||
|
||||
In-kernel caller ::
|
||||
|
||||
int iommu_sva_unbind_gpasid(struct iommu_domain *domain,
|
||||
struct device *dev, ioasid_t ioasid);
|
||||
@@ -11685,7 +11685,6 @@ L: iommu@lists.linux.dev
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux.git
|
||||
F: Documentation/devicetree/bindings/iommu/
|
||||
F: Documentation/userspace-api/iommu.rst
|
||||
F: drivers/iommu/
|
||||
F: include/linux/iommu.h
|
||||
F: include/linux/iova.h
|
||||
|
||||
@@ -243,6 +243,7 @@
|
||||
iommu-map = <0x0 &smmu 0x0 0x10000>;
|
||||
|
||||
dma-coherent;
|
||||
ats-supported;
|
||||
};
|
||||
|
||||
smmu: iommu@2b400000 {
|
||||
|
||||
@@ -1221,10 +1221,10 @@ static bool iort_pci_rc_supports_ats(struct acpi_iort_node *node)
|
||||
static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node,
|
||||
u32 streamid)
|
||||
{
|
||||
const struct iommu_ops *ops;
|
||||
struct fwnode_handle *iort_fwnode;
|
||||
|
||||
if (!node)
|
||||
/* If there's no SMMU driver at all, give up now */
|
||||
if (!node || !iort_iommu_driver_enabled(node->type))
|
||||
return -ENODEV;
|
||||
|
||||
iort_fwnode = iort_get_fwnode(node);
|
||||
@@ -1232,19 +1232,10 @@ static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node,
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* If the ops look-up fails, this means that either
|
||||
* the SMMU drivers have not been probed yet or that
|
||||
* the SMMU drivers are not built in the kernel;
|
||||
* Depending on whether the SMMU drivers are built-in
|
||||
* in the kernel or not, defer the IOMMU configuration
|
||||
* or just abort it.
|
||||
* If the SMMU drivers are enabled but not loaded/probed
|
||||
* yet, this will defer.
|
||||
*/
|
||||
ops = iommu_ops_from_fwnode(iort_fwnode);
|
||||
if (!ops)
|
||||
return iort_iommu_driver_enabled(node->type) ?
|
||||
-EPROBE_DEFER : -ENODEV;
|
||||
|
||||
return acpi_iommu_fwspec_init(dev, streamid, iort_fwnode, ops);
|
||||
return acpi_iommu_fwspec_init(dev, streamid, iort_fwnode);
|
||||
}
|
||||
|
||||
struct iort_pci_alias_info {
|
||||
|
||||
@@ -1606,38 +1606,25 @@ int acpi_dma_get_range(struct device *dev, const struct bus_dma_region **map)
|
||||
|
||||
#ifdef CONFIG_IOMMU_API
|
||||
int acpi_iommu_fwspec_init(struct device *dev, u32 id,
|
||||
struct fwnode_handle *fwnode,
|
||||
const struct iommu_ops *ops)
|
||||
struct fwnode_handle *fwnode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = iommu_fwspec_init(dev, fwnode, ops);
|
||||
ret = iommu_fwspec_init(dev, fwnode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return iommu_fwspec_add_ids(dev, &id, 1);
|
||||
}
|
||||
|
||||
static inline const struct iommu_ops *acpi_iommu_fwspec_ops(struct device *dev)
|
||||
{
|
||||
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
|
||||
|
||||
return fwspec ? fwspec->ops : NULL;
|
||||
}
|
||||
|
||||
static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
|
||||
{
|
||||
int err;
|
||||
const struct iommu_ops *ops;
|
||||
|
||||
/* Serialise to make dev->iommu stable under our potential fwspec */
|
||||
mutex_lock(&iommu_probe_device_lock);
|
||||
/*
|
||||
* If we already translated the fwspec there is nothing left to do,
|
||||
* return the iommu_ops.
|
||||
*/
|
||||
ops = acpi_iommu_fwspec_ops(dev);
|
||||
if (ops) {
|
||||
/* If we already translated the fwspec there is nothing left to do */
|
||||
if (dev_iommu_fwspec_get(dev)) {
|
||||
mutex_unlock(&iommu_probe_device_lock);
|
||||
return 0;
|
||||
}
|
||||
@@ -1654,22 +1641,13 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
|
||||
if (!err && dev->bus)
|
||||
err = iommu_probe_device(dev);
|
||||
|
||||
if (err == -EPROBE_DEFER)
|
||||
return err;
|
||||
if (err) {
|
||||
dev_dbg(dev, "Adding to IOMMU failed: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
if (!acpi_iommu_fwspec_ops(dev))
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
#else /* !CONFIG_IOMMU_API */
|
||||
|
||||
int acpi_iommu_fwspec_init(struct device *dev, u32 id,
|
||||
struct fwnode_handle *fwnode,
|
||||
const struct iommu_ops *ops)
|
||||
struct fwnode_handle *fwnode)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
@@ -1703,6 +1681,8 @@ int acpi_dma_configure_id(struct device *dev, enum dev_dma_attr attr,
|
||||
ret = acpi_iommu_configure_id(dev, input_id);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
if (ret)
|
||||
dev_dbg(dev, "Adding to IOMMU failed: %d\n", ret);
|
||||
|
||||
arch_setup_dma_ops(dev, attr == DEV_DMA_COHERENT);
|
||||
|
||||
|
||||
@@ -307,21 +307,14 @@ void __init acpi_viot_init(void)
|
||||
static int viot_dev_iommu_init(struct device *dev, struct viot_iommu *viommu,
|
||||
u32 epid)
|
||||
{
|
||||
const struct iommu_ops *ops;
|
||||
|
||||
if (!viommu)
|
||||
if (!viommu || !IS_ENABLED(CONFIG_VIRTIO_IOMMU))
|
||||
return -ENODEV;
|
||||
|
||||
/* We're not translating ourself */
|
||||
if (device_match_fwnode(dev, viommu->fwnode))
|
||||
return -EINVAL;
|
||||
|
||||
ops = iommu_ops_from_fwnode(viommu->fwnode);
|
||||
if (!ops)
|
||||
return IS_ENABLED(CONFIG_VIRTIO_IOMMU) ?
|
||||
-EPROBE_DEFER : -ENODEV;
|
||||
|
||||
return acpi_iommu_fwspec_init(dev, epid, viommu->fwnode, ops);
|
||||
return acpi_iommu_fwspec_init(dev, epid, viommu->fwnode);
|
||||
}
|
||||
|
||||
static int viot_pci_dev_iommu_init(struct pci_dev *pdev, u16 dev_id, void *data)
|
||||
|
||||
@@ -407,10 +407,13 @@ struct msm_mmu *msm_iommu_new(struct device *dev, unsigned long quirks)
|
||||
struct msm_iommu *iommu;
|
||||
int ret;
|
||||
|
||||
domain = iommu_domain_alloc(dev->bus);
|
||||
if (!domain)
|
||||
if (!device_iommu_mapped(dev))
|
||||
return NULL;
|
||||
|
||||
domain = iommu_paging_domain_alloc(dev);
|
||||
if (IS_ERR(domain))
|
||||
return ERR_CAST(domain);
|
||||
|
||||
iommu_set_pgtable_quirks(domain, quirks);
|
||||
|
||||
iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
|
||||
|
||||
@@ -443,11 +443,11 @@ struct usnic_uiom_pd *usnic_uiom_alloc_pd(struct device *dev)
|
||||
if (!pd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pd->domain = domain = iommu_domain_alloc(dev->bus);
|
||||
if (!domain) {
|
||||
pd->domain = domain = iommu_paging_domain_alloc(dev);
|
||||
if (IS_ERR(domain)) {
|
||||
usnic_err("Failed to allocate IOMMU domain");
|
||||
kfree(pd);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return ERR_CAST(domain);
|
||||
}
|
||||
|
||||
iommu_set_fault_handler(pd->domain, usnic_uiom_dma_fault, NULL);
|
||||
|
||||
@@ -394,6 +394,7 @@ config ARM_SMMU_V3
|
||||
select IOMMU_API
|
||||
select IOMMU_IO_PGTABLE_LPAE
|
||||
select GENERIC_MSI_IRQ
|
||||
select IOMMUFD_DRIVER if IOMMUFD
|
||||
help
|
||||
Support for implementations of the ARM System MMU architecture
|
||||
version 3 providing translation support to a PCIe root complex.
|
||||
|
||||
@@ -158,7 +158,7 @@ static u64 *v2_alloc_pte(int nid, u64 *pgd, unsigned long iova,
|
||||
|
||||
__npte = set_pgtable_attr(page);
|
||||
/* pte could have been changed somewhere. */
|
||||
if (cmpxchg64(pte, __pte, __npte) != __pte)
|
||||
if (!try_cmpxchg64(pte, &__pte, __npte))
|
||||
iommu_free_page(page);
|
||||
else if (IOMMU_PTE_PRESENT(__pte))
|
||||
*updated = true;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_ARM_SMMU_V3) += arm_smmu_v3.o
|
||||
arm_smmu_v3-objs-y += arm-smmu-v3.o
|
||||
arm_smmu_v3-objs-$(CONFIG_ARM_SMMU_V3_SVA) += arm-smmu-v3-sva.o
|
||||
arm_smmu_v3-objs := $(arm_smmu_v3-objs-y)
|
||||
arm_smmu_v3-y := arm-smmu-v3.o
|
||||
arm_smmu_v3-$(CONFIG_ARM_SMMU_V3_SVA) += arm-smmu-v3-sva.o
|
||||
|
||||
obj-$(CONFIG_ARM_SMMU_V3_KUNIT_TEST) += arm-smmu-v3-test.o
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -144,6 +144,14 @@ static void arm_smmu_v3_test_ste_expect_transition(
|
||||
KUNIT_EXPECT_MEMEQ(test, target->data, cur_copy.data, sizeof(cur_copy));
|
||||
}
|
||||
|
||||
static void arm_smmu_v3_test_ste_expect_non_hitless_transition(
|
||||
struct kunit *test, const struct arm_smmu_ste *cur,
|
||||
const struct arm_smmu_ste *target, unsigned int num_syncs_expected)
|
||||
{
|
||||
arm_smmu_v3_test_ste_expect_transition(test, cur, target,
|
||||
num_syncs_expected, false);
|
||||
}
|
||||
|
||||
static void arm_smmu_v3_test_ste_expect_hitless_transition(
|
||||
struct kunit *test, const struct arm_smmu_ste *cur,
|
||||
const struct arm_smmu_ste *target, unsigned int num_syncs_expected)
|
||||
@@ -155,6 +163,7 @@ static void arm_smmu_v3_test_ste_expect_hitless_transition(
|
||||
static const dma_addr_t fake_cdtab_dma_addr = 0xF0F0F0F0F0F0;
|
||||
|
||||
static void arm_smmu_test_make_cdtable_ste(struct arm_smmu_ste *ste,
|
||||
unsigned int s1dss,
|
||||
const dma_addr_t dma_addr)
|
||||
{
|
||||
struct arm_smmu_master master = {
|
||||
@@ -164,7 +173,7 @@ static void arm_smmu_test_make_cdtable_ste(struct arm_smmu_ste *ste,
|
||||
.smmu = &smmu,
|
||||
};
|
||||
|
||||
arm_smmu_make_cdtable_ste(ste, &master);
|
||||
arm_smmu_make_cdtable_ste(ste, &master, true, s1dss);
|
||||
}
|
||||
|
||||
static void arm_smmu_v3_write_ste_test_bypass_to_abort(struct kunit *test)
|
||||
@@ -194,7 +203,8 @@ static void arm_smmu_v3_write_ste_test_cdtable_to_abort(struct kunit *test)
|
||||
{
|
||||
struct arm_smmu_ste ste;
|
||||
|
||||
arm_smmu_test_make_cdtable_ste(&ste, fake_cdtab_dma_addr);
|
||||
arm_smmu_test_make_cdtable_ste(&ste, STRTAB_STE_1_S1DSS_SSID0,
|
||||
fake_cdtab_dma_addr);
|
||||
arm_smmu_v3_test_ste_expect_hitless_transition(test, &ste, &abort_ste,
|
||||
NUM_EXPECTED_SYNCS(2));
|
||||
}
|
||||
@@ -203,7 +213,8 @@ static void arm_smmu_v3_write_ste_test_abort_to_cdtable(struct kunit *test)
|
||||
{
|
||||
struct arm_smmu_ste ste;
|
||||
|
||||
arm_smmu_test_make_cdtable_ste(&ste, fake_cdtab_dma_addr);
|
||||
arm_smmu_test_make_cdtable_ste(&ste, STRTAB_STE_1_S1DSS_SSID0,
|
||||
fake_cdtab_dma_addr);
|
||||
arm_smmu_v3_test_ste_expect_hitless_transition(test, &abort_ste, &ste,
|
||||
NUM_EXPECTED_SYNCS(2));
|
||||
}
|
||||
@@ -212,7 +223,8 @@ static void arm_smmu_v3_write_ste_test_cdtable_to_bypass(struct kunit *test)
|
||||
{
|
||||
struct arm_smmu_ste ste;
|
||||
|
||||
arm_smmu_test_make_cdtable_ste(&ste, fake_cdtab_dma_addr);
|
||||
arm_smmu_test_make_cdtable_ste(&ste, STRTAB_STE_1_S1DSS_SSID0,
|
||||
fake_cdtab_dma_addr);
|
||||
arm_smmu_v3_test_ste_expect_hitless_transition(test, &ste, &bypass_ste,
|
||||
NUM_EXPECTED_SYNCS(3));
|
||||
}
|
||||
@@ -221,17 +233,59 @@ static void arm_smmu_v3_write_ste_test_bypass_to_cdtable(struct kunit *test)
|
||||
{
|
||||
struct arm_smmu_ste ste;
|
||||
|
||||
arm_smmu_test_make_cdtable_ste(&ste, fake_cdtab_dma_addr);
|
||||
arm_smmu_test_make_cdtable_ste(&ste, STRTAB_STE_1_S1DSS_SSID0,
|
||||
fake_cdtab_dma_addr);
|
||||
arm_smmu_v3_test_ste_expect_hitless_transition(test, &bypass_ste, &ste,
|
||||
NUM_EXPECTED_SYNCS(3));
|
||||
}
|
||||
|
||||
static void arm_smmu_v3_write_ste_test_cdtable_s1dss_change(struct kunit *test)
|
||||
{
|
||||
struct arm_smmu_ste ste;
|
||||
struct arm_smmu_ste s1dss_bypass;
|
||||
|
||||
arm_smmu_test_make_cdtable_ste(&ste, STRTAB_STE_1_S1DSS_SSID0,
|
||||
fake_cdtab_dma_addr);
|
||||
arm_smmu_test_make_cdtable_ste(&s1dss_bypass, STRTAB_STE_1_S1DSS_BYPASS,
|
||||
fake_cdtab_dma_addr);
|
||||
|
||||
/*
|
||||
* Flipping s1dss on a CD table STE only involves changes to the second
|
||||
* qword of an STE and can be done in a single write.
|
||||
*/
|
||||
arm_smmu_v3_test_ste_expect_hitless_transition(
|
||||
test, &ste, &s1dss_bypass, NUM_EXPECTED_SYNCS(1));
|
||||
arm_smmu_v3_test_ste_expect_hitless_transition(
|
||||
test, &s1dss_bypass, &ste, NUM_EXPECTED_SYNCS(1));
|
||||
}
|
||||
|
||||
static void
|
||||
arm_smmu_v3_write_ste_test_s1dssbypass_to_stebypass(struct kunit *test)
|
||||
{
|
||||
struct arm_smmu_ste s1dss_bypass;
|
||||
|
||||
arm_smmu_test_make_cdtable_ste(&s1dss_bypass, STRTAB_STE_1_S1DSS_BYPASS,
|
||||
fake_cdtab_dma_addr);
|
||||
arm_smmu_v3_test_ste_expect_hitless_transition(
|
||||
test, &s1dss_bypass, &bypass_ste, NUM_EXPECTED_SYNCS(2));
|
||||
}
|
||||
|
||||
static void
|
||||
arm_smmu_v3_write_ste_test_stebypass_to_s1dssbypass(struct kunit *test)
|
||||
{
|
||||
struct arm_smmu_ste s1dss_bypass;
|
||||
|
||||
arm_smmu_test_make_cdtable_ste(&s1dss_bypass, STRTAB_STE_1_S1DSS_BYPASS,
|
||||
fake_cdtab_dma_addr);
|
||||
arm_smmu_v3_test_ste_expect_hitless_transition(
|
||||
test, &bypass_ste, &s1dss_bypass, NUM_EXPECTED_SYNCS(2));
|
||||
}
|
||||
|
||||
static void arm_smmu_test_make_s2_ste(struct arm_smmu_ste *ste,
|
||||
bool ats_enabled)
|
||||
{
|
||||
struct arm_smmu_master master = {
|
||||
.smmu = &smmu,
|
||||
.ats_enabled = ats_enabled,
|
||||
};
|
||||
struct io_pgtable io_pgtable = {};
|
||||
struct arm_smmu_domain smmu_domain = {
|
||||
@@ -247,7 +301,7 @@ static void arm_smmu_test_make_s2_ste(struct arm_smmu_ste *ste,
|
||||
io_pgtable.cfg.arm_lpae_s2_cfg.vtcr.sl = 3;
|
||||
io_pgtable.cfg.arm_lpae_s2_cfg.vtcr.tsz = 4;
|
||||
|
||||
arm_smmu_make_s2_domain_ste(ste, &master, &smmu_domain);
|
||||
arm_smmu_make_s2_domain_ste(ste, &master, &smmu_domain, ats_enabled);
|
||||
}
|
||||
|
||||
static void arm_smmu_v3_write_ste_test_s2_to_abort(struct kunit *test)
|
||||
@@ -286,6 +340,48 @@ static void arm_smmu_v3_write_ste_test_bypass_to_s2(struct kunit *test)
|
||||
NUM_EXPECTED_SYNCS(2));
|
||||
}
|
||||
|
||||
static void arm_smmu_v3_write_ste_test_s1_to_s2(struct kunit *test)
|
||||
{
|
||||
struct arm_smmu_ste s1_ste;
|
||||
struct arm_smmu_ste s2_ste;
|
||||
|
||||
arm_smmu_test_make_cdtable_ste(&s1_ste, STRTAB_STE_1_S1DSS_SSID0,
|
||||
fake_cdtab_dma_addr);
|
||||
arm_smmu_test_make_s2_ste(&s2_ste, true);
|
||||
arm_smmu_v3_test_ste_expect_hitless_transition(test, &s1_ste, &s2_ste,
|
||||
NUM_EXPECTED_SYNCS(3));
|
||||
}
|
||||
|
||||
static void arm_smmu_v3_write_ste_test_s2_to_s1(struct kunit *test)
|
||||
{
|
||||
struct arm_smmu_ste s1_ste;
|
||||
struct arm_smmu_ste s2_ste;
|
||||
|
||||
arm_smmu_test_make_cdtable_ste(&s1_ste, STRTAB_STE_1_S1DSS_SSID0,
|
||||
fake_cdtab_dma_addr);
|
||||
arm_smmu_test_make_s2_ste(&s2_ste, true);
|
||||
arm_smmu_v3_test_ste_expect_hitless_transition(test, &s2_ste, &s1_ste,
|
||||
NUM_EXPECTED_SYNCS(3));
|
||||
}
|
||||
|
||||
static void arm_smmu_v3_write_ste_test_non_hitless(struct kunit *test)
|
||||
{
|
||||
struct arm_smmu_ste ste;
|
||||
struct arm_smmu_ste ste_2;
|
||||
|
||||
/*
|
||||
* Although no flow resembles this in practice, one way to force an STE
|
||||
* update to be non-hitless is to change its CD table pointer as well as
|
||||
* s1 dss field in the same update.
|
||||
*/
|
||||
arm_smmu_test_make_cdtable_ste(&ste, STRTAB_STE_1_S1DSS_SSID0,
|
||||
fake_cdtab_dma_addr);
|
||||
arm_smmu_test_make_cdtable_ste(&ste_2, STRTAB_STE_1_S1DSS_BYPASS,
|
||||
0x4B4B4b4B4B);
|
||||
arm_smmu_v3_test_ste_expect_non_hitless_transition(
|
||||
test, &ste, &ste_2, NUM_EXPECTED_SYNCS(3));
|
||||
}
|
||||
|
||||
static void arm_smmu_v3_test_cd_expect_transition(
|
||||
struct kunit *test, const struct arm_smmu_cd *cur,
|
||||
const struct arm_smmu_cd *target, unsigned int num_syncs_expected,
|
||||
@@ -439,10 +535,16 @@ static struct kunit_case arm_smmu_v3_test_cases[] = {
|
||||
KUNIT_CASE(arm_smmu_v3_write_ste_test_abort_to_cdtable),
|
||||
KUNIT_CASE(arm_smmu_v3_write_ste_test_cdtable_to_bypass),
|
||||
KUNIT_CASE(arm_smmu_v3_write_ste_test_bypass_to_cdtable),
|
||||
KUNIT_CASE(arm_smmu_v3_write_ste_test_cdtable_s1dss_change),
|
||||
KUNIT_CASE(arm_smmu_v3_write_ste_test_s1dssbypass_to_stebypass),
|
||||
KUNIT_CASE(arm_smmu_v3_write_ste_test_stebypass_to_s1dssbypass),
|
||||
KUNIT_CASE(arm_smmu_v3_write_ste_test_s2_to_abort),
|
||||
KUNIT_CASE(arm_smmu_v3_write_ste_test_abort_to_s2),
|
||||
KUNIT_CASE(arm_smmu_v3_write_ste_test_s2_to_bypass),
|
||||
KUNIT_CASE(arm_smmu_v3_write_ste_test_bypass_to_s2),
|
||||
KUNIT_CASE(arm_smmu_v3_write_ste_test_s1_to_s2),
|
||||
KUNIT_CASE(arm_smmu_v3_write_ste_test_s2_to_s1),
|
||||
KUNIT_CASE(arm_smmu_v3_write_ste_test_non_hitless),
|
||||
KUNIT_CASE(arm_smmu_v3_write_cd_test_s1_clear),
|
||||
KUNIT_CASE(arm_smmu_v3_write_cd_test_s1_change_asid),
|
||||
KUNIT_CASE(arm_smmu_v3_write_cd_test_sva_clear),
|
||||
@@ -465,4 +567,5 @@ static struct kunit_suite arm_smmu_v3_test_module = {
|
||||
kunit_test_suites(&arm_smmu_v3_test_module);
|
||||
|
||||
MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING);
|
||||
MODULE_DESCRIPTION("KUnit tests for arm-smmu-v3 driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user