mirror of
https://github.com/tianocore/edk2
synced 2026-08-27 00:23:19 -04:00
ArmPkg: Add SMMUv3 SmmuDxe driver with per-StreamID DMA isolation
Add a SMMUv3 DXE driver that implements the EDKII IOMMU protocol to provide DMA address translation and memory protection through Stage 1 xor Stage 2 translation. The driver consumes a SMMU_CONFIG HOB containing the platform IORT data, supports an arbitrary number of SMMUs, and dynamically parses any generic IORT structure to configure each SMMU accordingly. It installs both the IOMMU protocol and the IORT ACPI table. See ArmPkg/Drivers/SmmuDxe/README.md for flow diagrams, the HOB layout, and platform integration steps. Signed-off-by: Eeshan Londhe <eeshanlondhe@microsoft.com>
This commit is contained in:
parent
2970e5699b
commit
8c800cd41c
11 changed files with 10258 additions and 0 deletions
|
|
@ -98,6 +98,10 @@
|
|||
|
||||
gEdkiiTpmEventLogDescHobGuid = { 0x360c4a06, 0x146c, 0x11f0, { 0xb1, 0x73, 0x6b, 0xd5, 0x98, 0xff, 0x54, 0xd6 }}
|
||||
|
||||
## SMMU config data
|
||||
# Include/Guid/SmmuConfig.h
|
||||
gSmmuConfigHobGuid = { 0xcd56ec8f, 0x75f1, 0x440a, { 0xaa, 0x48, 0x09, 0x58, 0xb1, 0x1c, 0x9a, 0xa7 } }
|
||||
|
||||
[Protocols.common]
|
||||
## Arm System Control and Management Interface(SCMI) Base protocol
|
||||
## ArmPkg/Include/Protocol/ArmScmiBaseProtocol.h
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@
|
|||
ArmPkg/Universal/Smbios/OemMiscLibNull/OemMiscLibNull.inf
|
||||
|
||||
ArmPkg/Drivers/MmCommunicationPei/MmCommunicationPei.inf
|
||||
ArmPkg/Drivers/SmmuDxe/SmmuDxe.inf
|
||||
|
||||
ArmPkg/Library/FmpDevicePsaFwuLib/FmpDevicePsaFwuLib.inf
|
||||
|
||||
|
|
|
|||
1613
ArmPkg/Drivers/SmmuDxe/IoMmu.c
Normal file
1613
ArmPkg/Drivers/SmmuDxe/IoMmu.c
Normal file
File diff suppressed because it is too large
Load diff
57
ArmPkg/Drivers/SmmuDxe/IoMmu.h
Normal file
57
ArmPkg/Drivers/SmmuDxe/IoMmu.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/** @file IoMmu.h
|
||||
|
||||
This file is the IoMmu header file for SMMU driver.
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
Page Table bit definitions used by the Smmu/IoMmu for mapping.
|
||||
See ARM ARM section D8.3.1 (VMSAv8-64 descriptor formats):
|
||||
<https://developer.arm.com/documentation/ddi0487/mc/>
|
||||
|
||||
Note: shared architectural bit definitions (access flag, inner shareable,
|
||||
AP[2:1], etc.) are pulled from <AArch64/AArch64Mmu.h> (via
|
||||
<Library/ArmLib.h>) as TT_AF, TT_SH_INNER_SHAREABLE, TT_AP_RW_RW,
|
||||
TT_AP_RO_RO. Only Stage-2 specific and IOMMU-specific helpers are
|
||||
defined here.
|
||||
**/
|
||||
#define PAGE_TABLE_ENTRY_VALID_BIT 0x1
|
||||
#define PAGE_TABLE_BLOCK_MASK 0xFFF
|
||||
#define PAGE_TABLE_DESCRIPTOR (0x1 << 1)
|
||||
#define PAGE_TABLE_READ_WRITE_FROM_IOMMU_ACCESS(IoMmuAccess) (IoMmuAccess << 6)
|
||||
#define PAGE_TABLE_WRITE_BIT (0x1 << 7)
|
||||
#define PAGE_TABLE_S2_MEMATTR_NORMAL_WB (0xF << 2)
|
||||
|
||||
// Stage 1 VMSAv8-64 leaf descriptor AttrIndex bits (used when the SMMU
|
||||
// is configured for Stage 1 translation). AttrIndex 0 selects MAIR[0]
|
||||
// in the CD (Normal Inner+Outer WBWA per SmmuV3BuildStage1ContextDescriptor).
|
||||
#define PAGE_TABLE_S1_ATTRINDX0 (0x0 << 2)
|
||||
|
||||
typedef UINT64 PAGE_TABLE_ENTRY;
|
||||
|
||||
#define PAGE_TABLE_SIZE (EFI_PAGE_SIZE / sizeof(PAGE_TABLE_ENTRY)) // Number of entries in a page table
|
||||
|
||||
// Page Table Structure used by SMMU
|
||||
typedef struct _PAGE_TABLE {
|
||||
PAGE_TABLE_ENTRY Entries[PAGE_TABLE_SIZE];
|
||||
} PAGE_TABLE;
|
||||
|
||||
/**
|
||||
Installs the IOMMU Protocol on this SMMU instance.
|
||||
|
||||
@retval EFI_SUCCESS All the protocol interface was installed.
|
||||
@retval EFI_OUT_OF_RESOURCES There was not enough memory in pool to install all the protocols.
|
||||
@retval EFI_ALREADY_STARTED A Device Path Protocol instance was passed in that is already present in
|
||||
the handle database.
|
||||
@retval EFI_INVALID_PARAMETER Handle is NULL.
|
||||
@retval EFI_INVALID_PARAMETER Protocol is already installed on the handle specified by Handle.
|
||||
**/
|
||||
EFI_STATUS
|
||||
IoMmuInit (
|
||||
VOID
|
||||
);
|
||||
838
ArmPkg/Drivers/SmmuDxe/README.md
Normal file
838
ArmPkg/Drivers/SmmuDxe/README.md
Normal file
|
|
@ -0,0 +1,838 @@
|
|||
# SmmuDxe Driver
|
||||
|
||||
This document describes the System Memory Management Unit (SMMU) driver implementation, and how it integrates with the
|
||||
DMA capable subsystem. The driver configures the SMMUv3 hardware and implements the IOMMU protocol to provide address
|
||||
translation and memory protection for DMA operations.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The SmmuDxe driver will consume the SMMU_CONFIG HOB with the IORT data to configure the SMMUs found on the platform.
|
||||
It will set them up for Stage 2 Translation by default, or Stage 1 Translation when the platform explicitly opts in
|
||||
via `SMMU_CONFIG->TranslationStage` (see [Translation Stage](#translation-stage)). SmmuDxe will install the IoMmu
|
||||
Protocol. Translation table mapping can be done by leveraging the IoMmu Protocol. The protocol functions are outlined
|
||||
below. Seperatley, an IoMmuLib is provided for platforms to use as an abstraction for locating and using the IoMmu
|
||||
protocol. SmmuDxe will install the IORT ACPI Table. Platform should not install the IORT, but instead pass in the
|
||||
IORT data with the SMMU_CONFIG HOB.
|
||||
|
||||
The system consists of four main components working together:
|
||||
|
||||
1. **PCI I/O Protocol**: Provides interface for PCI / NonDiscoverable device access and DMA operations (handle-aware path)
|
||||
2. **DmaLib (`CoherentDmaLib` / `NonCoherentDmaLib`)**: Handle-less DMA path for firmware-internal agents; forwards
|
||||
caller-supplied `(IommuBase, DmaId)` to `IoMmuSetAttributeById`
|
||||
3. **IOMMU Protocol**: Implements DMA remapping and memory protection (`SetAttribute` and `SetAttributeById` entry points)
|
||||
4. **SMMU Hardware Driver**: Configures and manages the SMMU hardware
|
||||
|
||||
## IOMMU Protocol Integration
|
||||
|
||||
1. **PCI / NonDiscoverable Driver Initiates DMA** (handle-aware path):
|
||||
- Device driver calls `PciIo->Map()` / `PciIo->Unmap()`
|
||||
- SmmuDxe resolves the controller `EFI_HANDLE` → IORT → StreamID(s) + owning SMMU and programs the page table
|
||||
via `IoMmuSetAttribute`.
|
||||
|
||||
2. **Handle-less DMA Agent via DmaLib** (handle-less path):
|
||||
- Firmware-internal agents (no `EFI_HANDLE`, not in the IORT) call `DmaMap()` / `DmaUnmap()` from
|
||||
`EmbeddedPkg`'s `CoherentDmaLib` / `NonCoherentDmaLib`:
|
||||
|
||||
```c
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
DmaMap (
|
||||
IN DMA_MAP_OPERATION Operation,
|
||||
IN VOID *HostAddress,
|
||||
IN OUT UINTN *NumberOfBytes,
|
||||
IN UINT64 IommuBase, // SmmuV3 base address
|
||||
IN UINT32 DmaId, // StreamID on Arm SMMU
|
||||
OUT PHYSICAL_ADDRESS *DeviceAddress,
|
||||
OUT VOID **Mapping
|
||||
);
|
||||
```
|
||||
|
||||
- DmaLib calls `IoMmuMap` for bookkeeping, then forwards `(IommuBase, DmaId)` to `IoMmuSetAttributeById`.
|
||||
|
||||
3. **IOMMU Protocol Setup**:
|
||||
- Implements the IOMMU protocol:
|
||||
|
||||
```c
|
||||
struct _EDKII_IOMMU_PROTOCOL {
|
||||
UINT64 Revision;
|
||||
EDKII_IOMMU_SET_ATTRIBUTE SetAttribute;
|
||||
EDKII_IOMMU_MAP Map;
|
||||
EDKII_IOMMU_UNMAP Unmap;
|
||||
EDKII_IOMMU_ALLOCATE_BUFFER AllocateBuffer;
|
||||
EDKII_IOMMU_FREE_BUFFER FreeBuffer;
|
||||
EDKII_IOMMU_SET_ATTRIBUTE_BY_ID SetAttributeById;
|
||||
};
|
||||
```
|
||||
|
||||
- SmmuDxe will handle Translation Table initialization
|
||||
|
||||
## DMA Mapping with IoMmuLib and IoMmu Protocol
|
||||
|
||||
- Maintains up to a 4-level page table, depending on configuration, to map HostAddress and DeviceAddress
|
||||
- Identity Mapped
|
||||
|
||||
1. **IoMmu Map**:
|
||||
|
||||
```c
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
IoMmuMap (
|
||||
IN EDKII_IOMMU_OPERATION Operation,
|
||||
IN VOID *HostAddress,
|
||||
IN OUT UINTN *NumberOfBytes,
|
||||
OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
|
||||
OUT VOID **Mapping
|
||||
);
|
||||
```
|
||||
|
||||
- Maps HostAddress to DeviceAddress
|
||||
- Validates operation type
|
||||
- Called by PciIo protocol for mapping
|
||||
|
||||
### Bounce Buffering
|
||||
|
||||
In certain conditions, `IoMmuMap` will allocate a bounce buffer instead of using the original host address directly.
|
||||
A bounce buffer is a temporary intermediate buffer that is used when the original DMA buffer
|
||||
cannot be used directly by the device.
|
||||
|
||||
**Bounce Buffer Conditions:**
|
||||
|
||||
A bounce buffer is allocated when the operation is NOT `EdkiiIoMmuOperationBusMasterCommonBuffer` or
|
||||
`EdkiiIoMmuOperationBusMasterCommonBuffer64`, AND any of the following conditions are met:
|
||||
|
||||
1. **Alignment Requirements Not Met:**
|
||||
- Alignment check for the start and end of the DMA buffer are needed so that we don't over-map
|
||||
a buffer in the page table. Page table mappings can only be done page-by-page.
|
||||
- The `NumberOfBytes` is not 4KB aligned, OR
|
||||
- The `HostAddress` (PhysicalAddress) is not 4KB aligned
|
||||
|
||||
2. **32-bit DMA Limitation:**
|
||||
- The operation is a 32-bit DMA operation (`EdkiiIoMmuOperationBusMasterRead` or
|
||||
`EdkiiIoMmuOperationBusMasterWrite`), AND
|
||||
- Any part of the DMA transfer range (`PhysicalAddress + NumberOfBytes`) exceeds 4GB
|
||||
|
||||
**Bounce Buffer Behavior:**
|
||||
|
||||
- When a bounce buffer is needed due to **alignment issues only** (with 64-bit operations), memory can be allocated
|
||||
anywhere in the address space
|
||||
- When a bounce buffer is needed due to the **32-bit DMA limitation**, memory is allocated below 4GB using
|
||||
`AllocateMaxAddress` with `DmaMemoryTop` set to `SIZE_4GB - 1`
|
||||
- The `DeviceAddress` returned points to the bounce buffer, not the original host address
|
||||
- The `Mapping` handle stores information about both the original host address and the bounce buffer address
|
||||
|
||||
**CopyMem on Map (Host → Bounce Buffer):**
|
||||
|
||||
A `CopyMem` from the host buffer to the bounce buffer is performed during `IoMmuMap` when:
|
||||
|
||||
- A bounce buffer was allocated (NeedRemap is TRUE), AND
|
||||
- The operation is a **read operation** from the Bus Master's perspective:
|
||||
- `EdkiiIoMmuOperationBusMasterRead`, OR
|
||||
- `EdkiiIoMmuOperationBusMasterRead64`
|
||||
|
||||
This copy ensures the Bus Master can read the correct data from the bounce buffer during the DMA operation.
|
||||
For write operations, no copy is needed on Map since the Bus Master will write new data into the bounce buffer.
|
||||
|
||||
## DMA Unmapping with IoMmuLib and IoMmu Protocol
|
||||
|
||||
1. **PCI Driver Completes DMA**:
|
||||
- Calls PciIo->Unmap()
|
||||
- Provides mapping handle
|
||||
|
||||
2. **IoMmu Unmap**:
|
||||
|
||||
```c
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
IoMmuUnmap (
|
||||
IN VOID *Mapping
|
||||
);
|
||||
```
|
||||
|
||||
- Only does bounce buffer cleanup.
|
||||
- Caller must call SetAttributes(0) before IoMmuUnmap to remove R|W attributes to effectivley unmap the page table.
|
||||
|
||||
### Bounce Buffer Handling on Unmap
|
||||
|
||||
When unmapping a DMA operation that used a bounce buffer (i.e., `DeviceAddress != HostAddress`):
|
||||
|
||||
**CopyMem on Unmap (Bounce Buffer → Host):**
|
||||
|
||||
A `CopyMem` from the bounce buffer back to the host buffer is performed during `IoMmuUnmap` when:
|
||||
|
||||
- A bounce buffer was used (`DeviceAddress != HostAddress`), AND
|
||||
- The operation is a **write operation** from the Bus Master's perspective:
|
||||
- `EdkiiIoMmuOperationBusMasterWrite`, OR
|
||||
- `EdkiiIoMmuOperationBusMasterWrite64`
|
||||
|
||||
This copy ensures the processor can access the data that the Bus Master wrote into the bounce buffer.
|
||||
For read operations, no copy is needed on Unmap since the Bus Master only read data and did not modify it.
|
||||
|
||||
**Cleanup:**
|
||||
|
||||
- The bounce buffer pages are freed using `FreePages`
|
||||
- The mapping information structure is freed
|
||||
|
||||
For direct mappings (no bounce buffer), only the mapping information structure is freed.
|
||||
|
||||
### DMA Access Attributes with IoMmuLib and IoMmu Protocol
|
||||
|
||||
1. Setting R/W permissions
|
||||
- After mapping an address with IoMmuMap()
|
||||
- Clearning R/W permissions before unmmapping an address with IoMmuUnmap()
|
||||
- Sets access permissions based on IoMmuAccess type:
|
||||
- When IoMmuAccess is not 0, R|W permissions are set.
|
||||
- When IoMmuAccess is 0, no read or write access is permitted.
|
||||
|
||||
2. IoMmu SetAttribute
|
||||
|
||||
```c
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
IoMmuSetAttribute (
|
||||
IN EFI_HANDLE DeviceHandle,
|
||||
IN VOID *Mapping,
|
||||
IN UINT64 IoMmuAccess
|
||||
);
|
||||
```
|
||||
|
||||
`SetAttribute` is the **worker** of the IOMMU protocol - `IoMmuMap` only allocates the `IOMMU_MAP_INFO`
|
||||
bookkeeping (and a bounce buffer if needed); it does **not** touch the SMMU page tables. All real page-table
|
||||
mutation happens here, on the `SetAttribute` call that follows `Map` (and on the `SetAttribute(..., 0)` that
|
||||
precedes `Unmap`).
|
||||
|
||||
The `EFI_HANDLE DeviceHandle` parameter is how SmmuDxe knows *which* device (and therefore which StreamID(s) and
|
||||
which SMMU) to program:
|
||||
|
||||
1. `gBS->HandleProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, ...)` retrieves the `EFI_PCI_IO_PROTOCOL` instance
|
||||
installed on the handle. Both real PCIe devices (installed by `PciBusDxe`) and non-discoverable devices
|
||||
(installed by `NonDiscoverablePciDeviceDxe`) expose `PciIo` on the same handle the driver bound to.
|
||||
2. `PciIo->GetLocation (&Seg, &Bus, &Dev, &Func)` returns the BDF - for non-discoverable devices this is the
|
||||
synthesized `(0xFF, UniqueId>>5, UniqueId&0x1F, 0)`.
|
||||
3. `DeviceHandleToStreamId` uses `Seg` to dispatch: real PCIe goes through the IORT Root-Complex ID-mapping path,
|
||||
Segment `0xFF` goes through the NonDiscoverable lookup table + IORT Named Component path
|
||||
(see [Per-StreamID Isolation](#per-streamid-isolation)).
|
||||
4. The resolved primary StreamID's per-stream page-table root and tag (VMID on Stage 2, or ASID inside a
|
||||
Context Descriptor on Stage 1) are then used to actually install / invalidate the mapping for
|
||||
`MapInfo->DeviceAddress` with the requested `IoMmuAccess` permissions (or to tear it down when
|
||||
`IoMmuAccess == 0`).
|
||||
|
||||
This is also why the controller handle had to be plumbed through `NonDiscoverablePciDeviceDxe` - its
|
||||
`PciIoMap`/`PciIoUnmap` now pass `Dev->Handle` to `IoMmuSetAttribute` instead of the old `NULL`, so SmmuDxe can do
|
||||
the resolution above for non-discoverable devices the same way it does for real PCIe.
|
||||
|
||||
3. IoMmu SetAttributeById
|
||||
|
||||
```c
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
IoMmuSetAttributeById (
|
||||
IN EDKII_IOMMU_PROTOCOL *This,
|
||||
IN UINT64 IommuBase,
|
||||
IN UINT32 DmaId,
|
||||
IN VOID *Mapping,
|
||||
IN UINT64 IoMmuAccess
|
||||
);
|
||||
```
|
||||
|
||||
`SetAttributeById` is for callers that **do not** have an `EFI_HANDLE` for the DMA agent - for example, firmware
|
||||
internal MMIO/DMA agents that are not described in the IORT and have no UEFI device handle. The caller is
|
||||
responsible for supplying the owning SMMU/IOMMU base address (`IommuBase`) and the `DmaId` (StreamID on Arm SMMU,
|
||||
RequesterID on VT-d) emitted by the device. Only that single `DmaId` is programmed; no IORT lookup or alias
|
||||
resolution is performed.
|
||||
|
||||
The protocol now exposes both entry points:
|
||||
|
||||
```c
|
||||
struct _EDKII_IOMMU_PROTOCOL {
|
||||
UINT64 Revision; // EDKII_IOMMU_PROTOCOL_REVISION == 0x00010001
|
||||
EDKII_IOMMU_SET_ATTRIBUTE SetAttribute;
|
||||
EDKII_IOMMU_MAP Map;
|
||||
EDKII_IOMMU_UNMAP Unmap;
|
||||
EDKII_IOMMU_ALLOCATE_BUFFER AllocateBuffer;
|
||||
EDKII_IOMMU_FREE_BUFFER FreeBuffer;
|
||||
EDKII_IOMMU_SET_ATTRIBUTE_BY_ID SetAttributeById; // Optional; callers must NULL-check.
|
||||
};
|
||||
```
|
||||
|
||||
`EDKII_IOMMU_PROTOCOL_REVISION` was bumped to `0x00010001` to advertise `SetAttributeById`. Legacy producers that do
|
||||
not implement `SetAttributeById` leave the field `NULL`; callers (and `IoMmuLib::IoMmuSetAttributeById`) must check
|
||||
before invoking.
|
||||
|
||||
## Per-StreamID Isolation
|
||||
|
||||
Each DMA-capable device gets its own per-stream page-table root and per-stream tag. For Stage 2 the tag is a
|
||||
**VMID** encoded directly in the STE; for Stage 1 the tag is an **ASID** stored in a per-stream Context Descriptor
|
||||
(CD) whose address the STE's `S1ContextPtr` field points at. Mappings made for one device are **not** visible to
|
||||
any other device. Conceptually, on every `IoMmuSetAttribute` / `IoMmuSetAttributeById` call the driver:
|
||||
|
||||
1. Resolves the caller (device handle, or a caller-supplied `(IommuBase, DmaId)` for the handle-less path) to:
|
||||
- One or more StreamIDs.
|
||||
- The base address of the SMMU node that owns them.
|
||||
|
||||
For handle-aware callers this resolution walks the platform IORT (and the optional NonDiscoverable lookup table -
|
||||
see [Non-Discoverable Device Integration](#non-discoverable-device-integration)).
|
||||
2. Selects the matching SMMU instance by that base address.
|
||||
3. For the **primary** (first) StreamID, lazily allocates a per-stream page-table root plus:
|
||||
- **Stage 2:** a VMID, then promotes the Stream Table Entry (STE) from `INVALID` to `STAGE_2_TRANSLATE`
|
||||
(break-before-make).
|
||||
- **Stage 1:** a Context Descriptor plus an ASID (the CD's `Ttb0` points at the root, `Asid` holds the tag),
|
||||
then promotes the STE from `INVALID` to `STAGE_1_TRANSLATE / STAGE_2_BYPASS` with `S1ContextPtr` pointing at
|
||||
the CD.
|
||||
4. Aliases every additional StreamID reported for the same device onto the primary's per-stream state:
|
||||
- **Stage 2:** the alias STE is promoted to carry the same `S2Ttb + S2VMID` as the primary.
|
||||
- **Stage 1:** the alias STE is promoted to point at the **same CD** as the primary via `S1ContextPtr`, so all
|
||||
aliases share the same ASID + Stage 1 root.
|
||||
|
||||
A single page-table update therefore covers DMA from all StreamIDs that belong to one logical device.
|
||||
5. Updates the page table with the requested permissions (or tears the mapping down when permissions are cleared).
|
||||
|
||||
### Page table root and per-stream tag
|
||||
|
||||
Per-stream page tables on the SMMU are tagged by VMID (Stage 2) or ASID (Stage 1). The SMMU caches TLB entries
|
||||
keyed by that tag, and the STE (Stage 2) or CD (Stage 1) carries the tag field that selects which page tables the
|
||||
SMMU walks for that stream. Two streams with two different page-table roots **must** have two different tags.
|
||||
|
||||
Each page-table root therefore needs its own tag. Each per-stream context gets a unique tag unless the driver
|
||||
explicitly wants to share that root with another stream (the alias case below):
|
||||
|
||||
- **Stage 2**: The STE itself is the single source of truth for the `(Root, Vmid)` binding. On the first mapping
|
||||
for a StreamID a fresh VMID and root are allocated and the STE is promoted; on subsequent mappings the existing
|
||||
binding is read back from the live STE's `S2Ttb` / `S2Vmid` fields. VMID `0` is reserved as "unassigned".
|
||||
- **Stage 1**: The STE + CD together are the source of truth. `S1ContextPtr` locates the CD; the CD's `Ttb0` /
|
||||
`Asid` fields hold the root pointer and the ASID tag. ASID `0` is reserved as "unassigned". Per SMMUv3 §5.2,
|
||||
when only Stage 1 is enabled the STE's `S2VMID` field is ignored and Stage 1 TLB entries are tagged with
|
||||
VMID = 0; TLB invalidation uses `CMD_TLBI_NH_ASID` with VMID = 0.
|
||||
- Tag width comes from the SMMU's `IDR0.VMID16` (Stage 2) or `IDR0.ASID16` (Stage 1) capability: 8-bit SMMUs use
|
||||
values `1..0xFF`, 16-bit SMMUs use `1..0xFFFF`. A tag is never reused for a different root within the same boot.
|
||||
- Aliasing does the inverse: given the **primary's** state, the alias STE is promoted in place to reference the
|
||||
same state (same `S2Ttb + S2VMID` on Stage 2, same `S1ContextPtr` on Stage 1). If the alias STE already encodes
|
||||
the same state this is a no-op. That's how multiple StreamIDs share one mapping - they all resolve through the
|
||||
same tag, so a single TLB invalidation covers every alias.
|
||||
- On unmap the driver invalidates by VMID (Stage 2) or ASID (Stage 1). Because the tag is unique per page-table
|
||||
root, that invalidation can't accidentally evict another device's translations.
|
||||
|
||||
### End-to-end Flow Diagram
|
||||
|
||||
#### Driver Initialization
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Entry[Driver entry point] --> Hob[Read SMMU_CONFIG HOB<br/>gSmmuConfigHobGuid]
|
||||
Hob --> Ver[Validate config structure version]
|
||||
Ver --> Loc[Locate ACPI Table + GIC interrupt protocols]
|
||||
Loc --> Evt[Register ExitBootServices callback]
|
||||
Evt --> Cfg[Allocate IOMMU state]
|
||||
Cfg --> Parse[Parse IORT<br/>discover SMMU nodes]
|
||||
Parse --> SaveIort[Save IORT pointer for<br/>runtime StreamID resolution]
|
||||
SaveIort --> NcTbl{NonDiscoverable<br/>lookup table<br/>present in HOB?}
|
||||
NcTbl -- Yes --> NcOn[Save NC device table pointer]
|
||||
NcTbl -- No --> NcOff[NC StreamID resolution disabled]
|
||||
NcOn --> Inst
|
||||
NcOff --> Inst[Install IORT as an ACPI table]
|
||||
Inst --> Dis[Apply SmmuDisabledList<br/>mark each SMMU enabled/disabled]
|
||||
Dis --> EnLoop["For each enabled SMMU:<br/>Program SMMU for Stage-2 (default)<br/>or Stage-1 Translation"]
|
||||
EnLoop --> DisLoop[For each disabled SMMU:<br/>disable translation, set global bypass]
|
||||
DisLoop --> Install[Install IOMMU protocol]
|
||||
Install --> Ready([SMMU ready;<br/>STEs sit in INVALID until promoted<br/>by the first mapping call])
|
||||
```
|
||||
|
||||
After init the SMMU hardware is configured but **no device is mapped yet**. Every STE is in `INVALID` until
|
||||
promoted, so any unsolicited DMA is dropped. STEs are promoted (`STAGE_2_TRANSLATE` by default, or
|
||||
`STAGE_1_TRANSLATE / STAGE_2_BYPASS` when the platform opted into Stage 1) lazily on the first
|
||||
`IoMmuSetAttribute` / `IoMmuSetAttributeById` call for that StreamID.
|
||||
|
||||
#### Runtime Map / SetAttribute / Unmap
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Drv["PCI / NonDiscoverable<br/>device driver"] -->|PciIo->Map| Map[IoMmuMap<br/>bookkeeping only]
|
||||
DmaDrv["Handle-less DMA agent<br/>(no EFI_HANDLE)"] -->|DmaMap IommuBase, DmaId| Map
|
||||
Map -->|allocate mapping info<br/>+ optional bounce buffer| MapDone[(Begin stream configuration<br/>for DMA isolation)]
|
||||
MapDone -->|PciIo path| SA[IoMmuSetAttribute<br/>DeviceHandle, Mapping, IoMmuAccess]
|
||||
MapDone -->|DmaLib path| SAX[IoMmuSetAttributeById]
|
||||
|
||||
SA --> H[Retrieve PciIo on DeviceHandle]
|
||||
H --> GL["Query device location<br/>Seg/Bus/Dev/Func"]
|
||||
GL -->|Seg != 0xFF| RC[IORT Root Complex<br/>RID -> ID mapping -> StreamID + SMMU base]
|
||||
GL -->|Seg == 0xFF| NC["Reconstruct UniqueId<br/>from synthesized BDF"]
|
||||
NC --> Tbl[NC lookup table<br/>UniqueId -> ObjectName]
|
||||
Tbl --> Nc2[IORT Named Component node<br/>ID mappings -> StreamIDs + SMMU base]
|
||||
|
||||
RC --> Sel[Find SMMU matching resolved base]
|
||||
Nc2 --> Sel
|
||||
SAX -->|caller-supplied SMMU base + StreamId| Sel
|
||||
|
||||
Sel --> Pri{Primary StreamID<br/>context exists?}
|
||||
Pri -- No --> Alloc[Allocate per-stream root<br/>+ assign VMID/ASID<br/>+ CD on Stage 1]
|
||||
Alloc --> Promote["Promote STE<br/>INVALID -> STAGE_2_TRANSLATE or STAGE_1_TRANSLATE<br/>break-before-make"]
|
||||
Promote --> Aliases
|
||||
Pri -- Yes --> Aliases[For each alias StreamID:<br/>bind STE to primary's root + tag<br/><i>Stage 2: shared S2Ttb + VMID<br/>Stage 1: shared CD via S1ContextPtr</i><br/><i>SetAttributeById skips this step</i>]
|
||||
|
||||
Aliases --> PT{IoMmuAccess != 0?}
|
||||
PT -- Yes --> Upd[Update page table<br/>identity-map DeviceAddress<br/>set R/W flags]
|
||||
PT -- No --> Inv["Invalidate page-table entry<br/>+ TLB invalidate per-VMID (Stage 2)<br/>or per-ASID (Stage 1)"]
|
||||
|
||||
Upd --> Done([Return])
|
||||
Inv --> Done
|
||||
|
||||
Done --> Unmap[IoMmuUnmap<br/>preceded by SetAttribute / SetAttributeById with IoMmuAccess=0]
|
||||
Unmap --> Free["Free bounce buffer if any<br/>Free mapping info<br/>STE/page-table stay until reboot"]
|
||||
```
|
||||
|
||||
The diagram shows three caller paths that converge on the same per-StreamID isolation logic:
|
||||
|
||||
- **Map only does bookkeeping.** It allocates the mapping info (and a bounce buffer if needed) and returns a
|
||||
handle. No STE or page-table is touched. The `DmaLib` wrappers (`CoherentDmaLib` / `NonCoherentDmaLib`) call
|
||||
straight into `IoMmuMap` and then forward the caller-supplied `(IommuBase, DmaId)` to `IoMmuSetAttributeById`
|
||||
instead of using a handle.
|
||||
- **SetAttribute is the worker for handle-aware callers.** It resolves the handle to StreamID(s) and SMMU base via
|
||||
the IORT, lazily creates a per-stream page-table root and tag (VMID for Stage 2, ASID + CD for Stage 1) and
|
||||
promotes the STE on first use, aliases additional StreamIDs onto that same state, then either installs
|
||||
(`IoMmuAccess != 0`) or tears down (`IoMmuAccess == 0`) the identity mapping for the DMA address.
|
||||
- **SetAttributeById is the handle-less variant** used by `DmaLib` (and any other firmware-internal DMA agent that
|
||||
isn't described in the IORT). It skips both the handle-to-location lookup and the alias-binding loop - only the
|
||||
single `(IommuBase, DmaId)` the caller provided is promoted and programmed.
|
||||
- **Unmap** is purely cleanup of the mapping info and any bounce buffer. The STE and page tables stay promoted for
|
||||
the lifetime of UEFI; on `ExitBootServices` the SMMU is flipped to global bypass + BME-off so the OS can take
|
||||
over cleanly.
|
||||
|
||||
## Non-Discoverable Device Integration
|
||||
|
||||
Non-PCI MMIO devices registered through `MdeModulePkg/Library/NonDiscoverableDeviceRegistrationLib` are bound by
|
||||
`NonDiscoverablePciDeviceDxe`, which synthesizes a `PciIo` instance over them. The driver no longer auto-assigns a
|
||||
counter-based BDF; instead the platform supplies a deterministic `UniqueId` at registration time:
|
||||
|
||||
```c
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
RegisterNonDiscoverableMmioDevice (
|
||||
IN UINTN UniqueId, // platform-assigned, must be unique
|
||||
IN NON_DISCOVERABLE_DEVICE_TYPE Type,
|
||||
IN NON_DISCOVERABLE_DEVICE_DMA_TYPE DmaType,
|
||||
IN NON_DISCOVERABLE_DEVICE_INIT InitFunc,
|
||||
IN OUT EFI_HANDLE *Handle OPTIONAL,
|
||||
IN UINTN NumMmioResources,
|
||||
...
|
||||
);
|
||||
```
|
||||
|
||||
The `UniqueId` is stored on the `NON_DISCOVERABLE_DEVICE` protocol and copied into the `NON_DISCOVERABLE_PCI_DEVICE`
|
||||
instance by `NonDiscoverablePciDeviceDxe`. `PciIo->GetLocation()` then returns a deterministic
|
||||
`(Segment = 0xFF, Bus = UniqueId >> 5, Device = UniqueId & 0x1F, Function = 0)`. SmmuDxe uses Segment `0xFF` to
|
||||
detect a non-discoverable device and switch from the PCI Root-Complex lookup path to the Named Component lookup
|
||||
path.
|
||||
|
||||
`NonDiscoverablePciDeviceDxe` also remembers the controller `EFI_HANDLE` on the `NON_DISCOVERABLE_PCI_DEVICE` and
|
||||
passes it (instead of `NULL`) to `IoMmuSetAttribute` from its `PciIoMap` / `PciIoUnmap` implementations, so SmmuDxe
|
||||
can resolve the DMA identifier through the IORT.
|
||||
|
||||
### Mapping a Non-Discoverable Device to an IORT Named Component
|
||||
|
||||
To enable SMMU translation for a non-discoverable device, the platform must:
|
||||
|
||||
1. Add a **Named Component** node to the IORT whose `ObjectName` matches the device (e.g. `"USB"`) and whose
|
||||
ID mappings produce the StreamIDs the device emits. The Named Component's `OutputReference` must point at the
|
||||
SMMUv3 node that owns those StreamIDs.
|
||||
2. Publish a **NonDiscoverable lookup table** in the `SMMU_CONFIG` HOB that maps each registered `UniqueId` to the
|
||||
matching IORT NC `ObjectName`:
|
||||
|
||||
```c
|
||||
typedef struct _SMMU_NC_DEVICE_ENTRY {
|
||||
UINT64 UniqueId; // Same UniqueId passed to RegisterNonDiscoverableMmioDevice().
|
||||
CHAR8 ObjName[SMMU_NC_DEVICE_OBJNAME_MAX]; // IORT Named Component ObjectName (NUL-terminated).
|
||||
} SMMU_NC_DEVICE_ENTRY;
|
||||
```
|
||||
|
||||
Example - one xHCI controller registered with `UniqueId == 1` whose IORT Named Component node is named `"USB"`,
|
||||
plus an SATA AHCI controller registered with `UniqueId == 2`:
|
||||
|
||||
```c
|
||||
STATIC CONST SMMU_NC_DEVICE_ENTRY NcDeviceTable[] = {
|
||||
// { UniqueId, IORT NC ObjectName }
|
||||
{ 0x1, "USB" },
|
||||
{ 0x2, "SATA_AHCI" },
|
||||
};
|
||||
```
|
||||
|
||||
Each entry's `UniqueId` is exactly the value the platform passed to `RegisterNonDiscoverableMmioDevice`, and the
|
||||
`ObjName` must character-for-character match the `ObjectName` field of an `EFI_ACPI_IORT_TYPE_NAMED_COMP` node in
|
||||
the IORT blob. SmmuDxe uses this two-step lookup (UniqueId → ObjectName → IORT NC node → StreamID list +
|
||||
owning SMMU base) every time a non-discoverable device calls `IoMmuSetAttribute`.
|
||||
|
||||
3. Point the new `NcDeviceListSize`/`NcDeviceListOffset` fields in `SMMU_CONFIG` at that table (see
|
||||
[SMMU_CONFIG HOB Layout](#smmu_config-hob-layout)).
|
||||
|
||||
At translation time, when `PciIo->GetLocation()` returns Segment `0xFF`, SmmuDxe reconstructs the original
|
||||
`UniqueId` from `(Bus << 5) | (Device & 0x1F)`, looks it up in the NC table to get an `ObjectName`, walks the IORT
|
||||
for a Named Component node with that name, and **uses every StreamID in that node's ID mappings** - not just one.
|
||||
|
||||
This is where the alias logic from [Per-StreamID Isolation](#per-streamid-isolation) begins: the first StreamID
|
||||
returned becomes the **primary** and gets its own per-stream page-table root and tag (a VMID on Stage 2, or an
|
||||
ASID inside a Context Descriptor on Stage 1). Every other StreamID emitted by the same IORT NC node is then
|
||||
**aliased** so its STE points at the *same* root and uses the *same* tag - on Stage 2 the alias STE carries the
|
||||
same `S2Ttb + S2VMID` as the primary, on Stage 1 the alias STE's `S1ContextPtr` points at the primary's CD, so
|
||||
all aliases share the same ASID + Stage 1 root. The device therefore sees one unified mapping no matter which of
|
||||
its StreamIDs the transaction was tagged with.
|
||||
|
||||
```text
|
||||
NC HOB IORT Named Component SMMU per-stream state
|
||||
+-----------+ match +---------------------+ ID maps +-------------------------------------------+
|
||||
| UniqueId | --------> | ObjectName = "USB" | ----------> | Primary SID -> Root R, tag T
|
||||
| ObjName | | - StreamID 0x10 | | Alias SID -> Root R, tag T (shared)
|
||||
+-----------+ | - StreamID 0x11 | | Alias SID -> Root R, tag T (shared)
|
||||
+---------------------+ +-------------------------------------------+
|
||||
tag = VMID (Stage 2, in STE)
|
||||
= ASID (Stage 1, in shared CD via S1ContextPtr)
|
||||
```
|
||||
|
||||
Adding or removing alias StreamIDs is therefore purely an IORT edit - the platform NC table stays small and only
|
||||
needs one entry per logical device.
|
||||
|
||||
## SMMU_CONFIG HOB Layout
|
||||
|
||||
The platform builds one HOB (`gSmmuConfigHobGuid`) containing the `SMMU_CONFIG` header followed (in any order) by the
|
||||
IORT blob, the optional SMMU disable list, and the optional NonDiscoverable device lookup table. Each section is
|
||||
located via its offset and size in the header:
|
||||
|
||||
```c
|
||||
typedef struct _SMMU_CONFIG {
|
||||
UINT32 VersionMajor;
|
||||
UINT32 VersionMinor;
|
||||
UINT32 SmmuDisabledListSize; // Size of SmmuDisabledList in bytes.
|
||||
UINT32 SmmuDisabledListOffset; // Offset to the SmmuDisabledList from the start of the HOB.
|
||||
UINT32 IortSize;
|
||||
UINT32 IortOffset; // Offset to the IORT table from the start of the HOB.
|
||||
UINT32 NcDeviceListSize; // Size of the NonDiscoverable lookup array, in bytes.
|
||||
UINT32 NcDeviceListOffset; // Offset to the NonDiscoverable lookup array. 0 if absent.
|
||||
SMMU_TRANSLATION_STAGE TranslationStage; // SmmuTranslationStage2 (default) or SmmuTranslationStage1.
|
||||
// Applies to every SMMU described by this HOB.
|
||||
} SMMU_CONFIG;
|
||||
```
|
||||
|
||||
`SMMU_TRANSLATION_STAGE` is a shared enum defined in `<Guid/SmmuConfig.h>`:
|
||||
|
||||
```c
|
||||
typedef enum _SMMU_TRANSLATION_STAGE {
|
||||
SmmuTranslationStage2 = 0, // Stage 2 translate, Stage 1 bypass (default)
|
||||
SmmuTranslationStage1 = 1, // Stage 1 translate, Stage 2 bypass
|
||||
} SMMU_TRANSLATION_STAGE;
|
||||
```
|
||||
|
||||
Any value other than `SmmuTranslationStage1` is coerced to `SmmuTranslationStage2` by SmmuDxe.
|
||||
|
||||
Visual layout (one possible ordering):
|
||||
|
||||
```text
|
||||
+----------------------------+
|
||||
| SMMU_CONFIG | header
|
||||
+----------------------------+ <-- IortOffset
|
||||
| IORT table data | IortSize bytes
|
||||
+----------------------------+ <-- NcDeviceListOffset
|
||||
| SMMU_NC_DEVICE_ENTRY[ N ] | NcDeviceListSize bytes (multiple of sizeof(SMMU_NC_DEVICE_ENTRY))
|
||||
+----------------------------+ <-- SmmuDisabledListOffset (optional)
|
||||
| UINT64 disabledBases[] | SmmuDisabledListSize bytes
|
||||
+----------------------------+
|
||||
```
|
||||
|
||||
## SMMU Configuration
|
||||
|
||||
### 1. SMMUv3 Hardware Setup
|
||||
|
||||
The SMMU is configured in **Stage 2 translation** mode by default, or in **Stage 1 translation** mode when the
|
||||
platform sets `SMMU_CONFIG->TranslationStage = SmmuTranslationStage1`. Every SMMU described by the HOB is
|
||||
configured for the same stage (see [Translation Stage](#translation-stage)). Both modes bring up:
|
||||
|
||||
- Stream table for device ID mapping
|
||||
- Command queue for SMMU operations, like TLB management
|
||||
- Event queue for error handling
|
||||
- 4KB translation granule
|
||||
|
||||
Stage 1 additionally allocates a per-stream Context Descriptor (CD) whose `S1ContextPtr` is installed in the STE
|
||||
so the SMMU can locate the CD (and through it, the Stage 1 root and ASID) when translating that stream.
|
||||
|
||||
### Translation Stage
|
||||
|
||||
- **Stage 2 (default, `SmmuTranslationStage2`)**: The SMMU installs `STAGE_2_TRANSLATE / STAGE_1_BYPASS` STEs.
|
||||
Requires `IDR0.S2p`. Each StreamID's page-table root is encoded directly in the STE (`S2Ttb`, `S2VMID`).
|
||||
- **Stage 1 (`SmmuTranslationStage1`)**: The SMMU installs `STAGE_1_TRANSLATE / STAGE_2_BYPASS` STEs. Requires
|
||||
`IDR0.S1p`. Each StreamID's page-table root and ASID live in a Context Descriptor (64-byte, page-aligned); the
|
||||
STE's `S1ContextPtr` points at the CD. Stage 1 TLB entries are tagged with VMID = 0 (SMMUv3 §5.2) and
|
||||
invalidation uses `CMD_TLBI_NH_ASID`. Concatenation at the starting level is not architecturally allowed for
|
||||
Stage 1, so the walker uses a single-page root and dynamically picks L0 (OAS > 39 b) or L1 (OAS ≤ 39 b) as the
|
||||
starting level.
|
||||
|
||||
Choice of stage is a policy decision between platforms - the identity-mapping semantics that DMA agents observe
|
||||
are identical.
|
||||
|
||||
### 2. Page Table Structure
|
||||
|
||||
The IOMMU uses up to a 4-level page table structure for DMA address translation:
|
||||
<https://developer.arm.com/documentation/101811/0104/Translation-granule/The-starting-level-of-address-translation>
|
||||
|
||||
Depending on configuration from the SMMU registers, the starting level of translation is chosen. Stage 2 supports
|
||||
Concatenated Translation Tables at the starting level for wide OAS (Arm ARM D8.2.2); Stage 1 is architecturally
|
||||
forbidden from concatenating at the starting level (Arm ARM D8.5.2), so on Stage 1 the walker always installs a
|
||||
single-page root and picks the starting level dynamically:
|
||||
|
||||
- **Stage 2 (default)**: 16-page concatenated root; L1 start for OAS < 44 b, L0 start for OAS ≥ 44 b.
|
||||
- **Stage 1**: single-page root; L1 start for OAS ≤ 39 b, L0 start otherwise. Full 48-bit input is covered by a
|
||||
single L0 page (512 entries × 512 GB).
|
||||
|
||||
### 3. Address Translation Process
|
||||
|
||||
1. **Device Issues DMA**:
|
||||
- Device uses IOVA (I/O Virtual Address)
|
||||
- SMMU intercepts access
|
||||
|
||||
2. **SMMU Translation**:
|
||||
- Looks up Stream Table Entry (STE)
|
||||
- 2 level or Linear Stream Table. Depending on configurable maximum StreamId via IORT.
|
||||
- Stage 2: walks up to 4-level Stage 2 page tables directly from `STE.S2Ttb`.
|
||||
- Stage 1: dereferences `STE.S1ContextPtr` to fetch the Context Descriptor, then walks up to 4-level Stage 1
|
||||
page tables from `CD.Ttb0`.
|
||||
- Converts IOVA to PA (Physical Address)
|
||||
|
||||
### Leaf Page Table Entry Updates and Break-Before-Make
|
||||
|
||||
ARM's break-before-make (BBM) rule applies when a *live* leaf entry's PA / attributes change in place - without an
|
||||
Invalid+TLBI step in between, a concurrent walk can observe a torn entry. `UpdateMapping` (`IoMmu.c`) skips BBM
|
||||
because the only transitions it performs on a leaf are **Invalid ↔ Valid**; PA and flags are never rewritten on a
|
||||
Valid entry.
|
||||
|
||||
That invariant is enforced on the map path: if the leaf is already valid, the new `Entry` must be byte-for-byte
|
||||
identical, otherwise `EFI_DEVICE_ERROR` is returned instead of overwriting in place.
|
||||
|
||||
### TLB Invalidation
|
||||
|
||||
The SMMU TLB caches translations tagged by the per-stream tag (VMID for Stage 2, ASID for Stage 1). The driver
|
||||
therefore issues a different invalidation command depending on the configured translation stage.
|
||||
|
||||
| Stage | Helper | Command | Notes |
|
||||
|---------|-------------------------------|------------------------------------------------------|-------|
|
||||
| Stage 2 | `SmmuV3TLBInvalidateAllStage2` | `CMD_TLBI_S12_VMALL(Vmid)` | Invalidates **all** Stage 1 + Stage 2 TLB entries owned by the given `VMID`. VMID is read from the live STE (`S2Vmid`). VMID `0` is reserved and rejected. |
|
||||
| Stage 1 | `SmmuV3TLBInvalidateAllStage1` | `CMD_TLBI_NH_ASID(VMID = 0, Asid)` | The invalidation scope is equivalent to that of ASIDE1: when issued from the Non-secure Command queue, invalidates stage 1 NS-EL1 non-global entries by ASID and VMID. When only Stage 1 is enabled the STE's `S2VMID` is ignored and Stage 1 TLB entries are tagged with `VMID = 0`, so the invalidation always targets `VMID = 0`. ASID `0` is reserved and rejected. |
|
||||
|
||||
Because per-stream tags (VMID/ASID) are unique per page-table root within the boot
|
||||
(see [Page table root and per-stream tag](#page-table-root-and-per-stream-tag)), an invalidation for one device
|
||||
cannot evict another device's translations even though the command targets an entire tag rather than a single IPA.
|
||||
|
||||
## Memory Protection
|
||||
|
||||
The IOMMU protocol provides several protection mechanisms:
|
||||
|
||||
1. **Access Control**:
|
||||
- Read/Write permissions per mapping
|
||||
- Device isolation through Stream IDs
|
||||
|
||||
2. **Address Range Protection**:
|
||||
- Validates DMA addresses
|
||||
- Prevents access outside mapped regions
|
||||
|
||||
3. **Error Handling**:
|
||||
- Translation faults logged to Event Queue
|
||||
- See [SMMU Fault Reporting via GIC Interrupts](#smmu-fault-reporting-via-gic-interrupts) for how faults are
|
||||
surfaced at runtime.
|
||||
|
||||
### SMMU Fault Reporting via GIC Interrupts
|
||||
|
||||
SmmuDxe hooks the SMMU's two error-reporting interrupts so translation faults and command/global errors are surfaced
|
||||
the moment they happen instead of being noticed only on the next mapping call:
|
||||
|
||||
- **EVTQ IRQ** - fires when the SMMU pushes an entry into its **Event Queue** (e.g. a Stage 1 or Stage 2
|
||||
translation fault for some StreamID, a forbidden access, a CD/STE fetch fault, etc.). Each SMMU carries the
|
||||
per-SMMU `EvtqIrqNum` parsed from the IORT SMMUv3 node.
|
||||
- **GERR IRQ** - fires on **Global Errors** reported through `GERROR` / `GERRORN` (command-queue consumer faults,
|
||||
MSI write errors, queue overflow, etc.). Tracked as `GerrIrqNum`.
|
||||
|
||||
#### Configuration path
|
||||
|
||||
1. During driver init the SMMU driver locates the platform's GIC interrupt producer (the EDK II
|
||||
`HardwareInterrupt2` protocol) and caches it. If the protocol isn't present, IRQ hookup is skipped
|
||||
(non-fatal) - faults will only be surfaced on the next mapping call.
|
||||
2. For each enabled SMMU, both the EVTQ IRQ and the GERR IRQ are registered with a shared ISR: the source is bound
|
||||
to the handler, configured as edge-rising, and enabled at the GIC.
|
||||
3. The SMMU itself is programmed (in the same configure path) to deliver these IRQs as wired SPIs through the GIC.
|
||||
MSI delivery is not used here - the IORT-supplied SPI numbers are routed directly.
|
||||
|
||||
#### Runtime handling
|
||||
|
||||
The shared ISR runs at GIC dispatch:
|
||||
|
||||
1. Walks the driver's SMMU list to find the enabled SMMU whose EVTQ IRQ or GERR IRQ matches the firing source.
|
||||
2. Drains and pretty-prints every queued Event Queue entry plus the latched `GERROR` bits, then asserts so the
|
||||
failure is loud during development.
|
||||
3. Always signals end-of-interrupt to the GIC to deassert the IRQ.
|
||||
|
||||
The same error-logging is also invoked opportunistically from `IoMmuSetAttribute` / `IoMmuSetAttributeById` so
|
||||
anything that slipped past the IRQ path (or was raised while interrupts were masked) is still surfaced on the next
|
||||
mapping call.
|
||||
|
||||
## Continuous Protection Through the OS
|
||||
|
||||
### SMMU Initialization Pre-DXE
|
||||
|
||||
To ensure continuous protection, the SMMU should be set in abort mode during the pre-DXE phase. This means that the SMMU
|
||||
will block all transactions unless explicitly configured to allow them. This setup ensures that no unauthorized DMA
|
||||
operations can occur before the SMMU is fully configured.
|
||||
|
||||
### Interaction with PcdDisableBMEonEBS
|
||||
|
||||
The PcdDisableBMEonEBS (Disable Bus Master Enable on Exit Boot Services) setting plays a crucial role in maintaining
|
||||
system security during the transition from firmware to the operating system.
|
||||
|
||||
The following steps outline the end-to-end interaction:
|
||||
|
||||
1. Pre-DXE Initialization:
|
||||
|
||||
The SMMU is set to abort mode to block all transactions. This ensures that no DMA operations can bypass the SMMU's
|
||||
protection mechanisms.
|
||||
|
||||
2. DMA Remapping with SMMU:
|
||||
|
||||
The SMMU is configured to translate addresses and manage memory protection.
|
||||
The SMMU's page tables and stream tables are set up to allow only authorized DMA operations.
|
||||
|
||||
3. Exit Boot Services (EBS):
|
||||
|
||||
`SmmuV3ExitBootServices` runs at `TPL_NOTIFY` for every enabled SMMU and decides per-SMMU between two terminal
|
||||
states based on `SMMU_INFO->EBSBehaviorAbort`:
|
||||
|
||||
- **Default - `SmmuV3GlobalAbort`.** The SMMU is flipped into global-abort: every StreamID, including those that
|
||||
were translated during UEFI, has its transactions aborted. Translation is then disabled
|
||||
(`SmmuV3DisableTranslation`). This is the safe default: nothing can DMA until the OS reprograms the SMMU.
|
||||
- **Opt-out for SMMUs with RMR-mapped streams - `SmmuV3SetGlobalBypass`.** If any IORT RMR range was pre-mapped
|
||||
under this SMMU during init (`EBSBehaviorAbort == FALSE`), the SMMU is set to global-bypass instead so the
|
||||
reserved-memory devices the firmware handed off to the OS (UEFI runtime framebuffer, etc.) keep working until
|
||||
the OS rebinds them. Translation is still disabled. **Other SMMUs without RMRs are unaffected - their
|
||||
StreamIDs are still protected** until the OS reconfigures them.
|
||||
- In addition, `PcdDisableBMEonEBS` controls clearing the PCI Bus Master Enable bit on PCI controllers, which
|
||||
prevents any PCIe device from initiating new DMA in the abort window.
|
||||
|
||||
The net effect: SMMUs that must keep RMR streams alive go to bypass; every other SMMU stays in abort. There is no
|
||||
point in the handoff where a previously-protected StreamID can DMA freely against memory it didn't have a mapping
|
||||
for during UEFI.
|
||||
|
||||
4. OS Reconfiguration:
|
||||
|
||||
The operating system reconfigures the SMMU as needed for its own DMA remapping and memory protection requirements.
|
||||
By following these steps, the system ensures that the SMMU provides continuous protection from the pre-DXE phase
|
||||
through to the operating system's reconfiguration. This approach helps prevent unauthorized DMA operations and
|
||||
maintains system security.
|
||||
|
||||
## Limitations
|
||||
|
||||
Current implementation constraints:
|
||||
|
||||
1. Supports cache coherent SMMUs only
|
||||
2. Fixed 4KB granule size
|
||||
3. 48-bit address space limit
|
||||
4. Single translation stage per HOB
|
||||
- The platform picks Stage 2 (default) or Stage 1 via `SMMU_CONFIG->TranslationStage`, and every SMMU described
|
||||
by the HOB is configured for the same stage. Nested Stage 1 + Stage 2 is not supported.
|
||||
- By only ever programming one stage, firmware hands off a translation regime that both a hypervisor and a
|
||||
bare-metal OS can re-use for live handoff scenarios.
|
||||
5. Only Identity-mapped page tables are supported (one per StreamID, allocated lazily on first
|
||||
`IoMmuSetAttribute` for that StreamID)
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential improvements:
|
||||
|
||||
1. Multiple translation granule support
|
||||
2. Different page table mapping schemes besides Identiy-Mapping
|
||||
3. Updated IoMmu Protocol to optimize redundancies
|
||||
4. Bounce Buffer Optimization
|
||||
- Remove the `NumberOfBytes` end address alignment check in `IoMmuMap`
|
||||
- Update individual drivers to allocate their DMA buffers by page (page-aligned and page-sized)
|
||||
- This eliminates one case for bounce buffering, improving performance by avoiding unnecessary
|
||||
buffer copies and allocations when only the end address is unaligned
|
||||
|
||||
## Configuration Options
|
||||
|
||||
Key SMMU settings controlled through the SMMU config HOB:
|
||||
|
||||
- **IORT data:** The complete IORT table data that the SMMU(s) will be configured with. Located by
|
||||
`IortOffset` / `IortSize`. SmmuDxe walks this both at init (to discover SMMU nodes / RMR ranges) and at runtime
|
||||
(to resolve `DeviceHandle` → StreamID for `IoMmuSetAttribute`).
|
||||
|
||||
- **SmmuDisabledList:** Provides the platform the ability to individually disable/bypass an SMMU if needed.
|
||||
This list is a `UINT64[]` of SMMU base addresses that the platform wants to disable/bypass, located by
|
||||
`SmmuDisabledListOffset` / `SmmuDisabledListSize`. By default every SMMU found in the IORT is configured for the
|
||||
translation stage picked by `TranslationStage` (Stage 2 by default); any SMMU whose base appears in this list is
|
||||
instead set to global-bypass.
|
||||
|
||||
- **NonDiscoverable device lookup table (`NcDeviceListOffset` / `NcDeviceListSize`):** Maps each platform-assigned
|
||||
`UniqueId` (the value passed to `RegisterNonDiscoverableMmioDevice`) to the matching IORT Named Component
|
||||
`ObjectName`. SmmuDxe uses this two-step lookup
|
||||
(`UniqueId → ObjectName → IORT NC node → StreamID(s) + owning SMMU base`) every time a non-discoverable device
|
||||
calls `IoMmuSetAttribute`. Omit the table (or leave the offset/size as `0`) only if the platform has no
|
||||
non-discoverable DMA-capable devices; otherwise NC StreamID resolution will fail. See
|
||||
[Mapping a Non-Discoverable Device to an IORT Named Component](#mapping-a-non-discoverable-device-to-an-iort-named-component)
|
||||
for the entry format and an example.
|
||||
|
||||
- **TranslationStage:** Chooses the translation stage every SMMU in the HOB is configured for. Default
|
||||
`SmmuTranslationStage2` matches previous behavior (Stage 2 translate, Stage 1 bypass);
|
||||
`SmmuTranslationStage1` opts into Stage 1 translation (Stage 2 bypass). Any other value is coerced to
|
||||
`SmmuTranslationStage2`. Requires the SMMU to advertise `IDR0.S1p` when Stage 1 is selected; otherwise
|
||||
`SmmuV3Configure` will return `EFI_UNSUPPORTED` and driver init will fail. See
|
||||
[Translation Stage](#translation-stage) for behavior differences.
|
||||
|
||||
## Platform Integration Instructions
|
||||
|
||||
Generic Platform Integration:
|
||||
|
||||
1. **Build the IORT.** Include one SMMUv3 node, the necessary ITS / Root Complex nodes for PCI, and one
|
||||
`EFI_ACPI_IORT_TYPE_NAMED_COMP` node per non-discoverable DMA-capable device. Each Named Component's ID mappings
|
||||
must produce the StreamIDs the device emits and reference the owning SMMUv3 node.
|
||||
|
||||
2. **Register non-discoverable devices with a stable `UniqueId`** using `RegisterNonDiscoverableMmioDevice`. The same
|
||||
`UniqueId` is what SmmuDxe will reconstruct from `PciIo->GetLocation()` later, so it must be unique across all
|
||||
registrations on the platform.
|
||||
|
||||
3. **Publish the SMMU_CONFIG HOB** (`gSmmuConfigHobGuid`). Append (in any order) the IORT blob, the NC device lookup
|
||||
table (see [Non-Discoverable Device Integration](#non-discoverable-device-integration)), and the optional SMMU
|
||||
disable list, then point the offsets/sizes in the `SMMU_CONFIG` header at them. Leave `TranslationStage` at
|
||||
`SmmuTranslationStage2` (default) unless the platform explicitly wants Stage 1 - see
|
||||
[Translation Stage](#translation-stage). Example:
|
||||
|
||||
```c
|
||||
STATIC CONST SMMU_NC_DEVICE_ENTRY NcDeviceTable[] = {
|
||||
{ /* UniqueId */ 0x1, /* IORT NC ObjectName */ "USB" },
|
||||
};
|
||||
|
||||
SmmuConfig = AllocateZeroPool (sizeof (SMMU_CONFIG) + sizeof (IortData) + sizeof (NcDeviceTable));
|
||||
SmmuConfig->VersionMajor = CURRENT_SMMU_CONFIG_VERSION_MAJOR;
|
||||
SmmuConfig->VersionMinor = CURRENT_SMMU_CONFIG_VERSION_MINOR;
|
||||
SmmuConfig->IortSize = sizeof (IortData);
|
||||
SmmuConfig->IortOffset = sizeof (SMMU_CONFIG);
|
||||
SmmuConfig->NcDeviceListSize = sizeof (NcDeviceTable);
|
||||
SmmuConfig->NcDeviceListOffset = sizeof (SMMU_CONFIG) + sizeof (IortData);
|
||||
SmmuConfig->TranslationStage = SmmuTranslationStage2; // or SmmuTranslationStage1 to opt into Stage 1
|
||||
|
||||
CopyMem ((UINT8 *)SmmuConfig + SmmuConfig->IortOffset, &IortData, sizeof (IortData));
|
||||
CopyMem ((UINT8 *)SmmuConfig + SmmuConfig->NcDeviceListOffset, NcDeviceTable, sizeof (NcDeviceTable));
|
||||
|
||||
BuildGuidDataHob (&gSmmuConfigHobGuid, SmmuConfig, sizeof (SMMU_CONFIG) + sizeof (IortData) + sizeof (NcDeviceTable));
|
||||
```
|
||||
|
||||
4. **Do not install the IORT yourself** - SmmuDxe installs it as an ACPI table after parsing.
|
||||
|
||||
5. **Disable specific SMMUs (optional).** Append a `UINT64[]` of SMMU base addresses to put in bypass mode and point
|
||||
`SmmuDisabledListOffset` / `SmmuDisabledListSize` at it.
|
||||
|
||||
6. **Handle-less DMA agents (optional).** Firmware components that have no UEFI device handle (and therefore are not
|
||||
in the IORT) can program the SMMU directly through `IoMmuLib::IoMmuSetAttributeById (IommuBase, DmaId, ...)`. The
|
||||
caller is responsible for supplying the correct SMMU base and DMA identifier.
|
||||
|
||||
Integration with Qemu:
|
||||
|
||||
- SMMU is supported on Qemu v9.1.50 and above.
|
||||
|
||||
## Relevant Docs
|
||||
|
||||
- SMMUv3 specification <https://developer.arm.com/documentation/ihi0070/latest/>
|
||||
- Useful ARM SMMU documentation - <https://developer.arm.com/documentation/109242/0100/Programming-the-SMMU>
|
||||
- Arm AArch64 memory management guide - <https://developer.arm.com/documentation/101811/0104>
|
||||
- ARM a_a-profile_architecture_reference_manual <https://developer.arm.com/documentation/ddi0487/mc/>
|
||||
- Intel IOMMU for DMA protection in UEFI <https://www.intel.com/content/dam/develop/external/us/en/documents/intel-whitepaper-using-iommu-for-dma-protection-in-uefi.pdf>
|
||||
- IORT documentation <https://developer.arm.com/documentation/den0049/latest/>
|
||||
2416
ArmPkg/Drivers/SmmuDxe/SmmuDxe.c
Normal file
2416
ArmPkg/Drivers/SmmuDxe/SmmuDxe.c
Normal file
File diff suppressed because it is too large
Load diff
53
ArmPkg/Drivers/SmmuDxe/SmmuDxe.inf
Normal file
53
ArmPkg/Drivers/SmmuDxe/SmmuDxe.inf
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
## @file SmmuDxe.inf
|
||||
# This driver initializes the SmmuV3 hardware to enable stage 2 translation and DMA remapping.
|
||||
# The IOMMU protocol is implemented assuming a 4-level Page Table Structure.
|
||||
#
|
||||
# For more info see README.md
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
|
||||
[Defines]
|
||||
INF_VERSION = 0x00010006
|
||||
BASE_NAME = SmmuDxe
|
||||
FILE_GUID = BE506866-85F2-45EC-AC01-9BBF39D4CA78
|
||||
MODULE_TYPE = DXE_DRIVER
|
||||
VERSION_STRING = 1.0
|
||||
ENTRY_POINT = InitializeSmmuDxe
|
||||
|
||||
[Sources]
|
||||
IoMmu.c
|
||||
IoMmu.h
|
||||
SmmuDxe.c
|
||||
SmmuV3.h
|
||||
SmmuV3Util.c
|
||||
|
||||
[Guids]
|
||||
gSmmuConfigHobGuid
|
||||
gEfiEventExitBootServicesGuid
|
||||
|
||||
[Packages]
|
||||
ArmPkg/ArmPkg.dec
|
||||
EmbeddedPkg/EmbeddedPkg.dec
|
||||
MdeModulePkg/MdeModulePkg.dec
|
||||
MdePkg/MdePkg.dec
|
||||
|
||||
[LibraryClasses]
|
||||
ArmLib
|
||||
BaseLib
|
||||
BaseMemoryLib
|
||||
IoLib
|
||||
HobLib
|
||||
MemoryAllocationLib
|
||||
TimerLib
|
||||
UefiDriverEntryPoint
|
||||
|
||||
[Protocols]
|
||||
gEdkiiIoMmuProtocolGuid ## PRODUCES
|
||||
gEfiAcpiTableProtocolGuid ## CONSUMES
|
||||
gEfiPciIoProtocolGuid ## CONSUMES
|
||||
gHardwareInterrupt2ProtocolGuid ## CONSUMES
|
||||
|
||||
[Depex]
|
||||
gHardwareInterrupt2ProtocolGuid
|
||||
981
ArmPkg/Drivers/SmmuDxe/SmmuV3.h
Normal file
981
ArmPkg/Drivers/SmmuDxe/SmmuV3.h
Normal file
|
|
@ -0,0 +1,981 @@
|
|||
/** @file SmmuV3.h
|
||||
|
||||
This file is the SmmuV3 header file for SMMU driver compliant with the Smmu spec:
|
||||
|
||||
<https://developer.arm.com/documentation/ihi0070/latest/>
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Register/SmmuV3Registers.h>
|
||||
#include <Uefi/UefiBaseType.h>
|
||||
#include <IndustryStandard/IoRemappingTable.h>
|
||||
#include <Protocol/IoMmu.h>
|
||||
#include <Protocol/HardwareInterrupt2.h>
|
||||
#include <Guid/SmmuConfig.h>
|
||||
#include "IoMmu.h"
|
||||
|
||||
// Number of levels in the page table
|
||||
#define PAGE_TABLE_DEPTH 4
|
||||
// If the starting level is 1, and the address width exceeds 39 bits, then the page table is concatenated
|
||||
#define PAGE_TABLE_CONCATENATED_PAGES_BITS_CUTOFF 39
|
||||
// Page Table Index macro to calculate the index of the page table entry based on the level and address width, supports concatenated page tables
|
||||
#define PAGE_TABLE_INDEX(VirtualAddress, Level, OutputAddressWidth, TranslationStartingLevel, PageTableRootConcatenated) \
|
||||
(PageTableRootConcatenated && (Level == 1) && (TranslationStartingLevel == Level)) ? \
|
||||
(((VirtualAddress) >> (12 + (9 * ((PAGE_TABLE_DEPTH - 1) - (Level))))) & \
|
||||
((1 << (9 + ((OutputAddressWidth) - PAGE_TABLE_CONCATENATED_PAGES_BITS_CUTOFF))) - 1)) : \
|
||||
(((VirtualAddress) >> (12 + (9 * ((PAGE_TABLE_DEPTH - 1) - (Level))))) & 0x1FF)
|
||||
|
||||
#define PAGE_TABLE_4_LEVEL_OUTPUT_ADDRESS_WIDTH_MIN 44
|
||||
#define PAGE_TABLE_OUTPUT_ADDRESS_WIDTH_MAX 48
|
||||
#define PAGE_TABLE_OUTPUT_ADDRESS_WIDTH_MIN 32
|
||||
#define PAGE_TABLE_ROOT_CONCATENATED_PAGES_MAX 16
|
||||
|
||||
#define PAGE_TABLE_ROOT_STAGE1_PAGES 1
|
||||
#define PAGE_TABLE_ROOT_STAGE2_PAGES PAGE_TABLE_ROOT_CONCATENATED_PAGES_MAX
|
||||
|
||||
#define PAGE_TABLE_ROOT_PAGES(SmmuInfo) \
|
||||
(((SmmuInfo)->TranslationStage == SmmuTranslationStage1) \
|
||||
? PAGE_TABLE_ROOT_STAGE1_PAGES \
|
||||
: PAGE_TABLE_ROOT_STAGE2_PAGES)
|
||||
|
||||
// Cacheability and Shareability attributes
|
||||
#define ARM64_RGNCACHEATTR_NONCACHEABLE 0
|
||||
#define ARM64_RGNCACHEATTR_WRITEBACK_WRITEALLOCATE 1
|
||||
#define ARM64_RGNCACHEATTR_WRITETHROUGH 2
|
||||
#define ARM64_RGNCACHEATTR_WRITEBACK_NOWRITEALLOCATE 3
|
||||
|
||||
#define ARM64_SHATTR_NON_SHAREABLE 0
|
||||
#define ARM64_SHATTR_OUTER_SHAREABLE 2
|
||||
#define ARM64_SHATTR_INNER_SHAREABLE 3
|
||||
|
||||
#define SMMUV3_PAGE_1_OFFSET 0x10000
|
||||
|
||||
// log2 size of the command queue
|
||||
#define SMMUV3_COMMAND_QUEUE_LOG2ENTRIES (8)
|
||||
|
||||
//
|
||||
// Define the size of each entry in the command queue.
|
||||
//
|
||||
#define SMMUV3_COMMAND_QUEUE_ENTRY_SIZE (sizeof(SMMUV3_CMD_GENERIC))
|
||||
|
||||
//
|
||||
// Macros to compute command queue size given its Log2 size.
|
||||
//
|
||||
#define SMMUV3_COMMAND_QUEUE_SIZE_FROM_LOG2(QueueLog2Size) \
|
||||
((UINT32)(1UL << (QueueLog2Size)) * \
|
||||
(UINT16)(SMMUV3_COMMAND_QUEUE_ENTRY_SIZE))
|
||||
|
||||
// log2 size of the event queue
|
||||
#define SMMUV3_EVENT_QUEUE_LOG2ENTRIES (7)
|
||||
|
||||
//
|
||||
// Define the size of each entry in the event queue.
|
||||
//
|
||||
#define SMMUV3_EVENT_QUEUE_ENTRY_SIZE (sizeof(SMMUV3_FAULT_RECORD))
|
||||
|
||||
//
|
||||
// Macros to compute event queue size given its Log2 size.
|
||||
//
|
||||
#define SMMUV3_EVENT_QUEUE_SIZE_FROM_LOG2(QueueLog2Size) \
|
||||
((UINT32)(1UL << (QueueLog2Size)) * (UINT16)(SMMUV3_EVENT_QUEUE_ENTRY_SIZE))
|
||||
|
||||
#define SMMUV3_COUNT_FROM_LOG2(Log2Size) (1UL << (Log2Size))
|
||||
|
||||
//
|
||||
// Macro to determine if a queue is empty. It is empty if the producer and
|
||||
// consumer indices are equal and their wrap bits are also equal.
|
||||
//
|
||||
#define SMMUV3_IS_QUEUE_EMPTY(ProducerIndex, \
|
||||
ProducerWrap, \
|
||||
ConsumerIndex, \
|
||||
ConsumerWrap) \
|
||||
\
|
||||
(((ProducerIndex) == (ConsumerIndex)) && ((ProducerWrap) == (ConsumerWrap)))
|
||||
|
||||
//
|
||||
// Macro to determine if a queue is full. It is full if the producer and
|
||||
// consumer indices are equal and their wrap bits are different.
|
||||
//
|
||||
#define SMMUV3_IS_QUEUE_FULL(ProducerIndex, \
|
||||
ProducerWrap, \
|
||||
ConsumerIndex, \
|
||||
ConsumerWrap) \
|
||||
\
|
||||
(((ProducerIndex) == (ConsumerIndex)) && ((ProducerWrap) != (ConsumerWrap)))
|
||||
|
||||
//
|
||||
// SMMUV3 Stream Table Entry bit definitions
|
||||
//
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_CCA 1 // Cache Coherent Attribute
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_CPM 1 // Coherent Path to Memory
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_DACS 1 // Device attributes are Cacheable and Inner-Shareable
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_CONFIG_STAGE_2_TRANSLATE_STAGE_1_BYPASS 0x6 // Stage 2 Translate, Stage 1 Bypass
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_CONFIG_STAGE_1_TRANSLATE_STAGE_2_BYPASS 0x5 // Stage 1 Translate, Stage 2 Bypass
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_CONFIG_STAGE_2_BYPASS_STAGE_1_BYPASS 0x4 // Stage 2 Bypass, Stage 1 Bypass
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_EATS_NOT_SUPPORTED 0 // ATS not supported
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_S2VMID 1 // Pick non-zero value
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_S2TG_4KB 0 // Granule size 4KB
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_S2AA64 1 // AA64
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_S2TTB_OFFSET 4 // S2TTB offset >> 4
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_S2PTW 1 // S2PTW bit
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_S2SL0 2 // Encoded level of page table walk, 2 -> 4-level page table starting from 0
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_OUTPUT_ADDRESS_MAX 48 // 48 bit output address width max
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_S2RS_RECORD_FAULTS 2 // Record faults
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_SHCFG_INCOMING_SHAREABILITY 1 // Incoming shareability attribute
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_SHCFG_INNER_SHAREABLE 3 // Inner shareable
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_MTCFG 1 // MTCFG bit
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_MEMATTR_INNER_OUTTER_WRITEBACK_CACHED 0xF // Inner+Outer write-back cached
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_VALID 1 // Entry is valid
|
||||
|
||||
//
|
||||
// SMMUV3 Stage 1 Stream Table Entry bit definitions (used when
|
||||
// Config = STAGE_1_TRANSLATE_STAGE_2_BYPASS). The S2VMID field is
|
||||
// ignored when only Stage 1 is enabled and TLB entries
|
||||
// are tagged with VMID = 0, so STE.S2Vmid is left 0 and every Stage 1
|
||||
// TLB invalidation targets VMID = 0.
|
||||
//
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_S1CONTEXTPTR_OFFSET 6 // S1ContextPtr address is stored shifted right by 6
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_S1FMT_LINEAR 0 // Linear single-CD format
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_S1CDMAX_SINGLE_CD 0 // 2^0 = 1 CD, no SubStreamID support
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_S1DSS_ABORT 0x2 // Default SubStreamID handling: abort untagged DMA
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_S1STALLD_TERMINATE 1 // Force Stage 1 stalling faults to terminate
|
||||
#define SMMUV3_STREAM_TABLE_ENTRY_S1_ONLY_VMID 0 // Per §5.2: S2Vmid ignored, TLB tagged VMID 0
|
||||
|
||||
//
|
||||
// SMMUV3 Context Descriptor field values used by the Stage 1 CD.
|
||||
//
|
||||
#define SMMUV3_CD_TG0_4KB 0 // 4KB granule
|
||||
#define SMMUV3_CD_AA64 1 // AArch64 translation regime
|
||||
#define SMMUV3_CD_TTB0_OFFSET 4 // Ttb0 is stored shifted right by 4
|
||||
#define SMMUV3_CD_EPD1 1 // Disable TTBR1 (TTBR0-only walk)
|
||||
|
||||
//
|
||||
// CD.Ars packed { A(bit2), R(bit1), S(bit0) }: 0x6 = abort + record fault
|
||||
// event (visible in event queue).
|
||||
//
|
||||
#define SMMUV3_CD_ARS_ABORT_RECORD 0x6
|
||||
#define SMMUV3_CD_MAIR_ATTR0_NORMAL_WBWA 0xFFULL // MAIR[0] = Normal Inner+Outer WBWA
|
||||
#define SMMUV3_CD_MAIR_ATTR1_DEVICE_NGNRNE 0x00ULL // MAIR[1] = Device-nGnRnE
|
||||
#define SMMUV3_CD_MAIR0_NORMAL_WBWA (SMMUV3_CD_MAIR_ATTR0_NORMAL_WBWA | (SMMUV3_CD_MAIR_ATTR1_DEVICE_NGNRNE << 8))
|
||||
|
||||
// VMID 0 is not a valid VMID so whenever we wrap past the max VMID, we set it to 0 to mark exhaustion.
|
||||
// (Applies to both 8/16-bit VMID width).
|
||||
#define SMMU_VMID_RESERVED 0
|
||||
|
||||
// ASID 0 is treated the same way as VMID but for the Stage 1 ASID allocator.
|
||||
#define SMMU_ASID_RESERVED 0
|
||||
|
||||
//
|
||||
// SMMUV3 Configuration bit definitions
|
||||
//
|
||||
#define SMMUV3_STR_TAB_BASE_CFG_FMT_LINEAR 0 // Linear Stream Table format
|
||||
#define SMMUV3_STR_TAB_BASE_CFG_FMT_2LEVEL 1 // 2-Level Stream Table format
|
||||
#define SMMUV3_STR_TAB_BASE_CFG_SPLIT 6 // Split bit for 2-Level Stream Table
|
||||
#define SMMUV3_STR_TAB_BASE_L2_PTR_OFFSET 6 // Offset of L2 pointer in the L1 stream table entry
|
||||
#define SMMUV3_STR_TAB_BASE_ADDR_OFFSET 6 // Stream Table base address offset
|
||||
#define SMMUV3_STR_TAB_BASE_CMDQ_OFFSET 5 // Command queue base address offset
|
||||
#define SMMUV3_STR_TAB_BASE_EVENTQ_OFFSET 5 // Event queue base address offset
|
||||
#define SMMUV3_CR2_E2H 0 // E2H bit 0
|
||||
#define SMMUV3_CR2_REC_INV_SID 1 // Record C_BAD_STREAMID for invalid input streams
|
||||
#define SMMUV3_CR2_PTM 1 // PTM bit
|
||||
#define SMMUV3_CR0_EVENTQ_EN 1 // Event queue enable
|
||||
#define SMMUV3_CR0_CMDQ_EN 1 // Command queue enable
|
||||
#define SMMUV3_CR0_SMMU_EN 1 // SMMU enable
|
||||
#define SMMUV3_CR0_EVENTQ_EN 1 // Event queue enable
|
||||
#define SMMUV3_CR0_CMDQ_EN 1 // Command queue enable
|
||||
#define SMMUV3_CR0_PRIQ_EN_DISABLED 0 // Disable PRI queue
|
||||
#define SMMUV3_CR0_VMW_DISABLED 0 // Disable VMID wildcard matching
|
||||
#define SMMUV3_CR0_ATS_CHK_DISABLE 1 // Disable bypass for ATS translated traffic
|
||||
|
||||
typedef enum _SMMU_ADDRESS_SIZE_TYPE {
|
||||
SmmuAddressSize32Bit = 0,
|
||||
SmmuAddressSize36Bit = 1,
|
||||
SmmuAddressSize40Bit = 2,
|
||||
SmmuAddressSize42Bit = 3,
|
||||
SmmuAddressSize44Bit = 4,
|
||||
SmmuAddressSize48Bit = 5,
|
||||
SmmuAddressSize52Bit = 6,
|
||||
} SMMU_ADDRESS_SIZE_TYPE;
|
||||
|
||||
typedef struct _RMR_NODE_INFO {
|
||||
EFI_ACPI_6_0_IO_REMAPPING_RMR_NODE *RmrNode; // Pointer to the RMR node
|
||||
LIST_ENTRY Link; // Link to the RMR node in the list
|
||||
} RMR_NODE_INFO;
|
||||
|
||||
// Single node in a StreamID list returned by DeviceHandleToStreamId.
|
||||
// Allocated from pool; freed by SmmuStreamIdListFree.
|
||||
typedef struct {
|
||||
LIST_ENTRY Link;
|
||||
UINT32 StreamId;
|
||||
} SMMU_STREAM_ID_ENTRY;
|
||||
|
||||
// General SMMU Information for a SMMU instance
|
||||
typedef struct _SMMU_INFO {
|
||||
VOID *SharedAbortL2; // 2-level only: shared L2 page of all-ABORT STEs. L1 entries point here until split-on-write.
|
||||
VOID *StreamTable;
|
||||
VOID *CommandQueue;
|
||||
VOID *EventQueue;
|
||||
LIST_ENTRY RmrNodeList;
|
||||
UINT64 SmmuBase;
|
||||
UINT64 CachedProducer;
|
||||
UINT64 CachedConsumer;
|
||||
UINT32 StreamTableSize;
|
||||
UINT32 StreamTableEntryMax;
|
||||
UINT32 Flags;
|
||||
UINT32 CommandQueueSize;
|
||||
UINT32 EventQueueSize;
|
||||
UINT32 StreamTableLog2Size;
|
||||
UINT32 CommandQueueLog2Size;
|
||||
UINT32 EventQueueLog2Size;
|
||||
UINT32 OutputAddressWidth;
|
||||
UINT8 TranslationStartingLevel;
|
||||
BOOLEAN PageTableRootConcatenated;
|
||||
BOOLEAN RangeInvalidationSupported;
|
||||
BOOLEAN EBSBehaviorAbort;
|
||||
BOOLEAN Enabled;
|
||||
BOOLEAN TwoLevelStreamTableSupported; // Whether the SMMU supports 2-level stream tables, which allows more entries than can fit in a single page.
|
||||
BOOLEAN Vmid16Supported; // IDR0.VMID16. FALSE = 8-bit VMIDs only.
|
||||
UINT16 NextVmid; // Next per-stream VMID to hand out. Starts at 1.
|
||||
//
|
||||
// Per-SMMU translation stage. Defaults to SmmuTranslationStage2.
|
||||
//
|
||||
SMMU_TRANSLATION_STAGE TranslationStage;
|
||||
//
|
||||
// Stage 1-only fields populated during SmmuV3Configure when
|
||||
// TranslationStage == SmmuTranslationStage1.
|
||||
//
|
||||
BOOLEAN Asid16Supported; // IDR0.ASID16. FALSE = 8-bit ASIDs.
|
||||
UINT16 NextAsid; // Next per-stream ASID to hand out. Starts at 1.
|
||||
UINTN EvtqIrqNum;
|
||||
UINTN GerrIrqNum;
|
||||
} SMMU_INFO;
|
||||
|
||||
// IoMmu configuration structure
|
||||
typedef struct _IOMMU_CONFIG {
|
||||
UINT32 SmmuCount;
|
||||
SMMU_INFO *SmmuInfo;
|
||||
// Platform-provided UniqueId -> Named Component ObjectName mapping for NonDiscoverable devices.
|
||||
SMMU_NC_DEVICE_ENTRY *NcDeviceList;
|
||||
UINT32 NcDeviceCount;
|
||||
} IOMMU_CONFIG;
|
||||
|
||||
// IOMMU/SMMU instance
|
||||
extern IOMMU_CONFIG *mIoMmu;
|
||||
extern EFI_HARDWARE_INTERRUPT2_PROTOCOL *mGicInterrupt;
|
||||
extern VOID *mIortData; // Global IORT data pointer
|
||||
|
||||
/**
|
||||
Decode the address width from the given address size type.
|
||||
|
||||
@param [in] AddressSizeType The address size type.
|
||||
|
||||
@return The decoded address width. 0 if the address size type is invalid.
|
||||
**/
|
||||
UINT32
|
||||
SmmuV3DecodeAddressWidth (
|
||||
IN UINT32 AddressSizeType
|
||||
);
|
||||
|
||||
/**
|
||||
Encode the address width to the corresponding address size type.
|
||||
|
||||
@param [in] AddressWidth The address width.
|
||||
|
||||
@return The encoded address size type. 0 if the address width is invalid.
|
||||
**/
|
||||
UINT8
|
||||
SmmuV3EncodeAddressWidth (
|
||||
IN UINT32 AddressWidth
|
||||
);
|
||||
|
||||
/**
|
||||
Set the translation starting level for SMMUv3 page tables.
|
||||
Only 3 and 4 level paging are supported.
|
||||
|
||||
@param [in] SmmuInfo Pointer to the SMMU_INFO structure.
|
||||
@param [in] OutputAddressWidth The output address width.
|
||||
@param [out] S2Sl0 The starting level for stage 2 translation.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_INVALID_PARAMETER Invalid parameter.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3SetTranslationStartingLevel (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
IN UINT32 OutputAddressWidth,
|
||||
OUT UINT64 *S2Sl0
|
||||
);
|
||||
|
||||
/**
|
||||
Read a 32-bit value from the specified SMMU register.
|
||||
|
||||
@param [in] SmmuBase The base address of the SMMU.
|
||||
@param [in] Register The offset of the register.
|
||||
|
||||
@return The 32-bit value read from the register. 0 if the SMMU base address is invalid.
|
||||
**/
|
||||
UINT32
|
||||
SmmuV3ReadRegister32 (
|
||||
IN UINT64 SmmuBase,
|
||||
IN UINT64 Register
|
||||
);
|
||||
|
||||
/**
|
||||
Read a 64-bit value from the specified SMMU register.
|
||||
|
||||
@param [in] SmmuBase The base address of the SMMU.
|
||||
@param [in] Register The offset of the register.
|
||||
|
||||
@return The 64-bit value read from the register. 0 if the SMMU base address is invalid.
|
||||
**/
|
||||
UINT64
|
||||
SmmuV3ReadRegister64 (
|
||||
IN UINT64 SmmuBase,
|
||||
IN UINT64 Register
|
||||
);
|
||||
|
||||
/**
|
||||
Write a 32-bit value to the specified SMMU register.
|
||||
|
||||
@param [in] SmmuBase The base address of the SMMU.
|
||||
@param [in] Register The offset of the register.
|
||||
@param [in] Value The 32-bit value to write.
|
||||
|
||||
@return The 32-bit value written to the register, or 0 if the SMMU base address is invalid.
|
||||
**/
|
||||
UINT32
|
||||
SmmuV3WriteRegister32 (
|
||||
IN UINT64 SmmuBase,
|
||||
IN UINT64 Register,
|
||||
IN UINT32 Value
|
||||
);
|
||||
|
||||
/**
|
||||
Write a 64-bit value to the specified SMMU register.
|
||||
|
||||
@param [in] SmmuBase The base address of the SMMU.
|
||||
@param [in] Register The offset of the register.
|
||||
@param [in] Value The 64-bit value to write.
|
||||
|
||||
@return The 64-bit value written to the register, or 0 if the SMMU base address is invalid.
|
||||
**/
|
||||
UINT64
|
||||
SmmuV3WriteRegister64 (
|
||||
IN UINT64 SmmuBase,
|
||||
IN UINT64 Register,
|
||||
IN UINT64 Value
|
||||
);
|
||||
|
||||
/**
|
||||
Disable interrupts for the SMMUv3.
|
||||
|
||||
@param [in] SmmuBase The base address of the SMMU.
|
||||
@param [in] ClearStaleErrors Whether to clear stale errors.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_INVALID_PARAMETER Invalid Parameters.
|
||||
@retval EFI_TIMEOUT Timeout.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3DisableInterrupts (
|
||||
IN UINT64 SmmuBase,
|
||||
IN BOOLEAN ClearStaleErrors
|
||||
);
|
||||
|
||||
/**
|
||||
Enable interrupts for the SMMUv3.
|
||||
|
||||
@param [in] SmmuBase The base address of the SMMU.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_INVALID_PARAMETER Invalid Parameters.
|
||||
@retval EFI_TIMEOUT Timeout.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3EnableInterrupts (
|
||||
IN UINT64 SmmuBase
|
||||
);
|
||||
|
||||
/**
|
||||
Disable translation for the SMMUv3.
|
||||
|
||||
@param [in] SmmuBase The base address of the SMMU.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_INVALID_PARAMETER Invalid Parameters.
|
||||
@retval EFI_TIMEOUT Timeout.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3DisableTranslation (
|
||||
IN UINT64 SmmuBase
|
||||
);
|
||||
|
||||
/**
|
||||
Set the Smmu in ABORT mode and stop DMA.
|
||||
|
||||
@param [in] SmmuReg Base address of the SMMUv3.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_INVALID_PARAMETER Invalid Parameters.
|
||||
@retval EFI_TIMEOUT Timeout.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3GlobalAbort (
|
||||
IN UINT64 SmmuBase
|
||||
);
|
||||
|
||||
/**
|
||||
Set all streams to bypass the SMMU.
|
||||
|
||||
@param [in] SmmuReg Base address of the SMMUv3.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_TIMEOUT Timeout.
|
||||
@retval EFI_INVALID_PARAMETER Invalid Parameters.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3SetGlobalBypass (
|
||||
IN UINT64 SmmuBase
|
||||
);
|
||||
|
||||
/**
|
||||
Poll the SMMU register and test the value based on the mask.
|
||||
|
||||
@param [in] SmmuBase Base address of the SMMU.
|
||||
@param [in] SmmuReg The SMMU register to poll.
|
||||
@param [in] Mask Mask of register bits to monitor.
|
||||
@param [in] Value Expected value.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_TIMEOUT Timeout.
|
||||
@retval EFI_INVALID_PARAMETER Invalid Parameters.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3Poll (
|
||||
IN UINT64 SmmuBase,
|
||||
IN UINT64 SmmuReg,
|
||||
IN UINT32 Mask,
|
||||
IN UINT32 Value
|
||||
);
|
||||
|
||||
/**
|
||||
Consume the event queue for errors and retrieve the fault record.
|
||||
Clears the outputted FaultRecord if the queue is empty.
|
||||
|
||||
@param [in] SmmuInfo Pointer to the SMMU_INFO structure.
|
||||
@param [out] FaultRecord Pointer to the fault record structure.
|
||||
@param [out] IsEmpty Flag to indicate if the queue is empty.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_TIMEOUT Timeout.
|
||||
@retval EFI_INVALID_PARAMETER Invalid Parameters.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3ConsumeEventQueueForErrors (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
OUT SMMUV3_FAULT_RECORD *FaultRecord,
|
||||
OUT BOOLEAN *IsEmpty
|
||||
);
|
||||
|
||||
/**
|
||||
Dump the page table entries for a given virtual address.
|
||||
Dumps PTE's for all levels regardless of the starting level chosen for translation.
|
||||
|
||||
@param [in] SmmuInfo Pointer to the SMMU_INFO structure.
|
||||
@param [in] VirtualAddress The virtual address to dump.
|
||||
@param [in] Root Pointer to the root page table.
|
||||
**/
|
||||
VOID
|
||||
SmmuV3DumpPageTableEntries (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
IN UINT64 VirtualAddress,
|
||||
IN PAGE_TABLE *Root
|
||||
);
|
||||
|
||||
/**
|
||||
Check if an entire address range has a valid identity mapping in the
|
||||
stage-2 translation table.
|
||||
|
||||
Walks Root using SmmuInfo's translation parameters for every 4 KB page in
|
||||
[Address, Address + Pages * EFI_PAGE_SIZE). Address is rounded down to the
|
||||
enclosing page. Since this driver identity-maps DMA a page counts as mapped only when:
|
||||
- every intermediate level has a non-zero descriptor,
|
||||
- the leaf entry has PAGE_TABLE_ENTRY_VALID_BIT set, and
|
||||
- the leaf entry's encoded physical address matches the page address.
|
||||
|
||||
Returns TRUE only if every page in the range satisfies the above; FALSE on
|
||||
the first page that fails (short-circuit), bad parameters, or Pages == 0.
|
||||
|
||||
@param [in] SmmuInfo Pointer to the SMMU_INFO structure whose translation
|
||||
parameters drive the walk.
|
||||
@param [in] Root Pointer to the root page table.
|
||||
@param [in] Address Start address of the range (same value used as both
|
||||
VA and PA under identity mapping). Aligned down to
|
||||
the enclosing 4 KB page.
|
||||
@param [in] Pages Number of 4 KB pages to check, starting at Address.
|
||||
|
||||
@retval TRUE Every page in the range has a valid identity-mapped leaf.
|
||||
@retval FALSE At least one page is not mapped, encodes a different PA, or
|
||||
a required parameter is invalid.
|
||||
**/
|
||||
BOOLEAN
|
||||
SmmuV3IsAddressRangeMapped (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
IN PAGE_TABLE *Root,
|
||||
IN UINT64 Address,
|
||||
IN UINTN Pages
|
||||
);
|
||||
|
||||
/**
|
||||
Log the errors if found from the SMMUv3. Prints Event Queue entries and GError register.
|
||||
Does nothing if no errors found.
|
||||
|
||||
@param [in] SmmuInfo Pointer to the SMMU_INFO structure.
|
||||
|
||||
@retval EFI_SUCCESS No SMMU errors found.
|
||||
@retval EFI_INVALID_PARAMETER Invalid Parameters.
|
||||
@retval EFI_DEVICE_ERROR SMMU error found.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3LogErrors (
|
||||
IN SMMU_INFO *SmmuInfo
|
||||
);
|
||||
|
||||
/**
|
||||
Register GIC interrupt sources for SmmuV3 EVTQ and GERR interrupts.
|
||||
|
||||
@param[in] GicInterrupt Pointer to the GIC interrupt protocol.
|
||||
@param[in] SmmuInfo Pointer to the SMMU_INFO structure.
|
||||
|
||||
@retval EFI_SUCCESS The interrupt sources were registered successfully.
|
||||
@retval EFI_INVALID_PARAMETER The GicInterrupt or SmmuInfo is NULL.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3RegisterGicIsr (
|
||||
IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *GicInterrupt,
|
||||
IN SMMU_INFO *SmmuInfo
|
||||
);
|
||||
|
||||
/**
|
||||
Send a SMMUV3_CMD_GENERIC command to the SMMUv3.
|
||||
|
||||
@param [in] SmmuInfo Pointer to the SMMU_INFO structure.
|
||||
@param [in] Command Pointer to the command to send.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_TIMEOUT Timeout.
|
||||
@retval EFI_INVALID_PARAMETER Invalid Parameters.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3SendCommand (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
IN SMMUV3_CMD_GENERIC *Command
|
||||
);
|
||||
|
||||
/**
|
||||
Invalidate all Stage 2 TLB entries owned by the given VMID on this SMMU.
|
||||
Uses CMD_TLBI_S12_VMALL.
|
||||
|
||||
@param [in] SmmuInfo Pointer to the SMMU_INFO structure.
|
||||
@param [in] Vmid The VMID to invalidate.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_TIMEOUT Timeout.
|
||||
@retval EFI_INVALID_PARAMETER Invalid Parameters.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3TLBInvalidateAllStage2 (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
IN UINT16 Vmid
|
||||
);
|
||||
|
||||
/**
|
||||
Invalidate all Stage 1 TLB entries owned by the given ASID on this SMMU.
|
||||
Uses CMD_TLBI_NH_ASID with VMID = 0 (Stage 1 only mode tags TLBs with VMID = 0).
|
||||
|
||||
@param [in] SmmuInfo Pointer to the SMMU_INFO structure.
|
||||
@param [in] Asid ASID to invalidate.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_TIMEOUT Timeout.
|
||||
@retval EFI_INVALID_PARAMETER Invalid Parameters.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3TLBInvalidateAllStage1 (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
IN UINT16 Asid
|
||||
);
|
||||
|
||||
/**
|
||||
* Add RMR mappings for each SMMU node in the SmmuInfo structure.
|
||||
* This function iterates through the RMR nodes and updates the page table
|
||||
* for each memory range described in the RMR node.
|
||||
*
|
||||
* @param [in] SmmuInfo Pointer to the SMMU_INFO structure.
|
||||
*
|
||||
* @retval EFI_SUCCESS Success.
|
||||
* @retval EFI_INVALID_PARAMETER Invalid Parameters.
|
||||
* @retval Other RMR mapping update failure.
|
||||
*/
|
||||
EFI_STATUS
|
||||
SmmuV3AddRMRMapping (
|
||||
IN SMMU_INFO *SmmuInfo
|
||||
);
|
||||
|
||||
/**
|
||||
* Parse IORT table and extract SMMU information
|
||||
*
|
||||
* @param[in] IortTable Pointer to the IORT table
|
||||
* @param[out] SmmuInfo Pointer to store the array of SMMU_INFO structures
|
||||
* @param[out] SmmuCount Pointer to store the number of SMMU nodes found
|
||||
*
|
||||
* @return EFI_SUCCESS on success
|
||||
* @return EFI_INVALID_PARAMETER if any parameter is NULL
|
||||
* @return EFI_OUT_OF_RESOURCES if memory allocation fails
|
||||
* @return EFI_NOT_FOUND if no SMMU nodes are found
|
||||
* @return EFI_UNSUPPORTED if the IORT table is not supported
|
||||
*/
|
||||
EFI_STATUS
|
||||
SmmuV3ParseIort (
|
||||
IN VOID *IortTable,
|
||||
OUT SMMU_INFO **SmmuInfo,
|
||||
OUT UINT32 *SmmuCount
|
||||
);
|
||||
|
||||
/**
|
||||
Allocate a per-stream page-table root suitable for use as an STE's S2TTB
|
||||
(Stage 2) or CD.TTB0 (Stage 1). The root size is derived from
|
||||
SmmuInfo->TranslationStage: Stage 2 allocates
|
||||
PAGE_TABLE_ROOT_STAGE2_PAGES for concatenation at the starting level;
|
||||
Stage 1 allocates PAGE_TABLE_ROOT_STAGE1_PAGES since concatenation is
|
||||
not architecturally allowed.
|
||||
|
||||
@param [in] SmmuInfo SMMU instance whose TranslationStage decides how
|
||||
many pages the root occupies.
|
||||
|
||||
@retval Pointer to the zeroed root, or NULL on failure.
|
||||
**/
|
||||
PAGE_TABLE *
|
||||
SmmuV3AllocatePageTableRoot (
|
||||
IN SMMU_INFO *SmmuInfo
|
||||
);
|
||||
|
||||
/**
|
||||
Recursively free a per-stream page-table tree previously returned by
|
||||
SmmuV3AllocatePageTableRoot(). SmmuInfo->TranslationStage is consulted
|
||||
at Level == 0 to release the correct number of pages for the root.
|
||||
|
||||
@param [in] SmmuInfo SMMU instance the tree was allocated for.
|
||||
@param [in] Level Current level (caller must pass 0 for the root).
|
||||
@param [in] PageTable The page-table tree to free. May be NULL.
|
||||
**/
|
||||
VOID
|
||||
SmmuV3FreePageTableTree (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
IN UINT8 Level,
|
||||
IN PAGE_TABLE *PageTable
|
||||
);
|
||||
|
||||
/**
|
||||
Ensure a stage-2 page-table root exists for the given StreamID. If this is
|
||||
the first call for the StreamID on this SMMU, allocate a fresh root + VMID
|
||||
and promote the corresponding STE from ABORT to STAGE_2_TRANSLATE using a
|
||||
break-before-make sequence (CFGI_STE + CMD_SYNC twice). Subsequent calls
|
||||
return the (Root, Vmid) already encoded in the live STE.
|
||||
|
||||
@param [in] SmmuInfo Pointer to the SMMU_INFO structure.
|
||||
@param [in] StreamId The StreamID.
|
||||
@param [out] OutRoot Receives the stage-2 page-table root.
|
||||
@param [out] OutVmid Receives the VMID tag installed in the STE.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_INVALID_PARAMETER Invalid parameters.
|
||||
@retval EFI_OUT_OF_RESOURCES Allocation failed / VMID space exhausted.
|
||||
@retval Other STE promotion failure.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3StreamGetOrCreate (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
IN UINT32 StreamId,
|
||||
OUT PAGE_TABLE **OutRoot,
|
||||
OUT UINT16 *OutVmid
|
||||
);
|
||||
|
||||
/**
|
||||
Free every SMMU_STREAM_ID_ENTRY hanging off the given list head and
|
||||
re-initialize the head as empty. Safe to call on an already-empty list.
|
||||
|
||||
@param[in,out] StreamIdList List head previously populated by
|
||||
DeviceHandleToStreamId.
|
||||
**/
|
||||
VOID
|
||||
SmmuStreamIdListFree (
|
||||
IN OUT LIST_ENTRY *StreamIdList
|
||||
);
|
||||
|
||||
/**
|
||||
Resolve a DeviceHandle to its IORT-derived StreamID(s).
|
||||
|
||||
For real PCIe devices (Segment != 0xFF):
|
||||
PciIo->GetLocation() -> RID -> IORT RC node ID mapping -> single StreamID.
|
||||
The matched mapping's OutputReference identifies the owning SMMUv3 node,
|
||||
whose base address is returned in *SmmuBase.
|
||||
|
||||
For NonDiscoverable devices (Segment == 0xFF):
|
||||
UniqueId -> platform NC table entry -> IORT Named Component node ->
|
||||
full StreamID list (every mapping expanded, including ranges).
|
||||
|
||||
@param[in] IortTable Pointer to the IORT ACPI table.
|
||||
@param[in] DeviceHandle The device handle to resolve.
|
||||
@param[in,out] StreamIdList Caller-supplied, initialized-empty list head.
|
||||
On success contains one SMMU_STREAM_ID_ENTRY
|
||||
per resolved StreamID, in IORT order (first
|
||||
entry is the primary; rest are aliases that
|
||||
share the primary's stage-2 page table and
|
||||
VMID). Caller must release via
|
||||
SmmuStreamIdListFree.
|
||||
@param[out] SmmuBase Optional. If non-NULL, receives the base
|
||||
address of the SMMUv3 node that owns these
|
||||
StreamIDs (0 if unknown).
|
||||
|
||||
@retval EFI_SUCCESS StreamIDs resolved.
|
||||
@retval EFI_INVALID_PARAMETER One or more required parameters are NULL.
|
||||
@retval EFI_UNSUPPORTED DeviceHandle has no PciIo protocol.
|
||||
@retval EFI_NOT_FOUND No IORT mapping found.
|
||||
@retval EFI_OUT_OF_RESOURCES Allocation failure while building the list.
|
||||
**/
|
||||
EFI_STATUS
|
||||
DeviceHandleToStreamId (
|
||||
IN VOID *IortTable,
|
||||
IN EFI_HANDLE DeviceHandle,
|
||||
IN OUT LIST_ENTRY *StreamIdList,
|
||||
OUT UINT64 *SmmuBase
|
||||
);
|
||||
|
||||
/**
|
||||
Build a default "abort-equivalent" stream-table entry. Equivalent to a
|
||||
STAGE_2_TRANSLATE STE with S2Ttb = 0 — stage 2 is enabled but no
|
||||
translations are installed, so DMA misses fault and are recorded. Used at
|
||||
init to populate every STE slot before any device has been mapped.
|
||||
|
||||
@param [in] SmmuInfo SMMU instance (needed for IDR-derived fields).
|
||||
@param [out] StreamEntry STE buffer to populate.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_INVALID_PARAMETER Invalid parameters.
|
||||
@retval Other Failure from the underlying translate STE builder.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3BuildInvalidStreamTableEntry (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
OUT SMMUV3_STREAM_TABLE_ENTRY *StreamEntry
|
||||
);
|
||||
|
||||
/**
|
||||
Build a STAGE_2_TRANSLATE stream-table entry using the given page-table root.
|
||||
|
||||
@param [in] SmmuInfo Pointer to the SMMU_INFO structure.
|
||||
@param [in] PageTableRoot Page-table root the STE should point at.
|
||||
@param [in] Vmid VMID tag to install in the STE's S2VMID field.
|
||||
@param [out] StreamEntry STE buffer to populate.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_INVALID_PARAMETER Invalid parameters.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3BuildStage2TranslateStreamTableEntry (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
IN PAGE_TABLE *PageTableRoot,
|
||||
IN UINT16 Vmid,
|
||||
OUT SMMUV3_STREAM_TABLE_ENTRY *StreamEntry
|
||||
);
|
||||
|
||||
/**
|
||||
Locate the STE slot in the SMMU's stream table for a given StreamID.
|
||||
Supports both linear and 2-level stream tables.
|
||||
|
||||
@param [in] SmmuInfo Pointer to the SMMU_INFO structure.
|
||||
@param [in] StreamId The StreamID.
|
||||
|
||||
@retval Pointer to the STE slot, or NULL if out-of-range.
|
||||
**/
|
||||
SMMUV3_STREAM_TABLE_ENTRY *
|
||||
SmmuV3GetSteSlot (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
IN UINT32 StreamId
|
||||
);
|
||||
|
||||
/**
|
||||
Promote the STE for StreamId from Invalid to Valid with the given
|
||||
page-table root, using the SMMU break-before-make sequence required by the
|
||||
SMMUv3 spec for STE Config changes:
|
||||
|
||||
1. Write the STE with V=0.
|
||||
2. DSB + CFGI_STE(StreamId) + CMD_SYNC.
|
||||
3. Write the full new STE contents (S2Ttb etc., Config=S2_TRANSLATE, V=1).
|
||||
4. DSB + CFGI_STE(StreamId) + CMD_SYNC.
|
||||
|
||||
@param [in] SmmuInfo Pointer to the SMMU_INFO structure.
|
||||
@param [in] StreamId The StreamID whose STE is being promoted.
|
||||
@param [in] Vmid VMID tag to install in the STE's S2VMID field.
|
||||
@param [in] PageTableRoot Page-table root to install in the STE.
|
||||
@param [in] NewL2 Caller-provided L2 page used by
|
||||
SmmuV3SplitL1IfShared() when the covering L1
|
||||
descriptor still points at the shared-ABORT
|
||||
L2. Because the caller owns the PageTableRoot and NewL2,
|
||||
this function does not allocate or free them.
|
||||
@param [out] NewL2Consumed Set to TRUE if NewL2 was installed into an L1
|
||||
descriptor by the split step (the caller must
|
||||
NOT free it), FALSE otherwise (the caller
|
||||
should free NewL2).
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_INVALID_PARAMETER Invalid parameters.
|
||||
@retval Other Command-queue / sync failure.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3PromoteSteToStage2Translate (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
IN UINT32 StreamId,
|
||||
IN UINT16 Vmid,
|
||||
IN PAGE_TABLE *PageTableRoot,
|
||||
IN SMMUV3_STREAM_TABLE_ENTRY *NewL2,
|
||||
OUT BOOLEAN *NewL2Consumed
|
||||
);
|
||||
|
||||
/**
|
||||
Allocate a Context Descriptor (CD) suitable for use as a Stage 1 STE's
|
||||
S1ContextPtr. The CD is 64-byte aligned as required by the SMMUv3 spec
|
||||
and zero-initialized (V=0). The caller populates Ttb0 / Asid / TCR
|
||||
fields via SmmuV3BuildStage1ContextDescriptor before publishing it via
|
||||
SmmuV3PromoteSteToStage1Translate.
|
||||
|
||||
@retval Pointer to the zeroed CD, or NULL on failure.
|
||||
**/
|
||||
SMMUV3_CONTEXT_DESCRIPTOR *
|
||||
SmmuV3AllocateContextDescriptor (
|
||||
VOID
|
||||
);
|
||||
|
||||
/**
|
||||
Free a Context Descriptor previously returned by
|
||||
SmmuV3AllocateContextDescriptor.
|
||||
|
||||
@param [in] Cd Context Descriptor to free. May be NULL.
|
||||
**/
|
||||
VOID
|
||||
SmmuV3FreeContextDescriptor (
|
||||
IN SMMUV3_CONTEXT_DESCRIPTOR *Cd
|
||||
);
|
||||
|
||||
/**
|
||||
Populate a Context Descriptor for Stage 1 identity-mapped translation.
|
||||
Sets Ttb0 to the given page-table root, ASID to the supplied per-stream
|
||||
tag, T0Sz / TG0 / IPS / IR0 / OR0 / SH0 based on the SMMU's output
|
||||
address width and the platform's coherency configuration, MAIR to
|
||||
attribute index 0 = Normal WB Inner+Outer, disables TTBR1, and sets
|
||||
AArch64 + Valid = 1.
|
||||
|
||||
@param [in] SmmuInfo SMMU instance (needed for IDR-derived fields).
|
||||
@param [in] PageTableRoot Stage 1 page-table root to install in Ttb0.
|
||||
NULL builds an invalid (V = 0) CD suitable
|
||||
for the init-time template.
|
||||
@param [in] Asid ASID tag installed in CD.Asid.
|
||||
@param [out] Cd CD buffer to populate.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_INVALID_PARAMETER Invalid parameters.
|
||||
@retval Other Failure from starting-level computation.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3BuildStage1ContextDescriptor (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
IN PAGE_TABLE *PageTableRoot,
|
||||
IN UINT16 Asid,
|
||||
OUT SMMUV3_CONTEXT_DESCRIPTOR *Cd
|
||||
);
|
||||
|
||||
/**
|
||||
Build a STAGE_1_TRANSLATE / STAGE_2_BYPASS stream-table entry that
|
||||
points at the supplied Context Descriptor. Passing Cd = NULL builds an
|
||||
invalid (Valid = 0) STE suitable for the init-time template.
|
||||
|
||||
@param [in] SmmuInfo SMMU instance.
|
||||
@param [in] Cd CD the STE's S1ContextPtr should point at, or
|
||||
NULL to build an invalid STE.
|
||||
@param [out] StreamEntry STE buffer to populate.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_INVALID_PARAMETER Invalid parameters.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3BuildStage1TranslateStreamTableEntry (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
IN SMMUV3_CONTEXT_DESCRIPTOR *Cd,
|
||||
OUT SMMUV3_STREAM_TABLE_ENTRY *StreamEntry
|
||||
);
|
||||
|
||||
/**
|
||||
Promote the STE for StreamId from ABORT to STAGE_1_TRANSLATE /
|
||||
STAGE_2_BYPASS with the supplied CD, using the SMMU break-before-make
|
||||
sequence required for STE Config changes.
|
||||
|
||||
@param [in] SmmuInfo Pointer to the SMMU_INFO structure.
|
||||
@param [in] StreamId The StreamID whose STE is being promoted.
|
||||
@param [in] Cd Context Descriptor to install in the STE.
|
||||
@param [in] NewL2 Caller-provided L2 page used by
|
||||
SmmuV3SplitL1IfShared() when the covering L1
|
||||
descriptor still points at the shared-ABORT
|
||||
L2. Because the caller owns Cd and NewL2,
|
||||
this function does not allocate or free them.
|
||||
@param [out] NewL2Consumed Set to TRUE if NewL2 was installed into an L1
|
||||
descriptor by the split step (the caller must
|
||||
NOT free it), FALSE otherwise (the caller
|
||||
should free NewL2).
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_INVALID_PARAMETER Invalid parameters.
|
||||
@retval Other Command-queue / sync failure.
|
||||
**/
|
||||
EFI_STATUS
|
||||
SmmuV3PromoteSteToStage1Translate (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
IN UINT32 StreamId,
|
||||
IN SMMUV3_CONTEXT_DESCRIPTOR *Cd,
|
||||
IN SMMUV3_STREAM_TABLE_ENTRY *NewL2,
|
||||
OUT BOOLEAN *NewL2Consumed
|
||||
);
|
||||
|
||||
/**
|
||||
Update the page table mapping with the given physical address and attributes.
|
||||
|
||||
@param [in] SmmuInfo SMMU instance.
|
||||
@param [in] Root Pointer to the root page table.
|
||||
@param [in] TagId Per-stream tag whose TLB entries
|
||||
should be invalidated on unmap.
|
||||
VMID for Stage 2 SMMUs, ASID for
|
||||
Stage 1 SMMUs.
|
||||
@param [in] PhysicalAddress Physical address to map.
|
||||
@param [in] Bytes Number of bytes to map.
|
||||
@param [in] Attributes Attributes to set for the mapping. Must be a valid Stage 2 Translation Table attribute (12 bits or less).
|
||||
@param [in] Valid Boolean to indicate if the entry is valid.
|
||||
|
||||
@retval EFI_SUCCESS Success.
|
||||
@retval EFI_INVALID_PARAMETER Invalid parameter.
|
||||
@retval EFI_OUT_OF_RESOURCES Out of resources.
|
||||
**/
|
||||
EFI_STATUS
|
||||
UpdatePageTable (
|
||||
IN SMMU_INFO *SmmuInfo,
|
||||
IN PAGE_TABLE *Root,
|
||||
IN UINT16 TagId,
|
||||
IN UINT64 PhysicalAddress,
|
||||
IN UINT64 Bytes,
|
||||
IN UINT16 Attributes,
|
||||
IN BOOLEAN Valid
|
||||
);
|
||||
2384
ArmPkg/Drivers/SmmuDxe/SmmuV3Util.c
Normal file
2384
ArmPkg/Drivers/SmmuDxe/SmmuV3Util.c
Normal file
File diff suppressed because it is too large
Load diff
73
ArmPkg/Include/Guid/SmmuConfig.h
Normal file
73
ArmPkg/Include/Guid/SmmuConfig.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/** @file
|
||||
File for SMMU config structures.
|
||||
|
||||
This SMMU_CONFIG structure is used to pass the SMMU configuration data from
|
||||
the platform to the SMMU driver. The Smmu driver will use this data to install
|
||||
the IORT table and configure the SMMU hardware.
|
||||
|
||||
Given the IORT is configurable and platform dependent, the SMMU_CONFIG structure contains
|
||||
all info relevant to the IORT table and SMMUv3 platform specific configuration.
|
||||
|
||||
See <https://developer.arm.com/documentation/den0049/latest/> for IORT spec.
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Increment SMMU_CONFIG version when the structure changes.
|
||||
// Backwards compatibility is currently not supported.
|
||||
// Future backwards compatibility is only possible if new fields are added to the end of the structure and existing fields are not modified.
|
||||
// SmmuDxe driver will check and enforce the version of the SMMU_CONFIG structure to this current version set here.
|
||||
#define CURRENT_SMMU_CONFIG_VERSION_MAJOR 1
|
||||
#define CURRENT_SMMU_CONFIG_VERSION_MINOR 1
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
// Maximum length (including NULL) of the ACPI namespace path stored for a
|
||||
// NonDiscoverable device entry. Must be large enough to hold the
|
||||
// IORT Named Component ObjectName
|
||||
#define SMMU_NC_DEVICE_OBJNAME_MAX 32
|
||||
|
||||
// Per-SMMU translation stage selection. Every SMMU described by the HOB is
|
||||
// configured for the same stage.
|
||||
typedef enum _SMMU_TRANSLATION_STAGE {
|
||||
SmmuTranslationStage2 = 0, // Stage 2 translate, Stage 1 bypass
|
||||
SmmuTranslationStage1 = 1, // Stage 1 translate, Stage 2 bypass
|
||||
} SMMU_TRANSLATION_STAGE;
|
||||
|
||||
// Platform-provided lookup entry mapping a NonDiscoverable device's
|
||||
// PciIo->GetLocation()-derived UniqueId to the SMMU StreamID's associated
|
||||
// with the NamedComponent node in the IORT.
|
||||
// Each NonDiscoverable device exposes a UniqueId from the NonDiscoverableDeviceRegistrationLib.
|
||||
// This is used to determine a determinstic PciIo->GetLocation().
|
||||
typedef struct _SMMU_NC_DEVICE_ENTRY {
|
||||
UINT64 UniqueId; // Value from the NON_DISCOVERABLE_DEVICE_UNIQUE_ID protocol on the handle.
|
||||
CHAR8 ObjName[SMMU_NC_DEVICE_OBJNAME_MAX]; // IORT Named Component ObjectName (NUL-terminated). SmmuDxe walks the IORT for the matching NC node to recover the owning SMMU base and the full StreamID list.
|
||||
} SMMU_NC_DEVICE_ENTRY;
|
||||
|
||||
// SMMU_CONFIG structure to pass the SMMU configuration data from the platform to the SMMU driver.
|
||||
// Platform will configure SmmuDisabledList size and offset to the SMMU disabled list appropriatley
|
||||
// for any SMMU that needs be disabled in UEFI and set to bypass.
|
||||
typedef struct _SMMU_CONFIG {
|
||||
UINT32 VersionMajor;
|
||||
UINT32 VersionMinor;
|
||||
UINT32 SmmuDisabledListSize; // Size of SmmuDisabledList in bytes.
|
||||
UINT32 SmmuDisabledListOffset; // Offset in bytes to the SmmuDisabledList from the start of the HOB structure.
|
||||
UINT32 IortSize;
|
||||
UINT32 IortOffset; // Offset in bytes to the IORT table from the start of the HOB structure.
|
||||
UINT32 NcDeviceListSize; // Size of the NonDiscoverable device lookup array in bytes (multiple of sizeof(SMMU_NC_DEVICE_ENTRY)).
|
||||
UINT32 NcDeviceListOffset; // Offset in bytes to the NonDiscoverable device lookup array from the start of the HOB structure. 0 if absent.
|
||||
SMMU_TRANSLATION_STAGE TranslationStage; // SmmuTranslationStage2 (default) or SmmuTranslationStage1.
|
||||
// SmmuDxe will configure every SMMU described by this HOB to the same stage.
|
||||
// Any value other than SmmuTranslationStage1 is treated as SmmuTranslationStage2.
|
||||
} SMMU_CONFIG;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#define SMMU_CONFIG_HOB_GUID \
|
||||
{ 0xcd56ec8f, 0x75f1, 0x440a, { 0xaa, 0x48, 0x09, 0x58, 0xb1, 0x1c, 0x9a, 0xa7 } }
|
||||
|
||||
extern EFI_GUID gSmmuConfigHobGuid;
|
||||
1838
ArmPkg/Include/Register/SmmuV3Registers.h
Normal file
1838
ArmPkg/Include/Register/SmmuV3Registers.h
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue