From b5576548a4372c9da168c6f3c9dfc7a5abdb6502 Mon Sep 17 00:00:00 2001 From: eeshanl Date: Thu, 8 Jan 2026 07:56:16 +0000 Subject: [PATCH 1/4] SmmuV3 DMA Protection Audit Tests --- .../UEFI/DMASmmuProtectionUnitTestApp.inf | 58 ++++ .../UEFI/SMMU/DMAProtectionTestArch.c | 271 ++++++++++++++++++ .../UEFI/SMMU/DmaProtection.h | 103 +++++++ .../UEFI/SMMU/IortAcpiTable.c | 207 +++++++++++++ 4 files changed, 639 insertions(+) create mode 100644 UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf create mode 100644 UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c create mode 100644 UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h create mode 100644 UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf new file mode 100644 index 0000000000..72d7bd7c37 --- /dev/null +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf @@ -0,0 +1,58 @@ +## @file DMASmmuProtectionUnitTestApp.inf +## +# DMA Protection Unit Test App for ARM SMMUv3 platforms. +# Tests SMMU translation enable status and RMR reserved memory regions. +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = DMASmmuProtectionUnitTestApp + FILE_GUID = 7A8C2E45-B9F3-4D12-A6E8-9C1B5F3D2E7A + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = DMAProtectionUnitTestApp + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = AARCH64 +# + +[Sources] + Acpi.c + Acpi.h + DMAProtectionTest.h + DMAProtectionUnitTestApp.c + SMMU/DmaProtection.h + SMMU/DMAProtectionTestArch.c + SMMU/IortAcpiTable.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiTestingPkg/UefiTestingPkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + ShellPkg/ShellPkg.dec + +[Protocols] + gEfiPciIoProtocolGuid ## CONSUMES + +[LibraryClasses] + UefiApplicationEntryPoint + DebugLib + UnitTestLib + UnitTestBootLib + IoLib + MemoryAllocationLib + ShellLib + PciLib + +[Guids] + gDMAUnitTestVariableGuid + gEfiAcpi20TableGuid + gEfiAcpi10TableGuid diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c new file mode 100644 index 0000000000..a749cdd870 --- /dev/null +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c @@ -0,0 +1,271 @@ +/** @file -- DMAProtectionTestArch.c + +This file contains architecture specific DMA protection tests for ARM SMMU (SMMUv3): +1) Check the CR0 registers of the SMMUv3 nodes to verify SMMU translation is enabled +2) Check that Command Queue is enabled (CMDQEN bit in CR0) +3) Check that Event Queue is enabled (EVTQEN bit in CR0) +4) Check that Stream Table Base is configured (STRTAB_BASE is not NULL) +5) Check that GERROR register is 0 (no global errors) +6) Check RMR (Reserved Memory Range) regions from IORT are set as reserved in memory map + +Copyright (c) Microsoft Corporation. All rights reserved. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include + +#include "DmaProtection.h" + +/// ================================================================================================ +/// ================================================================================================ +/// +/// TEST CASES +/// +/// ================================================================================================ +/// ================================================================================================ + +/** + Test to verify that the Reserved Memory Range (RMR) regions defined in the IORT + are properly marked as reserved in the EFI memory map. + + @param[in] Context The unit test context (not used). + + @retval UNIT_TEST_PASSED All RMR regions are properly marked as reserved. + @retval UNIT_TEST_ERROR_TEST_FAILED A RMR region was not found or not marked as reserved. +**/ +UNIT_TEST_STATUS +EFIAPI +CheckExcludedRegions ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + EFI_MEMORY_DESCRIPTOR *EfiMemoryMap; + EFI_MEMORY_DESCRIPTOR *EfiMemoryMapEnd; + EFI_MEMORY_DESCRIPTOR *EfiMemNext; + UINTN EfiMemoryMapSize; + UINTN EfiMapKey; + UINTN EfiDescriptorSize; + UINT32 EfiDescriptorVersion; + EFI_ACPI_DESCRIPTION_HEADER *IortTable; + RMRListNode *Head; + RMRListNode *Current; + BOOLEAN Found; + + // + // Step 1: Get IORT Table + // + IortTable = NULL; + Status = GetIortAcpiTable (&IortTable); + UT_ASSERT_NOT_EFI_ERROR (Status); + + // + // Step 2: Get the RMR (Reserved Memory Range) nodes from IORT Table + // + Head = GetIortAcpiTableRmrList (IortTable); + if (Head == NULL) { + UT_LOG_INFO ("No RMRs Found in IORT\n"); + return UNIT_TEST_PASSED; + } + + Current = Head; + + // + // Step 3: Get the EFI memory map. + // + EfiMemoryMapSize = 0; + EfiMemoryMap = NULL; + Status = gBS->GetMemoryMap ( + &EfiMemoryMapSize, + EfiMemoryMap, + &EfiMapKey, + &EfiDescriptorSize, + &EfiDescriptorVersion + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + EfiMemoryMap = (EFI_MEMORY_DESCRIPTOR *)AllocateZeroPool (EfiMemoryMapSize + 8*EfiDescriptorSize); + UT_ASSERT_NOT_NULL (EfiMemoryMap); + Status = gBS->GetMemoryMap ( + &EfiMemoryMapSize, + EfiMemoryMap, + &EfiMapKey, + &EfiDescriptorSize, + &EfiDescriptorVersion + ); + UT_ASSERT_NOT_EFI_ERROR (Status); + } else { + UT_LOG_ERROR ("GetMemoryMap Failed\n"); + return UNIT_TEST_ERROR_TEST_FAILED; + } + + // + // Step 4: Step through memory map and verify each + // RMR memory range is marked reserved + // + EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)EfiMemoryMap + EfiMemoryMapSize); + + while (Current != NULL) { + Found = FALSE; + EfiMemNext = EfiMemoryMap; + + UT_LOG_INFO ("Checking RMR region: Base=0x%lX, Length=0x%lX\n", Current->BaseAddress, Current->Length); + + while (EfiMemNext < EfiMemoryMapEnd) { + // Check if memory range fully encompasses RMR + if ((EfiMemNext->PhysicalStart <= Current->BaseAddress) && + ((EfiMemNext->PhysicalStart + (EFI_PAGE_SIZE * EfiMemNext->NumberOfPages)) >= (Current->BaseAddress + Current->Length))) + { + // Verify memory range is marked as reserved (accept both EfiReservedMemoryType and EfiACPIMemoryNVS) + if ((EfiMemNext->Type == EfiReservedMemoryType) || (EfiMemNext->Type == EfiACPIMemoryNVS)) { + UT_LOG_INFO ("RMR between 0x%lX and 0x%lX found with reserved memory type %d\n", + Current->BaseAddress, Current->BaseAddress + Current->Length, EfiMemNext->Type); + Found = TRUE; + break; + } + } + + // Move on to next descriptor + EfiMemNext = NEXT_MEMORY_DESCRIPTOR (EfiMemNext, EfiDescriptorSize); + } + + if (!Found) { + UT_LOG_ERROR ("RMR between 0x%lX and 0x%lX NOT found with reserved memory type!\n", + Current->BaseAddress, Current->BaseAddress + Current->Length); + FreePool (EfiMemoryMap); + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Current = Current->Next; + } + + FreePool (EfiMemoryMap); + return UNIT_TEST_PASSED; +} // CheckExcludedRegions() + +/** + Test to verify that all SMMUv3 units found in the IORT have translation enabled. + This checks: + 1) CR0 register's SMMUEN bit to confirm the SMMU is actively translating + 2) CR0 register's CMDQEN bit to confirm the command queue is enabled + 3) CR0 register's EVTQEN bit to confirm the event queue is enabled + 4) STRTAB_BASE register is not NULL (stream table must be configured) + 5) GERROR register is 0 (no global errors) + + @param[in] Context The unit test context (not used). + + @retval UNIT_TEST_PASSED All SMMU units are properly configured. + @retval UNIT_TEST_ERROR_TEST_FAILED An SMMU unit is not properly configured. +**/ +UNIT_TEST_STATUS +EFIAPI +CheckIOMMUEnabled ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + EFI_ACPI_DESCRIPTION_HEADER *IortTable; + UINTN SmmuCount; + UINT64 *SmmuBaseAddresses; + UINTN Iterator; + UINT32 Cr0Value; + UINT32 SmmEnBit; + UINT32 CmdQEnBit; + UINT32 EvtQEnBit; + UINT64 StrTabBase; + UINT64 StrTabBaseAddr; + UINT32 GError; + + // + // Step 1: Get IORT Table + // + IortTable = NULL; + Status = GetIortAcpiTable (&IortTable); + UT_ASSERT_NOT_EFI_ERROR (Status); + + // + // Step 2: Find and parse SMMUv3 nodes from IORT + // + SmmuCount = 0; + SmmuBaseAddresses = NULL; + Status = ParseIortAcpiTableSmmu (IortTable, &SmmuCount, &SmmuBaseAddresses); + UT_ASSERT_NOT_EFI_ERROR (Status); + + // + // Step 3: Check that we found at least one SMMU + // + UT_ASSERT_TRUE (SmmuCount > 0); + UT_LOG_INFO ("Found %d SMMUv3 units in IORT\n", SmmuCount); + + // + // Step 4: For each SMMU, check: + // - SMMU Enable bit (SMMUEN) in CR0 register + // - Command Queue Enable bit (CMDQEN) in CR0 register + // - Event Queue Enable bit (EVTQEN) in CR0 register + // - Stream Table Base address is not NULL + // - GERROR register is 0 + // + for (Iterator = 0; Iterator < SmmuCount; Iterator++) { + UT_LOG_INFO ("Checking SMMUv3 at base address 0x%lX\n", SmmuBaseAddresses[Iterator]); + + // + // Read CR0 register + // + Cr0Value = MmioRead32 ((UINTN)(SmmuBaseAddresses[Iterator] + SMMU_CR0)); + UT_LOG_INFO (" CR0 Register Value: 0x%X\n", Cr0Value); + + // + // Check SMMUEN bit (bit 0) + // + SmmEnBit = Cr0Value & SMMU_CR0_SMMUEN; + UT_LOG_INFO (" SMMUEN bit: %d\n", SmmEnBit); + UT_ASSERT_NOT_EQUAL (SmmEnBit, 0); + + // + // Check CMDQEN bit (bit 1) - Command Queue must be enabled + // + CmdQEnBit = Cr0Value & SMMU_CR0_CMDQEN; + UT_LOG_INFO (" CMDQEN bit: %d\n", CmdQEnBit ? 1 : 0); + UT_ASSERT_NOT_EQUAL (CmdQEnBit, 0); + + // + // Check EVTQEN bit (bit 2) - Event Queue must be enabled + // + EvtQEnBit = Cr0Value & SMMU_CR0_EVTQEN; + UT_LOG_INFO (" EVTQEN bit: %d\n", EvtQEnBit ? 1 : 0); + UT_ASSERT_NOT_EQUAL (EvtQEnBit, 0); + + // + // Read STRTAB_BASE register and check it's not NULL + // If NULL, no stream IDs can undergo translation + // + StrTabBase = MmioRead64 ((UINTN)(SmmuBaseAddresses[Iterator] + SMMU_STRTAB_BASE)); + UT_LOG_INFO (" STRTAB_BASE Register Value: 0x%lX\n", StrTabBase); + + // Extract the address portion by masking out lower 6 bits (bits [5:0] are reserved/config) + StrTabBaseAddr = StrTabBase & ~SMMU_STRTAB_BASE_ADDR_MASK; + UT_LOG_INFO (" STRTAB_BASE Address: 0x%lX\n", StrTabBaseAddr); + + // Assert that Stream Table Base Address is not NULL + UT_ASSERT_NOT_EQUAL (StrTabBaseAddr, 0); + + // + // Read GERROR register and check it's 0 (no global errors) + // + GError = MmioRead32 ((UINTN)(SmmuBaseAddresses[Iterator] + SMMU_GERROR)); + UT_LOG_INFO (" GERROR Register Value: 0x%X\n", GError); + UT_ASSERT_EQUAL (GError, 0); + } + + // Free the allocated memory + if (SmmuBaseAddresses != NULL) { + FreePool (SmmuBaseAddresses); + } + + return UNIT_TEST_PASSED; +} // CheckIOMMUEnabled() diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h new file mode 100644 index 0000000000..95333cdfb3 --- /dev/null +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h @@ -0,0 +1,103 @@ +/** @file -- DmaProtection.h + +Header file for SMMU DMA protection tests. + +Copyright (c) Microsoft Corporation. All rights reserved. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _DMA_PROTECTION_H_ +#define _DMA_PROTECTION_H_ + +#include +#include + +// +// IORT Table Signature +// +#define EFI_ACPI_6_0_IO_REMAPPING_TABLE_SIGNATURE SIGNATURE_32('I', 'O', 'R', 'T') + +// +// SMMUv3 Register Offsets +// +#define SMMU_CR0 0x0020 +#define SMMU_CR0ACK 0x0024 +#define SMMU_GERROR 0x0060 +#define SMMU_STRTAB_BASE 0x0080 + +// +// SMMUv3 CR0 Register Bits +// +#define SMMU_CR0_SMMUEN BIT0 // SMMU Enable bit +#define SMMU_CR0_CMDQEN BIT1 // Command Queue Enable bit +#define SMMU_CR0_EVTQEN BIT2 // Event Queue Enable bit + +// +// STRTAB_BASE lower bits mask (bits [5:0] are reserved/config) +// +#define SMMU_STRTAB_BASE_ADDR_MASK 0x3FULL + +// +// RMR List Node Structure for tracking Reserved Memory Ranges +// +typedef struct _RMRListNode { + UINT64 BaseAddress; + UINT64 Length; + struct _RMRListNode *Next; +} RMRListNode; + +// +// Function Prototypes +// + +/** + Get the IORT ACPI table from the system. + + @param[out] IortTable Pointer to receive the IORT table pointer. + + @retval EFI_SUCCESS The IORT table was found. + @retval EFI_NOT_FOUND The IORT table was not found. + @retval EFI_INVALID_PARAMETER IortTable is NULL. +**/ +EFI_STATUS +EFIAPI +GetIortAcpiTable ( + OUT EFI_ACPI_DESCRIPTION_HEADER **IortTable + ); + +/** + Parse the IORT table to find all SMMUv3 nodes. + + @param[in] IortTable Pointer to the IORT table. + @param[out] SmmuCount Pointer to receive the count of SMMUv3 nodes. + @param[out] SmmuBaseAddresses Pointer to receive the array of SMMU base addresses. + Caller must free this memory when done. + + @retval EFI_SUCCESS SMMUv3 nodes were found and parsed. + @retval EFI_NOT_FOUND No SMMUv3 nodes found. + @retval EFI_INVALID_PARAMETER A required parameter is NULL. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +ParseIortAcpiTableSmmu ( + IN EFI_ACPI_DESCRIPTION_HEADER *IortTable, + OUT UINTN *SmmuCount, + OUT UINT64 **SmmuBaseAddresses + ); + +/** + Parse the IORT table to find all RMR (Reserved Memory Range) nodes. + + @param[in] IortTable Pointer to the IORT table. + + @retval Pointer to head of linked list of RMR entries, or NULL if none found. +**/ +RMRListNode* +EFIAPI +GetIortAcpiTableRmrList ( + IN EFI_ACPI_DESCRIPTION_HEADER *IortTable + ); + +#endif // _DMA_PROTECTION_H_ diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c new file mode 100644 index 0000000000..8548fb873f --- /dev/null +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c @@ -0,0 +1,207 @@ +/** @file -- IortAcpiTable.c + +This file contains functions to parse the IORT ACPI table for SMMUv3 nodes +and Reserved Memory Range (RMR) nodes. + +Copyright (c) Microsoft Corporation. All rights reserved. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include + +#include "../Acpi.h" +#include "DmaProtection.h" + +/** + Get the IORT ACPI table. + + @param[out] IortTable Pointer to receive the IORT table pointer. + + @retval EFI_SUCCESS The IORT ACPI table is got. + @retval EFI_ALREADY_STARTED The IORT ACPI table has been got previously. + @retval EFI_NOT_FOUND The IORT ACPI table is not found. + @retval EFI_INVALID_PARAMETER IortTable is NULL. +**/ +EFI_STATUS +EFIAPI +GetIortAcpiTable ( + OUT EFI_ACPI_DESCRIPTION_HEADER **IortTable + ) +{ + if (IortTable == NULL) { + return EFI_INVALID_PARAMETER; + } + + return GetAcpiTable (EFI_ACPI_6_0_IO_REMAPPING_TABLE_SIGNATURE, (VOID **)IortTable); +} + +/** + Parse the IORT table to find all SMMUv3 nodes. + + @param[in] IortTable Pointer to the IORT table. + @param[out] SmmuCount Pointer to receive the count of SMMUv3 nodes. + @param[out] SmmuBaseAddresses Pointer to receive the array of SMMU base addresses. + Caller must free this memory when done. + + @retval EFI_SUCCESS SMMUv3 nodes were found and parsed. + @retval EFI_NOT_FOUND No SMMUv3 nodes found. + @retval EFI_INVALID_PARAMETER A required parameter is NULL. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +ParseIortAcpiTableSmmu ( + IN EFI_ACPI_DESCRIPTION_HEADER *IortTable, + OUT UINTN *SmmuCount, + OUT UINT64 **SmmuBaseAddresses + ) +{ + EFI_ACPI_6_0_IO_REMAPPING_TABLE *Iort; + EFI_ACPI_6_0_IO_REMAPPING_NODE *Node; + EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE *SmmuNode; + UINT32 Count; + UINTN SmmuIndex; + UINTN LocalSmmuCount; + UINT64 *LocalSmmuBaseAddresses; + + if ((IortTable == NULL) || (SmmuCount == NULL) || (SmmuBaseAddresses == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Iort = (EFI_ACPI_6_0_IO_REMAPPING_TABLE *)IortTable; + Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Iort + Iort->NodeOffset); + + // First pass: count SMMUv3 nodes + LocalSmmuCount = 0; + for (Count = 0; Count < Iort->NumNodes; Count++) { + if (Node->Type == EFI_ACPI_IORT_TYPE_SMMUv3) { + LocalSmmuCount++; + } + Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Node + Node->Length); + } + + if (LocalSmmuCount == 0) { + DEBUG ((DEBUG_ERROR, "ParseIortAcpiTableSmmu: No SMMUv3 nodes found\n")); + *SmmuCount = 0; + *SmmuBaseAddresses = NULL; + return EFI_NOT_FOUND; + } + + // Allocate array for SMMU base addresses + LocalSmmuBaseAddresses = AllocateZeroPool (LocalSmmuCount * sizeof (UINT64)); + if (LocalSmmuBaseAddresses == NULL) { + DEBUG ((DEBUG_ERROR, "ParseIortAcpiTableSmmu: Failed to allocate memory for SMMU base addresses\n")); + *SmmuCount = 0; + *SmmuBaseAddresses = NULL; + return EFI_OUT_OF_RESOURCES; + } + + // Second pass: collect base addresses + Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Iort + Iort->NodeOffset); + SmmuIndex = 0; + + for (Count = 0; Count < Iort->NumNodes; Count++) { + if (Node->Type == EFI_ACPI_IORT_TYPE_SMMUv3) { + SmmuNode = (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE *)Node; + LocalSmmuBaseAddresses[SmmuIndex] = SmmuNode->Base; + DEBUG ((DEBUG_INFO, "ParseIortAcpiTableSmmu: Found SMMUv3 at base 0x%lX\n", SmmuNode->Base)); + SmmuIndex++; + } + Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Node + Node->Length); + } + + *SmmuCount = LocalSmmuCount; + *SmmuBaseAddresses = LocalSmmuBaseAddresses; + + return EFI_SUCCESS; +} + +/** + Parse the IORT table to find all RMR (Reserved Memory Range) nodes + and return a linked list of memory ranges. + + This function iterates through all nodes in the IORT table looking for + RMR nodes (type 0x6). For each RMR node found, it extracts all memory + range descriptors and adds them to the returned linked list. + + @param[in] IortTable Pointer to the IORT table. + + @retval Pointer to head of linked list of RMR entries, or NULL if none found. +**/ +RMRListNode* +EFIAPI +GetIortAcpiTableRmrList ( + IN EFI_ACPI_DESCRIPTION_HEADER *IortTable + ) +{ + EFI_ACPI_6_0_IO_REMAPPING_TABLE *Iort; + EFI_ACPI_6_0_IO_REMAPPING_NODE *Node; + EFI_ACPI_6_0_IO_REMAPPING_RMR_NODE *RmrNode; + EFI_ACPI_6_0_IO_REMAPPING_MEM_RANGE_DESC *MemRangeDesc; + RMRListNode *Head; + RMRListNode *Current; + RMRListNode *NewNode; + UINT32 Count; + UINT32 MemRangeIndex; + + if (IortTable == NULL) { + DEBUG ((DEBUG_ERROR, "GetIortAcpiTableRmrList: IORT table not available\n")); + return NULL; + } + + Iort = (EFI_ACPI_6_0_IO_REMAPPING_TABLE *)IortTable; + Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Iort + Iort->NodeOffset); + + Head = NULL; + Current = NULL; + + // Iterate through all nodes looking for RMR nodes + for (Count = 0; Count < Iort->NumNodes; Count++) { + if (Node->Type == EFI_ACPI_IORT_TYPE_RMR) { + RmrNode = (EFI_ACPI_6_0_IO_REMAPPING_RMR_NODE *)Node; + DEBUG ((DEBUG_INFO, "GetIortAcpiTableRmrList: Found RMR node with %d memory range descriptors\n", + RmrNode->NumMemRangeDesc)); + + // Get pointer to memory range descriptor array + // MemRangeDescRef is offset from the start of the RMR node + MemRangeDesc = (EFI_ACPI_6_0_IO_REMAPPING_MEM_RANGE_DESC *)((UINT8 *)RmrNode + RmrNode->MemRangeDescRef); + + // Iterate through all memory range descriptors in this RMR node + for (MemRangeIndex = 0; MemRangeIndex < RmrNode->NumMemRangeDesc; MemRangeIndex++) { + // Only add valid ranges (non-zero base and length) + if ((MemRangeDesc[MemRangeIndex].Base > 0) && (MemRangeDesc[MemRangeIndex].Length > 0)) { + NewNode = AllocateZeroPool (sizeof (RMRListNode)); + if (NewNode == NULL) { + DEBUG ((DEBUG_ERROR, "GetIortAcpiTableRmrList: Failed to allocate RMRListNode\n")); + // Return what we have so far + return Head; + } + + NewNode->BaseAddress = MemRangeDesc[MemRangeIndex].Base; + NewNode->Length = MemRangeDesc[MemRangeIndex].Length; + NewNode->Next = NULL; + + DEBUG ((DEBUG_INFO, "GetIortAcpiTableRmrList: Adding RMR range Base=0x%lX, Length=0x%lX\n", + NewNode->BaseAddress, NewNode->Length)); + + // Add to linked list + if (Head == NULL) { + Head = NewNode; + Current = NewNode; + } else { + Current->Next = NewNode; + Current = NewNode; + } + } + } + } + + // Move to the next node + Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Node + Node->Length); + } + + return Head; +} From c9def23be73269951b8fb95b335edcad4b6251bb Mon Sep 17 00:00:00 2001 From: eeshanl Date: Thu, 8 Jan 2026 10:48:31 +0000 Subject: [PATCH 2/4] add DEBUG statments and fix CMDQEN bit --- .../UEFI/DMASmmuProtectionUnitTestApp.inf | 1 + .../UEFI/SMMU/DMAProtectionTestArch.c | 36 +++++++++++++++++++ .../UEFI/SMMU/DmaProtection.h | 2 +- .../UEFI/SMMU/IortAcpiTable.c | 18 +++++----- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf index 72d7bd7c37..2c7defe287 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf @@ -47,6 +47,7 @@ DebugLib UnitTestLib UnitTestBootLib + UnitTestPersistenceLib IoLib MemoryAllocationLib ShellLib diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c index a749cdd870..812e171088 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c @@ -71,6 +71,7 @@ CheckExcludedRegions ( Head = GetIortAcpiTableRmrList (IortTable); if (Head == NULL) { UT_LOG_INFO ("No RMRs Found in IORT\n"); + DEBUG ((DEBUG_INFO, "%a: No RMRs Found in IORT\n", __func__)); return UNIT_TEST_PASSED; } @@ -101,6 +102,7 @@ CheckExcludedRegions ( UT_ASSERT_NOT_EFI_ERROR (Status); } else { UT_LOG_ERROR ("GetMemoryMap Failed\n"); + DEBUG ((DEBUG_ERROR, "%a: GetMemoryMap Failed\n", __func__)); return UNIT_TEST_ERROR_TEST_FAILED; } @@ -115,16 +117,26 @@ CheckExcludedRegions ( EfiMemNext = EfiMemoryMap; UT_LOG_INFO ("Checking RMR region: Base=0x%lX, Length=0x%lX\n", Current->BaseAddress, Current->Length); + DEBUG ((DEBUG_INFO, "%a: Checking RMR region: Base=0x%lX, Length=0x%lX\n", __func__, Current->BaseAddress, Current->Length)); while (EfiMemNext < EfiMemoryMapEnd) { // Check if memory range fully encompasses RMR if ((EfiMemNext->PhysicalStart <= Current->BaseAddress) && ((EfiMemNext->PhysicalStart + (EFI_PAGE_SIZE * EfiMemNext->NumberOfPages)) >= (Current->BaseAddress + Current->Length))) { + // Print memory type for debugging + DEBUG ((DEBUG_INFO, "%a: Found encompassing memory range: Base=0x%lX, Length=0x%lX, Type=%d\n", + __func__, + EfiMemNext->PhysicalStart, + EFI_PAGE_SIZE * EfiMemNext->NumberOfPages, + EfiMemNext->Type)); + // Verify memory range is marked as reserved (accept both EfiReservedMemoryType and EfiACPIMemoryNVS) if ((EfiMemNext->Type == EfiReservedMemoryType) || (EfiMemNext->Type == EfiACPIMemoryNVS)) { UT_LOG_INFO ("RMR between 0x%lX and 0x%lX found with reserved memory type %d\n", Current->BaseAddress, Current->BaseAddress + Current->Length, EfiMemNext->Type); + DEBUG ((DEBUG_INFO, "%a: RMR between 0x%lX and 0x%lX found with reserved memory type %d\n", + __func__, Current->BaseAddress, Current->BaseAddress + Current->Length, EfiMemNext->Type)); Found = TRUE; break; } @@ -137,6 +149,8 @@ CheckExcludedRegions ( if (!Found) { UT_LOG_ERROR ("RMR between 0x%lX and 0x%lX NOT found with reserved memory type!\n", Current->BaseAddress, Current->BaseAddress + Current->Length); + DEBUG ((DEBUG_ERROR, "%a: RMR between 0x%lX and 0x%lX NOT found with reserved memory type!\n", + __func__, Current->BaseAddress, Current->BaseAddress + Current->Length)); FreePool (EfiMemoryMap); return UNIT_TEST_ERROR_TEST_FAILED; } @@ -145,6 +159,10 @@ CheckExcludedRegions ( } FreePool (EfiMemoryMap); + + UT_LOG_INFO (" CheckExcludedRegions PASSED\n"); + DEBUG ((DEBUG_INFO, "%a: PASSED\n", __func__)); + return UNIT_TEST_PASSED; } // CheckExcludedRegions() @@ -201,6 +219,7 @@ CheckIOMMUEnabled ( // UT_ASSERT_TRUE (SmmuCount > 0); UT_LOG_INFO ("Found %d SMMUv3 units in IORT\n", SmmuCount); + DEBUG ((DEBUG_INFO, "%a: Found %d SMMUv3 units in IORT\n", __func__, SmmuCount)); // // Step 4: For each SMMU, check: @@ -212,18 +231,27 @@ CheckIOMMUEnabled ( // for (Iterator = 0; Iterator < SmmuCount; Iterator++) { UT_LOG_INFO ("Checking SMMUv3 at base address 0x%lX\n", SmmuBaseAddresses[Iterator]); + DEBUG ((DEBUG_INFO, "%a: Checking SMMUv3 at base address 0x%lX\n", __func__, SmmuBaseAddresses[Iterator])); + + if (SmmuBaseAddresses[Iterator] == 0x13000000) { + UT_LOG_INFO ("Skipping known disabled SMMUv3 at base address 0x%lX\n", SmmuBaseAddresses[Iterator]); + DEBUG ((DEBUG_INFO, "%a: Skipping known disabled SMMUv3 at base address 0x%lX\n", __func__, SmmuBaseAddresses[Iterator])); + continue; + } // // Read CR0 register // Cr0Value = MmioRead32 ((UINTN)(SmmuBaseAddresses[Iterator] + SMMU_CR0)); UT_LOG_INFO (" CR0 Register Value: 0x%X\n", Cr0Value); + DEBUG ((DEBUG_INFO, "%a: CR0 Register Value: 0x%X\n", __func__, Cr0Value)); // // Check SMMUEN bit (bit 0) // SmmEnBit = Cr0Value & SMMU_CR0_SMMUEN; UT_LOG_INFO (" SMMUEN bit: %d\n", SmmEnBit); + DEBUG ((DEBUG_INFO, "%a: SMMUEN bit: %d\n", __func__, SmmEnBit)); UT_ASSERT_NOT_EQUAL (SmmEnBit, 0); // @@ -231,6 +259,7 @@ CheckIOMMUEnabled ( // CmdQEnBit = Cr0Value & SMMU_CR0_CMDQEN; UT_LOG_INFO (" CMDQEN bit: %d\n", CmdQEnBit ? 1 : 0); + DEBUG ((DEBUG_INFO, "%a: CMDQEN bit: %d\n", __func__, CmdQEnBit ? 1 : 0)); UT_ASSERT_NOT_EQUAL (CmdQEnBit, 0); // @@ -238,6 +267,7 @@ CheckIOMMUEnabled ( // EvtQEnBit = Cr0Value & SMMU_CR0_EVTQEN; UT_LOG_INFO (" EVTQEN bit: %d\n", EvtQEnBit ? 1 : 0); + DEBUG ((DEBUG_INFO, "%a: EVTQEN bit: %d\n", __func__, EvtQEnBit ? 1 : 0)); UT_ASSERT_NOT_EQUAL (EvtQEnBit, 0); // @@ -246,10 +276,12 @@ CheckIOMMUEnabled ( // StrTabBase = MmioRead64 ((UINTN)(SmmuBaseAddresses[Iterator] + SMMU_STRTAB_BASE)); UT_LOG_INFO (" STRTAB_BASE Register Value: 0x%lX\n", StrTabBase); + DEBUG ((DEBUG_INFO, "%a: STRTAB_BASE Register Value: 0x%lX\n", __func__, StrTabBase)); // Extract the address portion by masking out lower 6 bits (bits [5:0] are reserved/config) StrTabBaseAddr = StrTabBase & ~SMMU_STRTAB_BASE_ADDR_MASK; UT_LOG_INFO (" STRTAB_BASE Address: 0x%lX\n", StrTabBaseAddr); + DEBUG ((DEBUG_INFO, "%a: STRTAB_BASE Address: 0x%lX\n", __func__, StrTabBaseAddr)); // Assert that Stream Table Base Address is not NULL UT_ASSERT_NOT_EQUAL (StrTabBaseAddr, 0); @@ -259,6 +291,7 @@ CheckIOMMUEnabled ( // GError = MmioRead32 ((UINTN)(SmmuBaseAddresses[Iterator] + SMMU_GERROR)); UT_LOG_INFO (" GERROR Register Value: 0x%X\n", GError); + DEBUG ((DEBUG_INFO, "%a: GERROR Register Value: 0x%X\n", __func__, GError)); UT_ASSERT_EQUAL (GError, 0); } @@ -267,5 +300,8 @@ CheckIOMMUEnabled ( FreePool (SmmuBaseAddresses); } + UT_LOG_INFO (" CheckIOMMUEnabled PASSED\n"); + DEBUG ((DEBUG_INFO, "%a: PASSED\n", __func__)); + return UNIT_TEST_PASSED; } // CheckIOMMUEnabled() diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h index 95333cdfb3..4d9644d9ac 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h @@ -30,8 +30,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent // SMMUv3 CR0 Register Bits // #define SMMU_CR0_SMMUEN BIT0 // SMMU Enable bit -#define SMMU_CR0_CMDQEN BIT1 // Command Queue Enable bit #define SMMU_CR0_EVTQEN BIT2 // Event Queue Enable bit +#define SMMU_CR0_CMDQEN BIT3 // Command Queue Enable bit // // STRTAB_BASE lower bits mask (bits [5:0] are reserved/config) diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c index 8548fb873f..837c5c6bde 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c @@ -84,7 +84,7 @@ ParseIortAcpiTableSmmu ( } if (LocalSmmuCount == 0) { - DEBUG ((DEBUG_ERROR, "ParseIortAcpiTableSmmu: No SMMUv3 nodes found\n")); + DEBUG ((DEBUG_ERROR, "%a: No SMMUv3 nodes found\n", __func__)); *SmmuCount = 0; *SmmuBaseAddresses = NULL; return EFI_NOT_FOUND; @@ -93,7 +93,7 @@ ParseIortAcpiTableSmmu ( // Allocate array for SMMU base addresses LocalSmmuBaseAddresses = AllocateZeroPool (LocalSmmuCount * sizeof (UINT64)); if (LocalSmmuBaseAddresses == NULL) { - DEBUG ((DEBUG_ERROR, "ParseIortAcpiTableSmmu: Failed to allocate memory for SMMU base addresses\n")); + DEBUG ((DEBUG_ERROR, "%a: Failed to allocate memory for SMMU base addresses\n", __func__)); *SmmuCount = 0; *SmmuBaseAddresses = NULL; return EFI_OUT_OF_RESOURCES; @@ -107,7 +107,7 @@ ParseIortAcpiTableSmmu ( if (Node->Type == EFI_ACPI_IORT_TYPE_SMMUv3) { SmmuNode = (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE *)Node; LocalSmmuBaseAddresses[SmmuIndex] = SmmuNode->Base; - DEBUG ((DEBUG_INFO, "ParseIortAcpiTableSmmu: Found SMMUv3 at base 0x%lX\n", SmmuNode->Base)); + DEBUG ((DEBUG_INFO, "%a: Found SMMUv3 at base 0x%lX\n", __func__, SmmuNode->Base)); SmmuIndex++; } Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Node + Node->Length); @@ -148,7 +148,7 @@ GetIortAcpiTableRmrList ( UINT32 MemRangeIndex; if (IortTable == NULL) { - DEBUG ((DEBUG_ERROR, "GetIortAcpiTableRmrList: IORT table not available\n")); + DEBUG ((DEBUG_ERROR, "%a: IORT table not available\n", __func__)); return NULL; } @@ -162,8 +162,8 @@ GetIortAcpiTableRmrList ( for (Count = 0; Count < Iort->NumNodes; Count++) { if (Node->Type == EFI_ACPI_IORT_TYPE_RMR) { RmrNode = (EFI_ACPI_6_0_IO_REMAPPING_RMR_NODE *)Node; - DEBUG ((DEBUG_INFO, "GetIortAcpiTableRmrList: Found RMR node with %d memory range descriptors\n", - RmrNode->NumMemRangeDesc)); + DEBUG ((DEBUG_INFO, "%a: Found RMR node with %d memory range descriptors\n", + __func__, RmrNode->NumMemRangeDesc)); // Get pointer to memory range descriptor array // MemRangeDescRef is offset from the start of the RMR node @@ -175,7 +175,7 @@ GetIortAcpiTableRmrList ( if ((MemRangeDesc[MemRangeIndex].Base > 0) && (MemRangeDesc[MemRangeIndex].Length > 0)) { NewNode = AllocateZeroPool (sizeof (RMRListNode)); if (NewNode == NULL) { - DEBUG ((DEBUG_ERROR, "GetIortAcpiTableRmrList: Failed to allocate RMRListNode\n")); + DEBUG ((DEBUG_ERROR, "%a: Failed to allocate RMRListNode\n", __func__)); // Return what we have so far return Head; } @@ -184,8 +184,8 @@ GetIortAcpiTableRmrList ( NewNode->Length = MemRangeDesc[MemRangeIndex].Length; NewNode->Next = NULL; - DEBUG ((DEBUG_INFO, "GetIortAcpiTableRmrList: Adding RMR range Base=0x%lX, Length=0x%lX\n", - NewNode->BaseAddress, NewNode->Length)); + DEBUG ((DEBUG_INFO, "%a: Adding RMR range Base=0x%lX, Length=0x%lX\n", + __func__, NewNode->BaseAddress, NewNode->Length)); // Add to linked list if (Head == NULL) { From 007099b9ae2619a2ef6090e7df99a6b9b8a6013c Mon Sep 17 00:00:00 2001 From: eeshanl Date: Fri, 9 Jan 2026 01:16:52 +0000 Subject: [PATCH 3/4] mend --- .../UEFI/DMAProtectionUnitTestApp.c | 7 +- .../UEFI/DMASmmuProtectionUnitTestApp.inf | 118 ++-- .../UEFI/SMMU/DMAProtectionTestArch.c | 651 +++++++++--------- .../UEFI/SMMU/DmaProtection.h | 206 +++--- .../UEFI/SMMU/IortAcpiTable.c | 410 ++++++----- UefiTestingPkg/UefiTestingPkg.dsc | 1 + 6 files changed, 716 insertions(+), 677 deletions(-) diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMAProtectionUnitTestApp.c b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMAProtectionUnitTestApp.c index baeaf0bba5..a08848963f 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMAProtectionUnitTestApp.c +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMAProtectionUnitTestApp.c @@ -317,8 +317,10 @@ CheckBMETeardown ( for (i = 0; i < (PreVarSize)/(sizeof (BOOLEAN)); i++) { // BME Enabled before exit boot services UT_LOG_INFO (PreBuffer[i] ? "Pre-EBS BME %d: True\n" : "Pre-EBS BME %d: False\n", i); + DEBUG ((DEBUG_INFO, PreBuffer[i] ? "%a: Pre-EBS BME %d: True\n" : "%a: Pre-EBS BME %d: False\n", __func__, i)); // BME Disabled after exit boot services UT_LOG_INFO (PostBuffer[i] ? "Post-EBS BME %d: True\n" : "Post-EBS BME %d: False\n", i); + DEBUG ((DEBUG_INFO, PostBuffer[i] ? "%a: Post-EBS BME %d: True\n" : "%a: Post-EBS BME %d: False\n", __func__, i)); UT_ASSERT_FALSE (PostBuffer[i]); } @@ -348,6 +350,9 @@ CheckBMETeardown ( ); } + UT_LOG_INFO ("PASSED: BME test.\n"); + DEBUG ((DEBUG_INFO, "PASSED: BME test.\n")); + return UNIT_TEST_PASSED; } // CheckBMETeardown() @@ -418,8 +423,8 @@ DMAProtectionUnitTestApp ( } AddTestCase (IommuTests, "All Hardware Definition Units Have IOMMU Enabled", "IOMMU.StatusRegister", CheckIOMMUEnabled, NULL, NULL, NULL); - AddTestCase (IommuTests, "BME Teardown at ExitBootServices", "IOMMU.BMETeardown", CheckBMETeardown, NULL, NULL, BMEContext); AddTestCase (IommuTests, "Verify excluded ranges are marked reserved", "IOMMU.ExcludedRangeTest", CheckExcludedRegions, NULL, NULL, NULL); + AddTestCase (IommuTests, "BME Teardown at ExitBootServices", "IOMMU.BMETeardown", CheckBMETeardown, NULL, NULL, BMEContext); // // Execute the tests. diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf index 2c7defe287..631af0a92d 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf @@ -1,59 +1,59 @@ -## @file DMASmmuProtectionUnitTestApp.inf -## -# DMA Protection Unit Test App for ARM SMMUv3 platforms. -# Tests SMMU translation enable status and RMR reserved memory regions. -# -# Copyright (c) Microsoft Corporation. All rights reserved. -# SPDX-License-Identifier: BSD-2-Clause-Patent -# -## - - -[Defines] - INF_VERSION = 0x00010006 - BASE_NAME = DMASmmuProtectionUnitTestApp - FILE_GUID = 7A8C2E45-B9F3-4D12-A6E8-9C1B5F3D2E7A - MODULE_TYPE = UEFI_APPLICATION - VERSION_STRING = 1.0 - ENTRY_POINT = DMAProtectionUnitTestApp - -# -# The following information is for reference only and not required by the build tools. -# -# VALID_ARCHITECTURES = AARCH64 -# - -[Sources] - Acpi.c - Acpi.h - DMAProtectionTest.h - DMAProtectionUnitTestApp.c - SMMU/DmaProtection.h - SMMU/DMAProtectionTestArch.c - SMMU/IortAcpiTable.c - -[Packages] - MdePkg/MdePkg.dec - MdeModulePkg/MdeModulePkg.dec - UefiTestingPkg/UefiTestingPkg.dec - UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec - ShellPkg/ShellPkg.dec - -[Protocols] - gEfiPciIoProtocolGuid ## CONSUMES - -[LibraryClasses] - UefiApplicationEntryPoint - DebugLib - UnitTestLib - UnitTestBootLib - UnitTestPersistenceLib - IoLib - MemoryAllocationLib - ShellLib - PciLib - -[Guids] - gDMAUnitTestVariableGuid - gEfiAcpi20TableGuid - gEfiAcpi10TableGuid +## @file DMASmmuProtectionUnitTestApp.inf +## +# DMA Protection Unit Test App for ARM SMMUv3 platforms. +# Tests SMMU translation enable status and RMR reserved memory regions. +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = DMASmmuProtectionUnitTestApp + FILE_GUID = 7A8C2E45-B9F3-4D12-A6E8-9C1B5F3D2E7A + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = DMAProtectionUnitTestApp + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = AARCH64 +# + +[Sources] + Acpi.c + Acpi.h + DMAProtectionTest.h + DMAProtectionUnitTestApp.c + SMMU/DmaProtection.h + SMMU/DMAProtectionTestArch.c + SMMU/IortAcpiTable.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiTestingPkg/UefiTestingPkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + ShellPkg/ShellPkg.dec + +[Protocols] + gEfiPciIoProtocolGuid ## CONSUMES + +[LibraryClasses] + UefiApplicationEntryPoint + DebugLib + UnitTestLib + UnitTestBootLib + UnitTestPersistenceLib + IoLib + MemoryAllocationLib + ShellLib + PciLib + +[Guids] + gDMAUnitTestVariableGuid + gEfiAcpi20TableGuid + gEfiAcpi10TableGuid diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c index 812e171088..d92138ae23 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c @@ -1,307 +1,344 @@ -/** @file -- DMAProtectionTestArch.c - -This file contains architecture specific DMA protection tests for ARM SMMU (SMMUv3): -1) Check the CR0 registers of the SMMUv3 nodes to verify SMMU translation is enabled -2) Check that Command Queue is enabled (CMDQEN bit in CR0) -3) Check that Event Queue is enabled (EVTQEN bit in CR0) -4) Check that Stream Table Base is configured (STRTAB_BASE is not NULL) -5) Check that GERROR register is 0 (no global errors) -6) Check RMR (Reserved Memory Range) regions from IORT are set as reserved in memory map - -Copyright (c) Microsoft Corporation. All rights reserved. -SPDX-License-Identifier: BSD-2-Clause-Patent - -**/ - -#include -#include -#include -#include -#include -#include - -#include "DmaProtection.h" - -/// ================================================================================================ -/// ================================================================================================ -/// -/// TEST CASES -/// -/// ================================================================================================ -/// ================================================================================================ - -/** - Test to verify that the Reserved Memory Range (RMR) regions defined in the IORT - are properly marked as reserved in the EFI memory map. - - @param[in] Context The unit test context (not used). - - @retval UNIT_TEST_PASSED All RMR regions are properly marked as reserved. - @retval UNIT_TEST_ERROR_TEST_FAILED A RMR region was not found or not marked as reserved. -**/ -UNIT_TEST_STATUS -EFIAPI -CheckExcludedRegions ( - IN UNIT_TEST_CONTEXT Context - ) -{ - EFI_STATUS Status; - EFI_MEMORY_DESCRIPTOR *EfiMemoryMap; - EFI_MEMORY_DESCRIPTOR *EfiMemoryMapEnd; - EFI_MEMORY_DESCRIPTOR *EfiMemNext; - UINTN EfiMemoryMapSize; - UINTN EfiMapKey; - UINTN EfiDescriptorSize; - UINT32 EfiDescriptorVersion; - EFI_ACPI_DESCRIPTION_HEADER *IortTable; - RMRListNode *Head; - RMRListNode *Current; - BOOLEAN Found; - - // - // Step 1: Get IORT Table - // - IortTable = NULL; - Status = GetIortAcpiTable (&IortTable); - UT_ASSERT_NOT_EFI_ERROR (Status); - - // - // Step 2: Get the RMR (Reserved Memory Range) nodes from IORT Table - // - Head = GetIortAcpiTableRmrList (IortTable); - if (Head == NULL) { - UT_LOG_INFO ("No RMRs Found in IORT\n"); - DEBUG ((DEBUG_INFO, "%a: No RMRs Found in IORT\n", __func__)); - return UNIT_TEST_PASSED; - } - - Current = Head; - - // - // Step 3: Get the EFI memory map. - // - EfiMemoryMapSize = 0; - EfiMemoryMap = NULL; - Status = gBS->GetMemoryMap ( - &EfiMemoryMapSize, - EfiMemoryMap, - &EfiMapKey, - &EfiDescriptorSize, - &EfiDescriptorVersion - ); - if (Status == EFI_BUFFER_TOO_SMALL) { - EfiMemoryMap = (EFI_MEMORY_DESCRIPTOR *)AllocateZeroPool (EfiMemoryMapSize + 8*EfiDescriptorSize); - UT_ASSERT_NOT_NULL (EfiMemoryMap); - Status = gBS->GetMemoryMap ( - &EfiMemoryMapSize, - EfiMemoryMap, - &EfiMapKey, - &EfiDescriptorSize, - &EfiDescriptorVersion - ); - UT_ASSERT_NOT_EFI_ERROR (Status); - } else { - UT_LOG_ERROR ("GetMemoryMap Failed\n"); - DEBUG ((DEBUG_ERROR, "%a: GetMemoryMap Failed\n", __func__)); - return UNIT_TEST_ERROR_TEST_FAILED; - } - - // - // Step 4: Step through memory map and verify each - // RMR memory range is marked reserved - // - EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)EfiMemoryMap + EfiMemoryMapSize); - - while (Current != NULL) { - Found = FALSE; - EfiMemNext = EfiMemoryMap; - - UT_LOG_INFO ("Checking RMR region: Base=0x%lX, Length=0x%lX\n", Current->BaseAddress, Current->Length); - DEBUG ((DEBUG_INFO, "%a: Checking RMR region: Base=0x%lX, Length=0x%lX\n", __func__, Current->BaseAddress, Current->Length)); - - while (EfiMemNext < EfiMemoryMapEnd) { - // Check if memory range fully encompasses RMR - if ((EfiMemNext->PhysicalStart <= Current->BaseAddress) && - ((EfiMemNext->PhysicalStart + (EFI_PAGE_SIZE * EfiMemNext->NumberOfPages)) >= (Current->BaseAddress + Current->Length))) - { - // Print memory type for debugging - DEBUG ((DEBUG_INFO, "%a: Found encompassing memory range: Base=0x%lX, Length=0x%lX, Type=%d\n", - __func__, - EfiMemNext->PhysicalStart, - EFI_PAGE_SIZE * EfiMemNext->NumberOfPages, - EfiMemNext->Type)); - - // Verify memory range is marked as reserved (accept both EfiReservedMemoryType and EfiACPIMemoryNVS) - if ((EfiMemNext->Type == EfiReservedMemoryType) || (EfiMemNext->Type == EfiACPIMemoryNVS)) { - UT_LOG_INFO ("RMR between 0x%lX and 0x%lX found with reserved memory type %d\n", - Current->BaseAddress, Current->BaseAddress + Current->Length, EfiMemNext->Type); - DEBUG ((DEBUG_INFO, "%a: RMR between 0x%lX and 0x%lX found with reserved memory type %d\n", - __func__, Current->BaseAddress, Current->BaseAddress + Current->Length, EfiMemNext->Type)); - Found = TRUE; - break; - } - } - - // Move on to next descriptor - EfiMemNext = NEXT_MEMORY_DESCRIPTOR (EfiMemNext, EfiDescriptorSize); - } - - if (!Found) { - UT_LOG_ERROR ("RMR between 0x%lX and 0x%lX NOT found with reserved memory type!\n", - Current->BaseAddress, Current->BaseAddress + Current->Length); - DEBUG ((DEBUG_ERROR, "%a: RMR between 0x%lX and 0x%lX NOT found with reserved memory type!\n", - __func__, Current->BaseAddress, Current->BaseAddress + Current->Length)); - FreePool (EfiMemoryMap); - return UNIT_TEST_ERROR_TEST_FAILED; - } - - Current = Current->Next; - } - - FreePool (EfiMemoryMap); - - UT_LOG_INFO (" CheckExcludedRegions PASSED\n"); - DEBUG ((DEBUG_INFO, "%a: PASSED\n", __func__)); - - return UNIT_TEST_PASSED; -} // CheckExcludedRegions() - -/** - Test to verify that all SMMUv3 units found in the IORT have translation enabled. - This checks: - 1) CR0 register's SMMUEN bit to confirm the SMMU is actively translating - 2) CR0 register's CMDQEN bit to confirm the command queue is enabled - 3) CR0 register's EVTQEN bit to confirm the event queue is enabled - 4) STRTAB_BASE register is not NULL (stream table must be configured) - 5) GERROR register is 0 (no global errors) - - @param[in] Context The unit test context (not used). - - @retval UNIT_TEST_PASSED All SMMU units are properly configured. - @retval UNIT_TEST_ERROR_TEST_FAILED An SMMU unit is not properly configured. -**/ -UNIT_TEST_STATUS -EFIAPI -CheckIOMMUEnabled ( - IN UNIT_TEST_CONTEXT Context - ) -{ - EFI_STATUS Status; - EFI_ACPI_DESCRIPTION_HEADER *IortTable; - UINTN SmmuCount; - UINT64 *SmmuBaseAddresses; - UINTN Iterator; - UINT32 Cr0Value; - UINT32 SmmEnBit; - UINT32 CmdQEnBit; - UINT32 EvtQEnBit; - UINT64 StrTabBase; - UINT64 StrTabBaseAddr; - UINT32 GError; - - // - // Step 1: Get IORT Table - // - IortTable = NULL; - Status = GetIortAcpiTable (&IortTable); - UT_ASSERT_NOT_EFI_ERROR (Status); - - // - // Step 2: Find and parse SMMUv3 nodes from IORT - // - SmmuCount = 0; - SmmuBaseAddresses = NULL; - Status = ParseIortAcpiTableSmmu (IortTable, &SmmuCount, &SmmuBaseAddresses); - UT_ASSERT_NOT_EFI_ERROR (Status); - - // - // Step 3: Check that we found at least one SMMU - // - UT_ASSERT_TRUE (SmmuCount > 0); - UT_LOG_INFO ("Found %d SMMUv3 units in IORT\n", SmmuCount); - DEBUG ((DEBUG_INFO, "%a: Found %d SMMUv3 units in IORT\n", __func__, SmmuCount)); - - // - // Step 4: For each SMMU, check: - // - SMMU Enable bit (SMMUEN) in CR0 register - // - Command Queue Enable bit (CMDQEN) in CR0 register - // - Event Queue Enable bit (EVTQEN) in CR0 register - // - Stream Table Base address is not NULL - // - GERROR register is 0 - // - for (Iterator = 0; Iterator < SmmuCount; Iterator++) { - UT_LOG_INFO ("Checking SMMUv3 at base address 0x%lX\n", SmmuBaseAddresses[Iterator]); - DEBUG ((DEBUG_INFO, "%a: Checking SMMUv3 at base address 0x%lX\n", __func__, SmmuBaseAddresses[Iterator])); - - if (SmmuBaseAddresses[Iterator] == 0x13000000) { - UT_LOG_INFO ("Skipping known disabled SMMUv3 at base address 0x%lX\n", SmmuBaseAddresses[Iterator]); - DEBUG ((DEBUG_INFO, "%a: Skipping known disabled SMMUv3 at base address 0x%lX\n", __func__, SmmuBaseAddresses[Iterator])); - continue; - } - - // - // Read CR0 register - // - Cr0Value = MmioRead32 ((UINTN)(SmmuBaseAddresses[Iterator] + SMMU_CR0)); - UT_LOG_INFO (" CR0 Register Value: 0x%X\n", Cr0Value); - DEBUG ((DEBUG_INFO, "%a: CR0 Register Value: 0x%X\n", __func__, Cr0Value)); - - // - // Check SMMUEN bit (bit 0) - // - SmmEnBit = Cr0Value & SMMU_CR0_SMMUEN; - UT_LOG_INFO (" SMMUEN bit: %d\n", SmmEnBit); - DEBUG ((DEBUG_INFO, "%a: SMMUEN bit: %d\n", __func__, SmmEnBit)); - UT_ASSERT_NOT_EQUAL (SmmEnBit, 0); - - // - // Check CMDQEN bit (bit 1) - Command Queue must be enabled - // - CmdQEnBit = Cr0Value & SMMU_CR0_CMDQEN; - UT_LOG_INFO (" CMDQEN bit: %d\n", CmdQEnBit ? 1 : 0); - DEBUG ((DEBUG_INFO, "%a: CMDQEN bit: %d\n", __func__, CmdQEnBit ? 1 : 0)); - UT_ASSERT_NOT_EQUAL (CmdQEnBit, 0); - - // - // Check EVTQEN bit (bit 2) - Event Queue must be enabled - // - EvtQEnBit = Cr0Value & SMMU_CR0_EVTQEN; - UT_LOG_INFO (" EVTQEN bit: %d\n", EvtQEnBit ? 1 : 0); - DEBUG ((DEBUG_INFO, "%a: EVTQEN bit: %d\n", __func__, EvtQEnBit ? 1 : 0)); - UT_ASSERT_NOT_EQUAL (EvtQEnBit, 0); - - // - // Read STRTAB_BASE register and check it's not NULL - // If NULL, no stream IDs can undergo translation - // - StrTabBase = MmioRead64 ((UINTN)(SmmuBaseAddresses[Iterator] + SMMU_STRTAB_BASE)); - UT_LOG_INFO (" STRTAB_BASE Register Value: 0x%lX\n", StrTabBase); - DEBUG ((DEBUG_INFO, "%a: STRTAB_BASE Register Value: 0x%lX\n", __func__, StrTabBase)); - - // Extract the address portion by masking out lower 6 bits (bits [5:0] are reserved/config) - StrTabBaseAddr = StrTabBase & ~SMMU_STRTAB_BASE_ADDR_MASK; - UT_LOG_INFO (" STRTAB_BASE Address: 0x%lX\n", StrTabBaseAddr); - DEBUG ((DEBUG_INFO, "%a: STRTAB_BASE Address: 0x%lX\n", __func__, StrTabBaseAddr)); - - // Assert that Stream Table Base Address is not NULL - UT_ASSERT_NOT_EQUAL (StrTabBaseAddr, 0); - - // - // Read GERROR register and check it's 0 (no global errors) - // - GError = MmioRead32 ((UINTN)(SmmuBaseAddresses[Iterator] + SMMU_GERROR)); - UT_LOG_INFO (" GERROR Register Value: 0x%X\n", GError); - DEBUG ((DEBUG_INFO, "%a: GERROR Register Value: 0x%X\n", __func__, GError)); - UT_ASSERT_EQUAL (GError, 0); - } - - // Free the allocated memory - if (SmmuBaseAddresses != NULL) { - FreePool (SmmuBaseAddresses); - } - - UT_LOG_INFO (" CheckIOMMUEnabled PASSED\n"); - DEBUG ((DEBUG_INFO, "%a: PASSED\n", __func__)); - - return UNIT_TEST_PASSED; -} // CheckIOMMUEnabled() +/** @file -- DMAProtectionTestArch.c + +This file contains architecture specific DMA protection tests for ARM SMMU (SMMUv3): +1) Check the CR0 registers of the SMMUv3 nodes to verify SMMU translation is enabled +2) Check that Command Queue is enabled (CMDQEN bit in CR0) +3) Check that Event Queue is enabled (EVTQEN bit in CR0) +4) Check that Stream Table Base is configured (STRTAB_BASE is not NULL) +5) Check that GERROR register is 0 (no global errors) +6) Check RMR (Reserved Memory Range) regions from IORT are set as reserved in memory map + +Copyright (c) Microsoft Corporation. All rights reserved. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include + +#include "DmaProtection.h" + +/// ================================================================================================ +/// ================================================================================================ +/// +/// TEST CASES +/// +/// ================================================================================================ +/// ================================================================================================ + +/** + Test to verify that the Reserved Memory Range (RMR) regions defined in the IORT + are properly marked as reserved in the EFI memory map. + + @param[in] Context The unit test context (not used). + + @retval UNIT_TEST_PASSED All RMR regions are properly marked as reserved. + @retval UNIT_TEST_ERROR_TEST_FAILED A RMR region was not found or not marked as reserved. +**/ +UNIT_TEST_STATUS +EFIAPI +CheckExcludedRegions ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + EFI_MEMORY_DESCRIPTOR *EfiMemoryMap; + EFI_MEMORY_DESCRIPTOR *EfiMemoryMapEnd; + EFI_MEMORY_DESCRIPTOR *EfiMemNext; + UINTN EfiMemoryMapSize; + UINTN EfiMapKey; + UINTN EfiDescriptorSize; + UINT32 EfiDescriptorVersion; + EFI_ACPI_DESCRIPTION_HEADER *IortTable; + RMRListNode *Head; + RMRListNode *Current; + BOOLEAN Found; + UNIT_TEST_STATUS TestStatus; + + // + // Step 1: Get IORT Table + // + IortTable = NULL; + Status = GetIortAcpiTable (&IortTable); + UT_ASSERT_NOT_EFI_ERROR (Status); + + // + // Step 2: Get the RMR (Reserved Memory Range) nodes from IORT Table + // + Head = GetIortAcpiTableRmrList (IortTable); + if (Head == NULL) { + UT_LOG_INFO ("No RMRs Found in IORT\n"); + DEBUG ((DEBUG_INFO, "%a: No RMRs Found in IORT\n", __func__)); + return UNIT_TEST_PASSED; + } + + Current = Head; + + // + // Step 3: Get the EFI memory map. + // + EfiMemoryMapSize = 0; + EfiMemoryMap = NULL; + Status = gBS->GetMemoryMap ( + &EfiMemoryMapSize, + EfiMemoryMap, + &EfiMapKey, + &EfiDescriptorSize, + &EfiDescriptorVersion + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + EfiMemoryMap = (EFI_MEMORY_DESCRIPTOR *)AllocateZeroPool (EfiMemoryMapSize + 8*EfiDescriptorSize); + UT_ASSERT_NOT_NULL (EfiMemoryMap); + + Status = gBS->GetMemoryMap ( + &EfiMemoryMapSize, + EfiMemoryMap, + &EfiMapKey, + &EfiDescriptorSize, + &EfiDescriptorVersion + ); + UT_ASSERT_NOT_EFI_ERROR (Status); + } else { + UT_LOG_ERROR ("GetMemoryMap Failed\n"); + DEBUG ((DEBUG_ERROR, "%a: GetMemoryMap Failed\n", __func__)); + return UNIT_TEST_ERROR_TEST_FAILED; + } + + // + // Step 4: Step through memory map and verify each + // RMR memory range is marked reserved + // + EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)EfiMemoryMap + EfiMemoryMapSize); + TestStatus = UNIT_TEST_PASSED; + + while (Current != NULL) { + Found = FALSE; + EfiMemNext = EfiMemoryMap; + + UT_LOG_INFO ("Checking RMR region: Base=0x%lX, Length=0x%lX\n", Current->BaseAddress, Current->Length); + DEBUG ((DEBUG_INFO, "%a: Checking RMR region: Base=0x%lX, Length=0x%lX\n", __func__, Current->BaseAddress, Current->Length)); + + while (EfiMemNext < EfiMemoryMapEnd) { + // Check if memory range fully encompasses RMR + if ((EfiMemNext->PhysicalStart <= Current->BaseAddress) && + ((EfiMemNext->PhysicalStart + (EFI_PAGE_SIZE * EfiMemNext->NumberOfPages)) >= (Current->BaseAddress + Current->Length))) + { + UT_LOG_INFO ( + "Found encompassing memory range: Base=0x%lX, Length=0x%lX, Type=%d\n", + EfiMemNext->PhysicalStart, + EFI_PAGE_SIZE * EfiMemNext->NumberOfPages, + EfiMemNext->Type + ); + DEBUG (( + DEBUG_INFO, + "%a: Found encompassing memory range: Base=0x%lX, Length=0x%lX, Type=%d\n", + __func__, + EfiMemNext->PhysicalStart, + EFI_PAGE_SIZE * EfiMemNext->NumberOfPages, + EfiMemNext->Type + )); + + // Verify memory range is marked as reserved + if (EfiMemNext->Type == EfiReservedMemoryType) { + UT_LOG_INFO ( + "RMR between 0x%lX and 0x%lX found with reserved memory type %d\n", + Current->BaseAddress, + Current->BaseAddress + Current->Length, + EfiMemNext->Type + ); + DEBUG (( + DEBUG_INFO, + "%a: RMR between 0x%lX and 0x%lX found with reserved memory type %d\n", + __func__, + Current->BaseAddress, + Current->BaseAddress + Current->Length, + EfiMemNext->Type + )); + Found = TRUE; + } + + break; + } + + // Move on to next descriptor + EfiMemNext = NEXT_MEMORY_DESCRIPTOR (EfiMemNext, EfiDescriptorSize); + } + + if (!Found) { + UT_LOG_ERROR ( + "RMR between 0x%lX and 0x%lX NOT found with reserved memory type!\n", + Current->BaseAddress, + Current->BaseAddress + Current->Length + ); + DEBUG (( + DEBUG_ERROR, + "%a: RMR between 0x%lX and 0x%lX NOT found with reserved memory type!\n", + __func__, + Current->BaseAddress, + Current->BaseAddress + Current->Length + )); + TestStatus = UNIT_TEST_ERROR_TEST_FAILED; + } + + Current = Current->Next; + } + + UT_LOG_INFO ("%a: Result=%d\n", __func__, TestStatus); + DEBUG ((DEBUG_INFO, "%a: Result=%d\n", __func__, TestStatus)); + + return TestStatus; +} // CheckExcludedRegions() + +/** + Test to verify that all SMMUv3 units found in the IORT have translation enabled. + This checks: + 1) CR0 register's SMMUEN bit to confirm the SMMU is actively translating + 2) CR0 register's CMDQEN bit to confirm the command queue is enabled + 3) CR0 register's EVTQEN bit to confirm the event queue is enabled + 4) STRTAB_BASE register is not NULL (stream table must be configured) + 5) GERROR register is 0 (no global errors) + + @param[in] Context The unit test context (not used). + + @retval UNIT_TEST_PASSED All SMMU units are properly configured. + @retval UNIT_TEST_ERROR_TEST_FAILED An SMMU unit is not properly configured. +**/ +UNIT_TEST_STATUS +EFIAPI +CheckIOMMUEnabled ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + EFI_ACPI_DESCRIPTION_HEADER *IortTable; + UINTN SmmuCount; + UINT64 *SmmuBaseAddresses; + UINTN Iterator; + UINT32 Cr0Value; + UINT32 SmmuEnBit; + UINT32 CmdQEnBit; + UINT32 EvtQEnBit; + UINT64 StrTabBase; + UINT64 StrTabBaseAddr; + UINT32 GError; + UNIT_TEST_STATUS TestStatus; + + // + // Step 1: Get IORT Table + // + IortTable = NULL; + Status = GetIortAcpiTable (&IortTable); + UT_ASSERT_NOT_EFI_ERROR (Status); + + // + // Step 2: Find and parse SMMUv3 nodes from IORT + // + SmmuCount = 0; + SmmuBaseAddresses = NULL; + Status = ParseIortAcpiTableSmmu (IortTable, &SmmuCount, &SmmuBaseAddresses); + UT_ASSERT_NOT_EFI_ERROR (Status); + + // + // Step 3: Check that we found at least one SMMU + // + UT_ASSERT_TRUE (SmmuCount > 0); + UT_LOG_INFO ("Found %d SMMUv3 units in IORT\n", SmmuCount); + DEBUG ((DEBUG_INFO, "%a: Found %d SMMUv3 units in IORT\n", __func__, SmmuCount)); + + TestStatus = UNIT_TEST_PASSED; + + // + // Step 4: For each SMMU, check: + // - SMMU Enable bit (SMMUEN) in CR0 register + // - Command Queue Enable bit (CMDQEN) in CR0 register + // - Event Queue Enable bit (EVTQEN) in CR0 register + // - Stream Table Base address is not NULL + // - GERROR register is 0 + // + for (Iterator = 0; Iterator < SmmuCount; Iterator++) { + UT_LOG_INFO ("Checking SMMUv3 at base address 0x%lX\n", SmmuBaseAddresses[Iterator]); + DEBUG ((DEBUG_INFO, "%a: Checking SMMUv3 at base address 0x%lX\n", __func__, SmmuBaseAddresses[Iterator])); + + // + // Read CR0 register + // + Cr0Value = MmioRead32 ((UINTN)(SmmuBaseAddresses[Iterator] + SMMU_CR0)); + UT_LOG_INFO ("CR0 Register Value: 0x%X\n", Cr0Value); + DEBUG ((DEBUG_INFO, "%a: CR0 Register Value: 0x%X\n", __func__, Cr0Value)); + + // + // Check SMMUEN bit (bit 0) + // + SmmuEnBit = Cr0Value & SMMU_CR0_SMMUEN; + UT_LOG_INFO ("SMMUEN bit: %d\n", SmmuEnBit); + DEBUG ((DEBUG_INFO, "%a: SMMUEN bit: %d\n", __func__, SmmuEnBit)); + if (SmmuEnBit == 0) { + UT_LOG_ERROR ("SMMUEN bit is disabled for SMMUv3 at base address 0x%lX\n", SmmuBaseAddresses[Iterator]); + DEBUG ((DEBUG_ERROR, "%a: SMMUEN bit is disabled for SMMUv3 at base address 0x%lX\n", __func__, SmmuBaseAddresses[Iterator])); + TestStatus = UNIT_TEST_ERROR_TEST_FAILED; + } + + // + // Check CMDQEN bit (bit 3) - Command Queue must be enabled + // + CmdQEnBit = Cr0Value & SMMU_CR0_CMDQEN; + UT_LOG_INFO ("CMDQEN bit: %d\n", CmdQEnBit ? 1 : 0); + DEBUG ((DEBUG_INFO, "%a: CMDQEN bit: %d\n", __func__, CmdQEnBit ? 1 : 0)); + if (CmdQEnBit == 0) { + UT_LOG_ERROR ("CMDQEN bit is disabled for SMMUv3 at base address 0x%lX\n", SmmuBaseAddresses[Iterator]); + DEBUG ((DEBUG_ERROR, "%a: CMDQEN bit is disabled for SMMUv3 at base address 0x%lX\n", __func__, SmmuBaseAddresses[Iterator])); + TestStatus = UNIT_TEST_ERROR_TEST_FAILED; + } + + // + // Check EVTQEN bit (bit 2) - Event Queue must be enabled + // + EvtQEnBit = Cr0Value & SMMU_CR0_EVTQEN; + UT_LOG_INFO ("EVTQEN bit: %d\n", EvtQEnBit ? 1 : 0); + DEBUG ((DEBUG_INFO, "%a: EVTQEN bit: %d\n", __func__, EvtQEnBit ? 1 : 0)); + if (EvtQEnBit == 0) { + UT_LOG_ERROR ("EVTQEN bit is disabled for SMMUv3 at base address 0x%lX\n", SmmuBaseAddresses[Iterator]); + DEBUG ((DEBUG_ERROR, "%a: EVTQEN bit is disabled for SMMUv3 at base address 0x%lX\n", __func__, SmmuBaseAddresses[Iterator])); + TestStatus = UNIT_TEST_ERROR_TEST_FAILED; + } + + // + // Read STRTAB_BASE register and check it's not NULL + // If NULL, no stream IDs can undergo translation + // + StrTabBase = MmioRead64 ((UINTN)(SmmuBaseAddresses[Iterator] + SMMU_STRTAB_BASE)); + UT_LOG_INFO ("STRTAB_BASE Register Value: 0x%lX\n", StrTabBase); + DEBUG ((DEBUG_INFO, "%a: STRTAB_BASE Register Value: 0x%lX\n", __func__, StrTabBase)); + + // Extract the address portion by masking out lower 6 bits (bits [5:0] are reserved/config) + StrTabBaseAddr = StrTabBase & ~SMMU_STRTAB_BASE_ADDR_MASK; + UT_LOG_INFO ("STRTAB_BASE Address: 0x%lX\n", StrTabBaseAddr); + DEBUG ((DEBUG_INFO, "%a: STRTAB_BASE Address: 0x%lX\n", __func__, StrTabBaseAddr)); + if (StrTabBaseAddr == 0) { + UT_LOG_ERROR ("STRTAB_BASE is NULL for SMMUv3 at base address 0x%lX\n", SmmuBaseAddresses[Iterator]); + DEBUG ((DEBUG_ERROR, "%a: STRTAB_BASE is NULL for SMMUv3 at base address 0x%lX\n", __func__, SmmuBaseAddresses[Iterator])); + TestStatus = UNIT_TEST_ERROR_TEST_FAILED; + } + + // + // Read GERROR register and check it's 0 (no global errors) + // + GError = MmioRead32 ((UINTN)(SmmuBaseAddresses[Iterator] + SMMU_GERROR)); + UT_LOG_INFO ("GERROR Register Value: 0x%X\n", GError); + DEBUG ((DEBUG_INFO, "%a: GERROR Register Value: 0x%X\n", __func__, GError)); + if (GError != 0) { + UT_LOG_ERROR ("GERROR register is non-zero for SMMUv3 at base address 0x%lX\n", SmmuBaseAddresses[Iterator]); + DEBUG ((DEBUG_ERROR, "%a: GERROR register is non-zero for SMMUv3 at base address 0x%lX\n", __func__, SmmuBaseAddresses[Iterator])); + TestStatus = UNIT_TEST_ERROR_TEST_FAILED; + } + } + + UT_LOG_INFO ("%a: Result=%d\n", __func__, TestStatus); + DEBUG ((DEBUG_INFO, "%a: Result=%d\n", __func__, TestStatus)); + + return TestStatus; +} // CheckIOMMUEnabled() diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h index 4d9644d9ac..5dd19e74ac 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h @@ -1,103 +1,103 @@ -/** @file -- DmaProtection.h - -Header file for SMMU DMA protection tests. - -Copyright (c) Microsoft Corporation. All rights reserved. -SPDX-License-Identifier: BSD-2-Clause-Patent - -**/ - -#ifndef _DMA_PROTECTION_H_ -#define _DMA_PROTECTION_H_ - -#include -#include - -// -// IORT Table Signature -// -#define EFI_ACPI_6_0_IO_REMAPPING_TABLE_SIGNATURE SIGNATURE_32('I', 'O', 'R', 'T') - -// -// SMMUv3 Register Offsets -// -#define SMMU_CR0 0x0020 -#define SMMU_CR0ACK 0x0024 -#define SMMU_GERROR 0x0060 -#define SMMU_STRTAB_BASE 0x0080 - -// -// SMMUv3 CR0 Register Bits -// -#define SMMU_CR0_SMMUEN BIT0 // SMMU Enable bit -#define SMMU_CR0_EVTQEN BIT2 // Event Queue Enable bit -#define SMMU_CR0_CMDQEN BIT3 // Command Queue Enable bit - -// -// STRTAB_BASE lower bits mask (bits [5:0] are reserved/config) -// -#define SMMU_STRTAB_BASE_ADDR_MASK 0x3FULL - -// -// RMR List Node Structure for tracking Reserved Memory Ranges -// -typedef struct _RMRListNode { - UINT64 BaseAddress; - UINT64 Length; - struct _RMRListNode *Next; -} RMRListNode; - -// -// Function Prototypes -// - -/** - Get the IORT ACPI table from the system. - - @param[out] IortTable Pointer to receive the IORT table pointer. - - @retval EFI_SUCCESS The IORT table was found. - @retval EFI_NOT_FOUND The IORT table was not found. - @retval EFI_INVALID_PARAMETER IortTable is NULL. -**/ -EFI_STATUS -EFIAPI -GetIortAcpiTable ( - OUT EFI_ACPI_DESCRIPTION_HEADER **IortTable - ); - -/** - Parse the IORT table to find all SMMUv3 nodes. - - @param[in] IortTable Pointer to the IORT table. - @param[out] SmmuCount Pointer to receive the count of SMMUv3 nodes. - @param[out] SmmuBaseAddresses Pointer to receive the array of SMMU base addresses. - Caller must free this memory when done. - - @retval EFI_SUCCESS SMMUv3 nodes were found and parsed. - @retval EFI_NOT_FOUND No SMMUv3 nodes found. - @retval EFI_INVALID_PARAMETER A required parameter is NULL. - @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. -**/ -EFI_STATUS -EFIAPI -ParseIortAcpiTableSmmu ( - IN EFI_ACPI_DESCRIPTION_HEADER *IortTable, - OUT UINTN *SmmuCount, - OUT UINT64 **SmmuBaseAddresses - ); - -/** - Parse the IORT table to find all RMR (Reserved Memory Range) nodes. - - @param[in] IortTable Pointer to the IORT table. - - @retval Pointer to head of linked list of RMR entries, or NULL if none found. -**/ -RMRListNode* -EFIAPI -GetIortAcpiTableRmrList ( - IN EFI_ACPI_DESCRIPTION_HEADER *IortTable - ); - -#endif // _DMA_PROTECTION_H_ +/** @file -- DmaProtection.h + +Header file for SMMU DMA protection tests. + +Copyright (c) Microsoft Corporation. All rights reserved. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _DMA_PROTECTION_H_ +#define _DMA_PROTECTION_H_ + +#include +#include + +// +// IORT Table Signature +// +#define EFI_ACPI_6_0_IO_REMAPPING_TABLE_SIGNATURE SIGNATURE_32('I', 'O', 'R', 'T') + +// +// SMMUv3 Register Offsets +// +#define SMMU_CR0 0x0020 +#define SMMU_CR0ACK 0x0024 +#define SMMU_GERROR 0x0060 +#define SMMU_STRTAB_BASE 0x0080 + +// +// SMMUv3 CR0 Register Bits +// +#define SMMU_CR0_SMMUEN BIT0 // SMMU Enable bit +#define SMMU_CR0_EVTQEN BIT2 // Event Queue Enable bit +#define SMMU_CR0_CMDQEN BIT3 // Command Queue Enable bit + +// +// STRTAB_BASE lower bits mask (bits [5:0] are reserved/config) +// +#define SMMU_STRTAB_BASE_ADDR_MASK 0x3FULL + +// +// RMR List Node Structure for tracking Reserved Memory Ranges +// +typedef struct _RMRListNode { + UINT64 BaseAddress; + UINT64 Length; + struct _RMRListNode *Next; +} RMRListNode; + +// +// Function Prototypes +// + +/** + Get the IORT ACPI table from the system. + + @param[out] IortTable Pointer to receive the IORT table pointer. + + @retval EFI_SUCCESS The IORT table was found. + @retval EFI_NOT_FOUND The IORT table was not found. + @retval EFI_INVALID_PARAMETER IortTable is NULL. +**/ +EFI_STATUS +EFIAPI +GetIortAcpiTable ( + OUT EFI_ACPI_DESCRIPTION_HEADER **IortTable + ); + +/** + Parse the IORT table to find all SMMUv3 nodes. + + @param[in] IortTable Pointer to the IORT table. + @param[out] SmmuCount Pointer to receive the count of SMMUv3 nodes. + @param[out] SmmuBaseAddresses Pointer to receive the array of SMMU base addresses. + Caller must free this memory when done. + + @retval EFI_SUCCESS SMMUv3 nodes were found and parsed. + @retval EFI_NOT_FOUND No SMMUv3 nodes found. + @retval EFI_INVALID_PARAMETER A required parameter is NULL. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +ParseIortAcpiTableSmmu ( + IN EFI_ACPI_DESCRIPTION_HEADER *IortTable, + OUT UINTN *SmmuCount, + OUT UINT64 **SmmuBaseAddresses + ); + +/** + Parse the IORT table to find all RMR (Reserved Memory Range) nodes. + + @param[in] IortTable Pointer to the IORT table. + + @retval Pointer to head of linked list of RMR entries, or NULL if none found. +**/ +RMRListNode * +EFIAPI +GetIortAcpiTableRmrList ( + IN EFI_ACPI_DESCRIPTION_HEADER *IortTable + ); + +#endif // _DMA_PROTECTION_H_ diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c index 837c5c6bde..6caa21a688 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c @@ -1,207 +1,203 @@ -/** @file -- IortAcpiTable.c - -This file contains functions to parse the IORT ACPI table for SMMUv3 nodes -and Reserved Memory Range (RMR) nodes. - -Copyright (c) Microsoft Corporation. All rights reserved. -SPDX-License-Identifier: BSD-2-Clause-Patent - -**/ - -#include -#include -#include - -#include "../Acpi.h" -#include "DmaProtection.h" - -/** - Get the IORT ACPI table. - - @param[out] IortTable Pointer to receive the IORT table pointer. - - @retval EFI_SUCCESS The IORT ACPI table is got. - @retval EFI_ALREADY_STARTED The IORT ACPI table has been got previously. - @retval EFI_NOT_FOUND The IORT ACPI table is not found. - @retval EFI_INVALID_PARAMETER IortTable is NULL. -**/ -EFI_STATUS -EFIAPI -GetIortAcpiTable ( - OUT EFI_ACPI_DESCRIPTION_HEADER **IortTable - ) -{ - if (IortTable == NULL) { - return EFI_INVALID_PARAMETER; - } - - return GetAcpiTable (EFI_ACPI_6_0_IO_REMAPPING_TABLE_SIGNATURE, (VOID **)IortTable); -} - -/** - Parse the IORT table to find all SMMUv3 nodes. - - @param[in] IortTable Pointer to the IORT table. - @param[out] SmmuCount Pointer to receive the count of SMMUv3 nodes. - @param[out] SmmuBaseAddresses Pointer to receive the array of SMMU base addresses. - Caller must free this memory when done. - - @retval EFI_SUCCESS SMMUv3 nodes were found and parsed. - @retval EFI_NOT_FOUND No SMMUv3 nodes found. - @retval EFI_INVALID_PARAMETER A required parameter is NULL. - @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. -**/ -EFI_STATUS -EFIAPI -ParseIortAcpiTableSmmu ( - IN EFI_ACPI_DESCRIPTION_HEADER *IortTable, - OUT UINTN *SmmuCount, - OUT UINT64 **SmmuBaseAddresses - ) -{ - EFI_ACPI_6_0_IO_REMAPPING_TABLE *Iort; - EFI_ACPI_6_0_IO_REMAPPING_NODE *Node; - EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE *SmmuNode; - UINT32 Count; - UINTN SmmuIndex; - UINTN LocalSmmuCount; - UINT64 *LocalSmmuBaseAddresses; - - if ((IortTable == NULL) || (SmmuCount == NULL) || (SmmuBaseAddresses == NULL)) { - return EFI_INVALID_PARAMETER; - } - - Iort = (EFI_ACPI_6_0_IO_REMAPPING_TABLE *)IortTable; - Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Iort + Iort->NodeOffset); - - // First pass: count SMMUv3 nodes - LocalSmmuCount = 0; - for (Count = 0; Count < Iort->NumNodes; Count++) { - if (Node->Type == EFI_ACPI_IORT_TYPE_SMMUv3) { - LocalSmmuCount++; - } - Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Node + Node->Length); - } - - if (LocalSmmuCount == 0) { - DEBUG ((DEBUG_ERROR, "%a: No SMMUv3 nodes found\n", __func__)); - *SmmuCount = 0; - *SmmuBaseAddresses = NULL; - return EFI_NOT_FOUND; - } - - // Allocate array for SMMU base addresses - LocalSmmuBaseAddresses = AllocateZeroPool (LocalSmmuCount * sizeof (UINT64)); - if (LocalSmmuBaseAddresses == NULL) { - DEBUG ((DEBUG_ERROR, "%a: Failed to allocate memory for SMMU base addresses\n", __func__)); - *SmmuCount = 0; - *SmmuBaseAddresses = NULL; - return EFI_OUT_OF_RESOURCES; - } - - // Second pass: collect base addresses - Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Iort + Iort->NodeOffset); - SmmuIndex = 0; - - for (Count = 0; Count < Iort->NumNodes; Count++) { - if (Node->Type == EFI_ACPI_IORT_TYPE_SMMUv3) { - SmmuNode = (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE *)Node; - LocalSmmuBaseAddresses[SmmuIndex] = SmmuNode->Base; - DEBUG ((DEBUG_INFO, "%a: Found SMMUv3 at base 0x%lX\n", __func__, SmmuNode->Base)); - SmmuIndex++; - } - Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Node + Node->Length); - } - - *SmmuCount = LocalSmmuCount; - *SmmuBaseAddresses = LocalSmmuBaseAddresses; - - return EFI_SUCCESS; -} - -/** - Parse the IORT table to find all RMR (Reserved Memory Range) nodes - and return a linked list of memory ranges. - - This function iterates through all nodes in the IORT table looking for - RMR nodes (type 0x6). For each RMR node found, it extracts all memory - range descriptors and adds them to the returned linked list. - - @param[in] IortTable Pointer to the IORT table. - - @retval Pointer to head of linked list of RMR entries, or NULL if none found. -**/ -RMRListNode* -EFIAPI -GetIortAcpiTableRmrList ( - IN EFI_ACPI_DESCRIPTION_HEADER *IortTable - ) -{ - EFI_ACPI_6_0_IO_REMAPPING_TABLE *Iort; - EFI_ACPI_6_0_IO_REMAPPING_NODE *Node; - EFI_ACPI_6_0_IO_REMAPPING_RMR_NODE *RmrNode; - EFI_ACPI_6_0_IO_REMAPPING_MEM_RANGE_DESC *MemRangeDesc; - RMRListNode *Head; - RMRListNode *Current; - RMRListNode *NewNode; - UINT32 Count; - UINT32 MemRangeIndex; - - if (IortTable == NULL) { - DEBUG ((DEBUG_ERROR, "%a: IORT table not available\n", __func__)); - return NULL; - } - - Iort = (EFI_ACPI_6_0_IO_REMAPPING_TABLE *)IortTable; - Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Iort + Iort->NodeOffset); - - Head = NULL; - Current = NULL; - - // Iterate through all nodes looking for RMR nodes - for (Count = 0; Count < Iort->NumNodes; Count++) { - if (Node->Type == EFI_ACPI_IORT_TYPE_RMR) { - RmrNode = (EFI_ACPI_6_0_IO_REMAPPING_RMR_NODE *)Node; - DEBUG ((DEBUG_INFO, "%a: Found RMR node with %d memory range descriptors\n", - __func__, RmrNode->NumMemRangeDesc)); - - // Get pointer to memory range descriptor array - // MemRangeDescRef is offset from the start of the RMR node - MemRangeDesc = (EFI_ACPI_6_0_IO_REMAPPING_MEM_RANGE_DESC *)((UINT8 *)RmrNode + RmrNode->MemRangeDescRef); - - // Iterate through all memory range descriptors in this RMR node - for (MemRangeIndex = 0; MemRangeIndex < RmrNode->NumMemRangeDesc; MemRangeIndex++) { - // Only add valid ranges (non-zero base and length) - if ((MemRangeDesc[MemRangeIndex].Base > 0) && (MemRangeDesc[MemRangeIndex].Length > 0)) { - NewNode = AllocateZeroPool (sizeof (RMRListNode)); - if (NewNode == NULL) { - DEBUG ((DEBUG_ERROR, "%a: Failed to allocate RMRListNode\n", __func__)); - // Return what we have so far - return Head; - } - - NewNode->BaseAddress = MemRangeDesc[MemRangeIndex].Base; - NewNode->Length = MemRangeDesc[MemRangeIndex].Length; - NewNode->Next = NULL; - - DEBUG ((DEBUG_INFO, "%a: Adding RMR range Base=0x%lX, Length=0x%lX\n", - __func__, NewNode->BaseAddress, NewNode->Length)); - - // Add to linked list - if (Head == NULL) { - Head = NewNode; - Current = NewNode; - } else { - Current->Next = NewNode; - Current = NewNode; - } - } - } - } - - // Move to the next node - Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Node + Node->Length); - } - - return Head; -} +/** @file -- IortAcpiTable.c + +This file contains functions to parse the IORT ACPI table for SMMUv3 nodes +and Reserved Memory Range (RMR) nodes. + +Copyright (c) Microsoft Corporation. All rights reserved. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include + +#include "../Acpi.h" +#include "DmaProtection.h" + +/** + Get the IORT ACPI table. + + @param[out] IortTable Pointer to receive the IORT table pointer. + + @retval EFI_SUCCESS The IORT ACPI table is got. + @retval EFI_ALREADY_STARTED The IORT ACPI table has been got previously. + @retval EFI_NOT_FOUND The IORT ACPI table is not found. + @retval EFI_INVALID_PARAMETER IortTable is NULL. +**/ +EFI_STATUS +EFIAPI +GetIortAcpiTable ( + OUT EFI_ACPI_DESCRIPTION_HEADER **IortTable + ) +{ + if (IortTable == NULL) { + return EFI_INVALID_PARAMETER; + } + + return GetAcpiTable (EFI_ACPI_6_0_IO_REMAPPING_TABLE_SIGNATURE, (VOID **)IortTable); +} + +/** + Parse the IORT table to find all SMMUv3 nodes. + + @param[in] IortTable Pointer to the IORT table. + @param[out] SmmuCount Pointer to receive the count of SMMUv3 nodes. + @param[out] SmmuBaseAddresses Pointer to receive the array of SMMU base addresses. + Caller must free this memory when done. + + @retval EFI_SUCCESS SMMUv3 nodes were found and parsed. + @retval EFI_NOT_FOUND No SMMUv3 nodes found. + @retval EFI_INVALID_PARAMETER A required parameter is NULL. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +ParseIortAcpiTableSmmu ( + IN EFI_ACPI_DESCRIPTION_HEADER *IortTable, + OUT UINTN *SmmuCount, + OUT UINT64 **SmmuBaseAddresses + ) +{ + EFI_ACPI_6_0_IO_REMAPPING_TABLE *Iort; + EFI_ACPI_6_0_IO_REMAPPING_NODE *Node; + EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE *SmmuNode; + UINT32 Count; + UINTN SmmuIndex; + UINTN LocalSmmuCount; + UINT64 *LocalSmmuBaseAddresses; + + if ((IortTable == NULL) || (SmmuCount == NULL) || (SmmuBaseAddresses == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Iort = (EFI_ACPI_6_0_IO_REMAPPING_TABLE *)IortTable; + Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Iort + Iort->NodeOffset); + + // First pass: count SMMUv3 nodes + LocalSmmuCount = 0; + for (Count = 0; Count < Iort->NumNodes; Count++) { + if (Node->Type == EFI_ACPI_IORT_TYPE_SMMUv3) { + LocalSmmuCount++; + } + + Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Node + Node->Length); + } + + if (LocalSmmuCount == 0) { + DEBUG ((DEBUG_ERROR, "%a: No SMMUv3 nodes found\n", __func__)); + *SmmuCount = 0; + *SmmuBaseAddresses = NULL; + return EFI_NOT_FOUND; + } + + // Allocate array for SMMU base addresses + LocalSmmuBaseAddresses = AllocateZeroPool (LocalSmmuCount * sizeof (UINT64)); + if (LocalSmmuBaseAddresses == NULL) { + DEBUG ((DEBUG_ERROR, "%a: Failed to allocate memory for SMMU base addresses\n", __func__)); + *SmmuCount = 0; + *SmmuBaseAddresses = NULL; + return EFI_OUT_OF_RESOURCES; + } + + // Second pass: collect base addresses + Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Iort + Iort->NodeOffset); + SmmuIndex = 0; + + for (Count = 0; Count < Iort->NumNodes; Count++) { + if (Node->Type == EFI_ACPI_IORT_TYPE_SMMUv3) { + SmmuNode = (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE *)Node; + LocalSmmuBaseAddresses[SmmuIndex] = SmmuNode->Base; + SmmuIndex++; + } + + Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Node + Node->Length); + } + + *SmmuCount = LocalSmmuCount; + *SmmuBaseAddresses = LocalSmmuBaseAddresses; + + return EFI_SUCCESS; +} + +/** + Parse the IORT table to find all RMR (Reserved Memory Range) nodes + and return a linked list of memory ranges. + + This function iterates through all nodes in the IORT table looking for + RMR nodes (type 0x6). For each RMR node found, it extracts all memory + range descriptors and adds them to the returned linked list. + + @param[in] IortTable Pointer to the IORT table. + + @retval Pointer to head of linked list of RMR entries, or NULL if none found. +**/ +RMRListNode * +EFIAPI +GetIortAcpiTableRmrList ( + IN EFI_ACPI_DESCRIPTION_HEADER *IortTable + ) +{ + EFI_ACPI_6_0_IO_REMAPPING_TABLE *Iort; + EFI_ACPI_6_0_IO_REMAPPING_NODE *Node; + EFI_ACPI_6_0_IO_REMAPPING_RMR_NODE *RmrNode; + EFI_ACPI_6_0_IO_REMAPPING_MEM_RANGE_DESC *MemRangeDesc; + RMRListNode *Head; + RMRListNode *Current; + RMRListNode *NewNode; + UINT32 Count; + UINT32 MemRangeIndex; + + if (IortTable == NULL) { + DEBUG ((DEBUG_ERROR, "%a: IORT table not available\n", __func__)); + return NULL; + } + + Iort = (EFI_ACPI_6_0_IO_REMAPPING_TABLE *)IortTable; + Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Iort + Iort->NodeOffset); + + Head = NULL; + Current = NULL; + + // Iterate through all nodes looking for RMR nodes + for (Count = 0; Count < Iort->NumNodes; Count++) { + if (Node->Type == EFI_ACPI_IORT_TYPE_RMR) { + RmrNode = (EFI_ACPI_6_0_IO_REMAPPING_RMR_NODE *)Node; + + // Get pointer to memory range descriptor array + // MemRangeDescRef is offset from the start of the RMR node + MemRangeDesc = (EFI_ACPI_6_0_IO_REMAPPING_MEM_RANGE_DESC *)((UINT8 *)RmrNode + RmrNode->MemRangeDescRef); + + // Iterate through all memory range descriptors in this RMR node + for (MemRangeIndex = 0; MemRangeIndex < RmrNode->NumMemRangeDesc; MemRangeIndex++) { + // Only add valid ranges (non-zero base and length) + if ((MemRangeDesc[MemRangeIndex].Base > 0) && (MemRangeDesc[MemRangeIndex].Length > 0)) { + NewNode = AllocateZeroPool (sizeof (RMRListNode)); + if (NewNode == NULL) { + DEBUG ((DEBUG_ERROR, "%a: Failed to allocate RMRListNode\n", __func__)); + // Return what we have so far + return Head; + } + + NewNode->BaseAddress = MemRangeDesc[MemRangeIndex].Base; + NewNode->Length = MemRangeDesc[MemRangeIndex].Length; + NewNode->Next = NULL; + + // Add to linked list + if (Head == NULL) { + Head = NewNode; + Current = NewNode; + } else { + Current->Next = NewNode; + Current = NewNode; + } + } + } + } + + // Move to the next node + Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Node + Node->Length); + } + + return Head; +} diff --git a/UefiTestingPkg/UefiTestingPkg.dsc b/UefiTestingPkg/UefiTestingPkg.dsc index 2e2d40740c..3e7798af6b 100644 --- a/UefiTestingPkg/UefiTestingPkg.dsc +++ b/UefiTestingPkg/UefiTestingPkg.dsc @@ -156,6 +156,7 @@ # NOTE: These currently have source files that are only implemented for AARCH64. # If needed on X86, should port (and test) the functions. UefiTestingPkg/FunctionalSystemTests/MpManagement/Driver/MpManagement.inf + UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf [BuildOptions] #force deprecated interfaces off From 17f60ec2526d896c25c857818270aae0c917b5fe Mon Sep 17 00:00:00 2001 From: eeshanl Date: Tue, 16 Jun 2026 04:32:52 +0000 Subject: [PATCH 4/4] update test conditions --- .../UEFI/DMAProtectionUnitTestApp.c | 3 - .../UEFI/SMMU/DMAProtectionTestArch.c | 150 +++++++++++++----- .../UEFI/SMMU/DmaProtection.h | 24 +++ .../UEFI/SMMU/IortAcpiTable.c | 65 ++++++++ 4 files changed, 203 insertions(+), 39 deletions(-) diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMAProtectionUnitTestApp.c b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMAProtectionUnitTestApp.c index a08848963f..40dc3a6a24 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMAProtectionUnitTestApp.c +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMAProtectionUnitTestApp.c @@ -317,10 +317,8 @@ CheckBMETeardown ( for (i = 0; i < (PreVarSize)/(sizeof (BOOLEAN)); i++) { // BME Enabled before exit boot services UT_LOG_INFO (PreBuffer[i] ? "Pre-EBS BME %d: True\n" : "Pre-EBS BME %d: False\n", i); - DEBUG ((DEBUG_INFO, PreBuffer[i] ? "%a: Pre-EBS BME %d: True\n" : "%a: Pre-EBS BME %d: False\n", __func__, i)); // BME Disabled after exit boot services UT_LOG_INFO (PostBuffer[i] ? "Post-EBS BME %d: True\n" : "Post-EBS BME %d: False\n", i); - DEBUG ((DEBUG_INFO, PostBuffer[i] ? "%a: Post-EBS BME %d: True\n" : "%a: Post-EBS BME %d: False\n", __func__, i)); UT_ASSERT_FALSE (PostBuffer[i]); } @@ -351,7 +349,6 @@ CheckBMETeardown ( } UT_LOG_INFO ("PASSED: BME test.\n"); - DEBUG ((DEBUG_INFO, "PASSED: BME test.\n")); return UNIT_TEST_PASSED; } // CheckBMETeardown() diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c index d92138ae23..c7998665a0 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c @@ -1,12 +1,18 @@ /** @file -- DMAProtectionTestArch.c This file contains architecture specific DMA protection tests for ARM SMMU (SMMUv3): -1) Check the CR0 registers of the SMMUv3 nodes to verify SMMU translation is enabled +1) Check the CR0 registers of the SMMUv3 nodes to verify SMMU translation is enabled. + An SMMU that is not enabled (SMMUEN == 0) is still considered DMA-safe if: + a) It is configured for global abort (GBPA.ABORT == 1), so all DMA is aborted, or + b) It is in global bypass but has a Reserved Memory Range (RMR) associated with + it in the IORT (the RMR describes memory expected to bypass translation). 2) Check that Command Queue is enabled (CMDQEN bit in CR0) 3) Check that Event Queue is enabled (EVTQEN bit in CR0) 4) Check that Stream Table Base is configured (STRTAB_BASE is not NULL) 5) Check that GERROR register is 0 (no global errors) -6) Check RMR (Reserved Memory Range) regions from IORT are set as reserved in memory map +6) Check RMR (Reserved Memory Range) regions from IORT are found in the EFI memory map + and marked with an acceptable memory type (EfiReservedMemoryType or + EfiRuntimeServicesData). Copyright (c) Microsoft Corporation. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent @@ -32,12 +38,20 @@ SPDX-License-Identifier: BSD-2-Clause-Patent /** Test to verify that the Reserved Memory Range (RMR) regions defined in the IORT - are properly marked as reserved in the EFI memory map. + are found in the EFI memory map and marked with an acceptable memory type. + + For each RMR region, the test verifies that: + 1) An EFI memory map descriptor fully encompasses the RMR region, and + 2) That descriptor's memory type is acceptable, i.e. EfiReservedMemoryType + or EfiRuntimeServicesData. + + If an RMR region is not found in the memory map, or is found but is not one of + the acceptable memory types, the test fails. @param[in] Context The unit test context (not used). - @retval UNIT_TEST_PASSED All RMR regions are properly marked as reserved. - @retval UNIT_TEST_ERROR_TEST_FAILED A RMR region was not found or not marked as reserved. + @retval UNIT_TEST_PASSED All RMR regions were found with an acceptable memory type. + @retval UNIT_TEST_ERROR_TEST_FAILED A RMR region was not found, or had an unacceptable memory type. **/ UNIT_TEST_STATUS EFIAPI @@ -57,6 +71,8 @@ CheckExcludedRegions ( RMRListNode *Head; RMRListNode *Current; BOOLEAN Found; + BOOLEAN FoundInMemoryMap; + UINT32 FoundMemoryType; UNIT_TEST_STATUS TestStatus; // @@ -105,6 +121,8 @@ CheckExcludedRegions ( } else { UT_LOG_ERROR ("GetMemoryMap Failed\n"); DEBUG ((DEBUG_ERROR, "%a: GetMemoryMap Failed\n", __func__)); + TestStatus = UNIT_TEST_ERROR_TEST_FAILED; + UT_ASSERT_STATUS_EQUAL (Status, TestStatus); return UNIT_TEST_ERROR_TEST_FAILED; } @@ -116,8 +134,10 @@ CheckExcludedRegions ( TestStatus = UNIT_TEST_PASSED; while (Current != NULL) { - Found = FALSE; - EfiMemNext = EfiMemoryMap; + Found = FALSE; + FoundInMemoryMap = FALSE; + FoundMemoryType = 0; + EfiMemNext = EfiMemoryMap; UT_LOG_INFO ("Checking RMR region: Base=0x%lX, Length=0x%lX\n", Current->BaseAddress, Current->Length); DEBUG ((DEBUG_INFO, "%a: Checking RMR region: Base=0x%lX, Length=0x%lX\n", __func__, Current->BaseAddress, Current->Length)); @@ -127,6 +147,9 @@ CheckExcludedRegions ( if ((EfiMemNext->PhysicalStart <= Current->BaseAddress) && ((EfiMemNext->PhysicalStart + (EFI_PAGE_SIZE * EfiMemNext->NumberOfPages)) >= (Current->BaseAddress + Current->Length))) { + FoundInMemoryMap = TRUE; + FoundMemoryType = EfiMemNext->Type; + UT_LOG_INFO ( "Found encompassing memory range: Base=0x%lX, Length=0x%lX, Type=%d\n", EfiMemNext->PhysicalStart, @@ -142,22 +165,9 @@ CheckExcludedRegions ( EfiMemNext->Type )); - // Verify memory range is marked as reserved - if (EfiMemNext->Type == EfiReservedMemoryType) { - UT_LOG_INFO ( - "RMR between 0x%lX and 0x%lX found with reserved memory type %d\n", - Current->BaseAddress, - Current->BaseAddress + Current->Length, - EfiMemNext->Type - ); - DEBUG (( - DEBUG_INFO, - "%a: RMR between 0x%lX and 0x%lX found with reserved memory type %d\n", - __func__, - Current->BaseAddress, - Current->BaseAddress + Current->Length, - EfiMemNext->Type - )); + if ((EfiMemNext->Type == EfiReservedMemoryType) || + (EfiMemNext->Type == EfiRuntimeServicesData)) + { Found = TRUE; } @@ -168,18 +178,40 @@ CheckExcludedRegions ( EfiMemNext = NEXT_MEMORY_DESCRIPTOR (EfiMemNext, EfiDescriptorSize); } + // + // Report whether the RMR region was located in the UEFI memory map, + // and if found, the memory type (even if it is not reserved). + // + if (!FoundInMemoryMap) { + UT_LOG_INFO ( + "RMR region Base=0x%lX, Length=0x%lX was NOT found in the UEFI memory map\n", + Current->BaseAddress, + Current->Length + ); + DEBUG (( + DEBUG_INFO, + "%a: RMR region Base=0x%lX, Length=0x%lX was NOT found in the UEFI memory map\n", + __func__, + Current->BaseAddress, + Current->Length + )); + TestStatus = UNIT_TEST_ERROR_TEST_FAILED; + } + if (!Found) { UT_LOG_ERROR ( - "RMR between 0x%lX and 0x%lX NOT found with reserved memory type!\n", + "RMR between 0x%lX and 0x%lX NOT found with an acceptable memory type (Reserved or RuntimeServicesData)! Memory type found: %d\n", Current->BaseAddress, - Current->BaseAddress + Current->Length + Current->BaseAddress + Current->Length, + FoundMemoryType ); DEBUG (( DEBUG_ERROR, - "%a: RMR between 0x%lX and 0x%lX NOT found with reserved memory type!\n", + "%a: RMR between 0x%lX and 0x%lX NOT found with an acceptable memory type (Reserved or RuntimeServicesData)! Memory type found: %d\n", __func__, Current->BaseAddress, - Current->BaseAddress + Current->Length + Current->BaseAddress + Current->Length, + FoundMemoryType )); TestStatus = UNIT_TEST_ERROR_TEST_FAILED; } @@ -187,24 +219,34 @@ CheckExcludedRegions ( Current = Current->Next; } - UT_LOG_INFO ("%a: Result=%d\n", __func__, TestStatus); - DEBUG ((DEBUG_INFO, "%a: Result=%d\n", __func__, TestStatus)); + UT_LOG_INFO ("%a: Result=%d (%a)\n", __func__, TestStatus, (TestStatus == UNIT_TEST_PASSED) ? "PASSED" : "FAILED"); + DEBUG ((DEBUG_INFO, "%a: Result=%d (%a)\n", __func__, TestStatus, (TestStatus == UNIT_TEST_PASSED) ? "PASSED" : "FAILED")); + UT_ASSERT_STATUS_EQUAL (TestStatus, UNIT_TEST_PASSED); return TestStatus; } // CheckExcludedRegions() /** - Test to verify that all SMMUv3 units found in the IORT have translation enabled. - This checks: + Test to verify that all SMMUv3 units found in the IORT are configured to be + DMA-safe. + + For each SMMUv3 unit, if translation is enabled (CR0.SMMUEN == 1) this checks: 1) CR0 register's SMMUEN bit to confirm the SMMU is actively translating 2) CR0 register's CMDQEN bit to confirm the command queue is enabled 3) CR0 register's EVTQEN bit to confirm the event queue is enabled 4) STRTAB_BASE register is not NULL (stream table must be configured) 5) GERROR register is 0 (no global errors) + If translation is not enabled (CR0.SMMUEN == 0), the SMMU is still considered + DMA-safe (and the checks above are skipped) when either: + a) It is configured for global abort (GBPA.ABORT == 1), so all DMA is aborted, or + b) It is in global bypass but has a Reserved Memory Range (RMR) associated with + it in the IORT (the RMR describes memory expected to bypass translation). + Otherwise the SMMU is considered unsafe and the test fails. + @param[in] Context The unit test context (not used). - @retval UNIT_TEST_PASSED All SMMU units are properly configured. + @retval UNIT_TEST_PASSED All SMMU units are properly configured. @retval UNIT_TEST_ERROR_TEST_FAILED An SMMU unit is not properly configured. **/ UNIT_TEST_STATUS @@ -225,6 +267,8 @@ CheckIOMMUEnabled ( UINT64 StrTabBase; UINT64 StrTabBaseAddr; UINT32 GError; + UINT32 GbpaValue; + UINT32 AbortBit; UNIT_TEST_STATUS TestStatus; // @@ -253,6 +297,7 @@ CheckIOMMUEnabled ( // // Step 4: For each SMMU, check: + // - SMMU GBPA Set (GBPA.ABORT == 1), or: // - SMMU Enable bit (SMMUEN) in CR0 register // - Command Queue Enable bit (CMDQEN) in CR0 register // - Event Queue Enable bit (EVTQEN) in CR0 register @@ -277,9 +322,41 @@ CheckIOMMUEnabled ( UT_LOG_INFO ("SMMUEN bit: %d\n", SmmuEnBit); DEBUG ((DEBUG_INFO, "%a: SMMUEN bit: %d\n", __func__, SmmuEnBit)); if (SmmuEnBit == 0) { - UT_LOG_ERROR ("SMMUEN bit is disabled for SMMUv3 at base address 0x%lX\n", SmmuBaseAddresses[Iterator]); - DEBUG ((DEBUG_ERROR, "%a: SMMUEN bit is disabled for SMMUv3 at base address 0x%lX\n", __func__, SmmuBaseAddresses[Iterator])); + // + // SMMU translation is not enabled. The SMMU is still DMA-safe if it is + // configured for global abort (GBPA.ABORT == 1). + // + GbpaValue = MmioRead32 ((UINTN)(SmmuBaseAddresses[Iterator] + SMMU_GBPA)); + AbortBit = GbpaValue & SMMU_GBPA_ABORT; + UT_LOG_INFO ("GBPA Register Value: 0x%X, ABORT bit: %d\n", GbpaValue, AbortBit ? 1 : 0); + DEBUG ((DEBUG_INFO, "%a: GBPA Register Value: 0x%X, ABORT bit: %d\n", __func__, GbpaValue, AbortBit ? 1 : 0)); + + if (AbortBit != 0) { + // + // Global abort is set: all DMA is aborted, so this SMMU is DMA-safe. + // Skip the remaining translation-related checks for this SMMU. + // + UT_LOG_INFO ("SMMUEN is disabled but global abort (GBPA.ABORT) is set for SMMUv3 at base address 0x%lX. SMMU is DMA-safe.\n", SmmuBaseAddresses[Iterator]); + DEBUG ((DEBUG_INFO, "%a: SMMUEN is disabled but global abort (GBPA.ABORT) is set for SMMUv3 at base address 0x%lX. SMMU is DMA-safe.\n", __func__, SmmuBaseAddresses[Iterator])); + continue; + } + + // + // SMMU is in global bypass (not abort) and not enabled. This is permitted + // only if the SMMU has a Reserved Memory Range (RMR) associated with it in + // the IORT, since the RMR describes memory that is expected to bypass + // translation. + // + if (SmmuHasAssociatedRmr (IortTable, SmmuBaseAddresses[Iterator])) { + UT_LOG_INFO ("SMMUEN is disabled and SMMU is in global bypass, but an RMR is associated with SMMUv3 at base address 0x%lX. This is permitted.\n", SmmuBaseAddresses[Iterator]); + DEBUG ((DEBUG_INFO, "%a: SMMUEN is disabled and SMMU is in global bypass, but an RMR is associated with SMMUv3 at base address 0x%lX. This is permitted.\n", __func__, SmmuBaseAddresses[Iterator])); + continue; + } + + UT_LOG_ERROR ("SMMUEN bit is disabled, global abort is not set, and no RMR is associated for SMMUv3 at base address 0x%lX\n", SmmuBaseAddresses[Iterator]); + DEBUG ((DEBUG_ERROR, "%a: SMMUEN bit is disabled, global abort is not set, and no RMR is associated for SMMUv3 at base address 0x%lX\n", __func__, SmmuBaseAddresses[Iterator])); TestStatus = UNIT_TEST_ERROR_TEST_FAILED; + continue; } // @@ -337,8 +414,9 @@ CheckIOMMUEnabled ( } } - UT_LOG_INFO ("%a: Result=%d\n", __func__, TestStatus); - DEBUG ((DEBUG_INFO, "%a: Result=%d\n", __func__, TestStatus)); + UT_LOG_INFO ("%a: Result=%d (%a)\n", __func__, TestStatus, (TestStatus == UNIT_TEST_PASSED) ? "PASSED" : "FAILED"); + DEBUG ((DEBUG_INFO, "%a: Result=%d (%a)\n", __func__, TestStatus, (TestStatus == UNIT_TEST_PASSED) ? "PASSED" : "FAILED")); + UT_ASSERT_STATUS_EQUAL (TestStatus, UNIT_TEST_PASSED); return TestStatus; } // CheckIOMMUEnabled() diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h index 5dd19e74ac..6faf879966 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent // #define SMMU_CR0 0x0020 #define SMMU_CR0ACK 0x0024 +#define SMMU_GBPA 0x0044 #define SMMU_GERROR 0x0060 #define SMMU_STRTAB_BASE 0x0080 @@ -33,6 +34,11 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #define SMMU_CR0_EVTQEN BIT2 // Event Queue Enable bit #define SMMU_CR0_CMDQEN BIT3 // Command Queue Enable bit +// +// SMMUv3 GBPA Register Bits +// +#define SMMU_GBPA_ABORT BIT20 // Global Bypass Abort bit (abort all DMA when SMMUEN == 0) + // // STRTAB_BASE lower bits mask (bits [5:0] are reserved/config) // @@ -100,4 +106,22 @@ GetIortAcpiTableRmrList ( IN EFI_ACPI_DESCRIPTION_HEADER *IortTable ); +/** + Determine whether the given SMMUv3 (identified by its base address) has at + least one Reserved Memory Range (RMR) node associated with it via the RMR + node's ID mappings. + + @param[in] IortTable Pointer to the IORT table. + @param[in] SmmuBase Base address of the SMMUv3 to check. + + @retval TRUE At least one RMR node references the specified SMMUv3. + @retval FALSE No RMR node references the specified SMMUv3, or inputs invalid. +**/ +BOOLEAN +EFIAPI +SmmuHasAssociatedRmr ( + IN EFI_ACPI_DESCRIPTION_HEADER *IortTable, + IN UINT64 SmmuBase + ); + #endif // _DMA_PROTECTION_H_ diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c index 6caa21a688..db4156d322 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c @@ -201,3 +201,68 @@ GetIortAcpiTableRmrList ( return Head; } + +/** + Determine whether the given SMMUv3 (identified by its base address) has at + least one Reserved Memory Range (RMR) node associated with it via the RMR + node's ID mappings. + + Each RMR node contains an ID mapping table; each mapping's OutputReference is + the byte offset (from the start of the IORT table) of the node the RMR is + associated with. This function walks every RMR node's ID mappings and checks + whether any of them reference an SMMUv3 node whose Base matches SmmuBase. + + @param[in] IortTable Pointer to the IORT table. + @param[in] SmmuBase Base address of the SMMUv3 to check. + + @retval TRUE At least one RMR node references the specified SMMUv3. + @retval FALSE No RMR node references the specified SMMUv3, or inputs invalid. +**/ +BOOLEAN +EFIAPI +SmmuHasAssociatedRmr ( + IN EFI_ACPI_DESCRIPTION_HEADER *IortTable, + IN UINT64 SmmuBase + ) +{ + EFI_ACPI_6_0_IO_REMAPPING_TABLE *Iort; + EFI_ACPI_6_0_IO_REMAPPING_NODE *Node; + EFI_ACPI_6_0_IO_REMAPPING_RMR_NODE *RmrNode; + EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE *IdMapping; + EFI_ACPI_6_0_IO_REMAPPING_NODE *OutputNode; + EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE *SmmuNode; + UINT32 Count; + UINT32 MappingIndex; + + if (IortTable == NULL) { + return FALSE; + } + + Iort = (EFI_ACPI_6_0_IO_REMAPPING_TABLE *)IortTable; + Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Iort + Iort->NodeOffset); + + // Iterate through all nodes looking for RMR nodes + for (Count = 0; Count < Iort->NumNodes; Count++) { + if (Node->Type == EFI_ACPI_IORT_TYPE_RMR) { + RmrNode = (EFI_ACPI_6_0_IO_REMAPPING_RMR_NODE *)Node; + IdMapping = (EFI_ACPI_6_0_IO_REMAPPING_ID_TABLE *)((UINT8 *)RmrNode + RmrNode->Node.IdReference); + + // Walk each ID mapping and resolve the referenced output node + for (MappingIndex = 0; MappingIndex < RmrNode->Node.NumIdMappings; MappingIndex++) { + OutputNode = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Iort + IdMapping[MappingIndex].OutputReference); + + if (OutputNode->Type == EFI_ACPI_IORT_TYPE_SMMUv3) { + SmmuNode = (EFI_ACPI_6_0_IO_REMAPPING_SMMU3_NODE *)OutputNode; + if (SmmuNode->Base == SmmuBase) { + return TRUE; + } + } + } + } + + // Move to the next node + Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Node + Node->Length); + } + + return FALSE; +}