From 7ab35fcb7719bc05b03f78d10c0c3d055c0f5789 Mon Sep 17 00:00:00 2001 From: eeshanl Date: Thu, 8 Jan 2026 07:56:16 +0000 Subject: [PATCH 1/6] 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 8c041491b8fa465ada5c39285f9400f06bcf0ce5 Mon Sep 17 00:00:00 2001 From: eeshanl Date: Thu, 8 Jan 2026 10:48:31 +0000 Subject: [PATCH 2/6] 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 801359cee28ce5a2b008a50da3b931287f73533e Mon Sep 17 00:00:00 2001 From: eeshanl Date: Fri, 9 Jan 2026 01:16:52 +0000 Subject: [PATCH 3/6] 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 af25664e8c789fcfb5d57cc1f4e722889f6cfbd9 Mon Sep 17 00:00:00 2001 From: eeshanl Date: Tue, 16 Jun 2026 04:32:52 +0000 Subject: [PATCH 4/6] update test conditions --- .../UEFI/DMAProtectionUnitTestApp.c | 3 - .../DMAProtectionAudit/UEFI/README.md | 1 + .../UEFI/SMMU/DMAProtectionTestArch.c | 134 +++++++++++++----- .../UEFI/SMMU/DmaProtection.h | 6 + 4 files changed, 105 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/README.md b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/README.md index 74735a9956..22c6b0fc34 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/README.md +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/README.md @@ -16,6 +16,7 @@ Shell based UEFI unit test based off of the UnitTestFrameworkPkg that test for: 3. All excluded regions are set as (EfiReservedMemoryType or EfiACPIMemoryNVS) for IVRS. 4. Bus mastering enabled (BME) is disabled on ExitBootServices. Because we can no longer write to file after ExitBootServices a variable is used to store the test state and the machine. +5. For ARM SMMUv3, each SMMU unit in the IORT has translation enabled (or is otherwise DMA-safe via GBPA abort / IORT RMR). Note: this unit test requires a restart to finish its testing. If you plan to use this unit test in automation make sure to set up your startup.nsh script properly. diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c index d92138ae23..eec1cf623f 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c @@ -1,12 +1,16 @@ /** @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 only if + it is configured for global abort (GBPA.ABORT == 1), so all DMA is aborted. 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 +36,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 +69,8 @@ CheckExcludedRegions ( RMRListNode *Head; RMRListNode *Current; BOOLEAN Found; + BOOLEAN FoundInMemoryMap; + UINT32 FoundMemoryType; UNIT_TEST_STATUS TestStatus; // @@ -105,6 +119,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 +132,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 +145,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 +163,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 +176,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 +217,32 @@ 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) only if it is configured for global + abort (GBPA.ABORT == 1), so all DMA is aborted. 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 +263,8 @@ CheckIOMMUEnabled ( UINT64 StrTabBase; UINT64 StrTabBaseAddr; UINT32 GError; + UINT32 GbpaValue; + UINT32 AbortBit; UNIT_TEST_STATUS TestStatus; // @@ -253,6 +293,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 +318,29 @@ 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; + } + + UT_LOG_ERROR ("SMMUEN bit is disabled and global abort is not set for SMMUv3 at base address 0x%lX\n", SmmuBaseAddresses[Iterator]); + DEBUG ((DEBUG_ERROR, "%a: SMMUEN bit is disabled and global abort is not set for SMMUv3 at base address 0x%lX\n", __func__, SmmuBaseAddresses[Iterator])); TestStatus = UNIT_TEST_ERROR_TEST_FAILED; + continue; } // @@ -337,8 +398,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..fca9d0eabb 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) // From 0bc8e372c98d26d4a88c081e3d93ecc55484543a Mon Sep 17 00:00:00 2001 From: eeshanl Date: Thu, 30 Jul 2026 15:22:50 -0700 Subject: [PATCH 5/6] address kuns comments --- .../UEFI/DMASmmuProtectionUnitTestApp.inf | 1 + .../DMAProtectionAudit/UEFI/README.md | 2 +- .../UEFI/SMMU/DMAProtectionTestArch.c | 94 +++++++------------ .../UEFI/SMMU/DmaProtection.h | 45 ++++++--- .../UEFI/SMMU/IortAcpiTable.c | 73 +++++++------- 5 files changed, 107 insertions(+), 108 deletions(-) diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf index 631af0a92d..bae9f27102 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMASmmuProtectionUnitTestApp.inf @@ -44,6 +44,7 @@ [LibraryClasses] UefiApplicationEntryPoint + BaseLib DebugLib UnitTestLib UnitTestBootLib diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/README.md b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/README.md index 22c6b0fc34..1a6c1baafb 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/README.md +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/README.md @@ -16,7 +16,7 @@ Shell based UEFI unit test based off of the UnitTestFrameworkPkg that test for: 3. All excluded regions are set as (EfiReservedMemoryType or EfiACPIMemoryNVS) for IVRS. 4. Bus mastering enabled (BME) is disabled on ExitBootServices. Because we can no longer write to file after ExitBootServices a variable is used to store the test state and the machine. -5. For ARM SMMUv3, each SMMU unit in the IORT has translation enabled (or is otherwise DMA-safe via GBPA abort / IORT RMR). +5. For ARM SMMUv3, each SMMU unit in the IORT has translation enabled (or is otherwise DMA-safe via GBPA abort). Note: this unit test requires a restart to finish its testing. If you plan to use this unit test in automation make sure to set up your startup.nsh script properly. diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c index eec1cf623f..8f36b2f6de 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c @@ -17,6 +17,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent **/ +#include #include #include #include @@ -66,8 +67,9 @@ CheckExcludedRegions ( UINTN EfiDescriptorSize; UINT32 EfiDescriptorVersion; EFI_ACPI_DESCRIPTION_HEADER *IortTable; - RMRListNode *Head; - RMRListNode *Current; + LIST_ENTRY RmrList; + LIST_ENTRY *Link; + RMR_LIST_NODE *RmrEntry; BOOLEAN Found; BOOLEAN FoundInMemoryMap; UINT32 FoundMemoryType; @@ -83,15 +85,14 @@ CheckExcludedRegions ( // // Step 2: Get the RMR (Reserved Memory Range) nodes from IORT Table // - Head = GetIortAcpiTableRmrList (IortTable); - if (Head == NULL) { + InitializeListHead (&RmrList); + Status = GetIortAcpiTableRmrList (IortTable, &RmrList); + UT_ASSERT_NOT_EFI_ERROR (Status); + if (IsListEmpty (&RmrList)) { 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. // @@ -118,7 +119,6 @@ CheckExcludedRegions ( UT_ASSERT_NOT_EFI_ERROR (Status); } 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; @@ -131,19 +131,22 @@ CheckExcludedRegions ( EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)EfiMemoryMap + EfiMemoryMapSize); TestStatus = UNIT_TEST_PASSED; - while (Current != NULL) { + for (Link = GetFirstNode (&RmrList); + !IsNull (&RmrList, Link); + Link = GetNextNode (&RmrList, Link)) + { + RmrEntry = RMR_LIST_NODE_FROM_LINK (Link); 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)); + UT_LOG_INFO ("Checking RMR region: Base=0x%lX, Length=0x%lX\n", RmrEntry->BaseAddress, RmrEntry->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))) + if ((EfiMemNext->PhysicalStart <= RmrEntry->BaseAddress) && + ((EfiMemNext->PhysicalStart + (EFI_PAGE_SIZE * EfiMemNext->NumberOfPages)) >= (RmrEntry->BaseAddress + RmrEntry->Length))) { FoundInMemoryMap = TRUE; FoundMemoryType = EfiMemNext->Type; @@ -154,14 +157,6 @@ CheckExcludedRegions ( 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 - )); if ((EfiMemNext->Type == EfiReservedMemoryType) || (EfiMemNext->Type == EfiRuntimeServicesData)) @@ -183,42 +178,34 @@ CheckExcludedRegions ( 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 + RmrEntry->BaseAddress, + RmrEntry->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 an acceptable memory type (Reserved or RuntimeServicesData)! Memory type found: %d\n", - Current->BaseAddress, - Current->BaseAddress + Current->Length, + RmrEntry->BaseAddress, + RmrEntry->BaseAddress + RmrEntry->Length, FoundMemoryType ); - DEBUG (( - DEBUG_ERROR, - "%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, - FoundMemoryType - )); TestStatus = UNIT_TEST_ERROR_TEST_FAILED; } - - Current = Current->Next; } 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")); + + // + // Free every RMR entry appended by GetIortAcpiTableRmrList. + // + while (!IsListEmpty (&RmrList)) { + Link = GetFirstNode (&RmrList); + RmrEntry = RMR_LIST_NODE_FROM_LINK (Link); + RemoveEntryList (Link); + FreePool (RmrEntry); + } UT_ASSERT_STATUS_EQUAL (TestStatus, UNIT_TEST_PASSED); return TestStatus; @@ -287,7 +274,6 @@ 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)); TestStatus = UNIT_TEST_PASSED; @@ -302,21 +288,18 @@ 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])); // // 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) { // // SMMU translation is not enabled. The SMMU is still DMA-safe if it is @@ -325,7 +308,6 @@ CheckIOMMUEnabled ( 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) { // @@ -333,12 +315,10 @@ CheckIOMMUEnabled ( // 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; } UT_LOG_ERROR ("SMMUEN bit is disabled and global abort is not set for SMMUv3 at base address 0x%lX\n", SmmuBaseAddresses[Iterator]); - DEBUG ((DEBUG_ERROR, "%a: SMMUEN bit is disabled and global abort is not set for SMMUv3 at base address 0x%lX\n", __func__, SmmuBaseAddresses[Iterator])); TestStatus = UNIT_TEST_ERROR_TEST_FAILED; continue; } @@ -348,10 +328,8 @@ 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)); 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; } @@ -360,10 +338,8 @@ 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)); 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; } @@ -373,15 +349,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)); 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; } @@ -390,16 +363,17 @@ 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)); 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 (%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")); + + // Free the SMMU base address array allocated by ParseIortAcpiTableSmmu. + FreePool (SmmuBaseAddresses); + SmmuBaseAddresses = NULL; UT_ASSERT_STATUS_EQUAL (TestStatus, UNIT_TEST_PASSED); return TestStatus; diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h index fca9d0eabb..41bdf48ced 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h @@ -45,13 +45,21 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #define SMMU_STRTAB_BASE_ADDR_MASK 0x3FULL // -// RMR List Node Structure for tracking Reserved Memory Ranges +// Signature and structure used to track Reserved Memory Ranges (RMRs) parsed +// from the IORT. Nodes are linked into a doubly-linked list rooted at a +// caller-provided LIST_ENTRY head. Use RMR_LIST_NODE_FROM_LINK() to recover +// the containing RMR_LIST_NODE from a LIST_ENTRY *. // -typedef struct _RMRListNode { - UINT64 BaseAddress; - UINT64 Length; - struct _RMRListNode *Next; -} RMRListNode; +#define RMR_LIST_NODE_SIGNATURE SIGNATURE_32 ('R', 'M', 'R', 'N') + +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + UINT64 BaseAddress; + UINT64 Length; +} RMR_LIST_NODE; + +#define RMR_LIST_NODE_FROM_LINK(a) CR (a, RMR_LIST_NODE, Link, RMR_LIST_NODE_SIGNATURE) // // Function Prototypes @@ -94,16 +102,31 @@ ParseIortAcpiTableSmmu ( ); /** - Parse the IORT table to find all RMR (Reserved Memory Range) nodes. + Parse the IORT table and append each Reserved Memory Range (RMR) descriptor + to the caller-provided doubly-linked list. + + Each entry appended is an RMR_LIST_NODE. The caller is responsible for: + - Initializing RmrList (e.g. via InitializeListHead) before calling. + - Freeing every appended RMR_LIST_NODE (e.g. via RemoveEntryList + FreePool) + when done. Entries appended before an error return must also be freed. - @param[in] IortTable Pointer to the IORT table. + If no RMR nodes are found, EFI_SUCCESS is returned and RmrList is left empty + (IsListEmpty returns TRUE). - @retval Pointer to head of linked list of RMR entries, or NULL if none found. + @param[in] IortTable Pointer to the IORT table. + @param[in,out] RmrList List head to append RMR entries to. + + @retval EFI_SUCCESS IORT was parsed; RmrList may be empty if there + are no RMR nodes. + @retval EFI_INVALID_PARAMETER IortTable or RmrList is NULL. + @retval EFI_OUT_OF_RESOURCES Failed to allocate an RMR_LIST_NODE. Any entries + appended before the failure remain in RmrList. **/ -RMRListNode * +EFI_STATUS EFIAPI GetIortAcpiTableRmrList ( - IN EFI_ACPI_DESCRIPTION_HEADER *IortTable + IN EFI_ACPI_DESCRIPTION_HEADER *IortTable, + IN OUT LIST_ENTRY *RmrList ); #endif // _DMA_PROTECTION_H_ diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c index 6caa21a688..0d0350f49c 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/IortAcpiTable.c @@ -9,6 +9,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include +#include #include #include @@ -121,44 +122,51 @@ ParseIortAcpiTableSmmu ( } /** - 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. + Parse the IORT table and append each Reserved Memory Range (RMR) descriptor + to the caller-provided doubly-linked list. + + This function iterates through all nodes in the IORT table looking for RMR + nodes (type 0x6). For each RMR node found, every valid memory range + descriptor (non-zero base and length) is wrapped in an RMR_LIST_NODE and + appended to RmrList via InsertTailList. + + The caller must have initialized RmrList (e.g. with InitializeListHead) + before calling this function, and is responsible for freeing every appended + entry (e.g. via RemoveEntryList + FreePool) when done. Entries appended + before an EFI_OUT_OF_RESOURCES return must also be freed. + + @param[in] IortTable Pointer to the IORT table. + @param[in,out] RmrList List head to append RMR entries to. + + @retval EFI_SUCCESS IORT was parsed; RmrList may be empty if there + are no RMR nodes. + @retval EFI_INVALID_PARAMETER IortTable or RmrList is NULL. + @retval EFI_OUT_OF_RESOURCES Failed to allocate an RMR_LIST_NODE. Any entries + appended before the failure remain in RmrList. **/ -RMRListNode * +EFI_STATUS EFIAPI GetIortAcpiTableRmrList ( - IN EFI_ACPI_DESCRIPTION_HEADER *IortTable + IN EFI_ACPI_DESCRIPTION_HEADER *IortTable, + IN OUT LIST_ENTRY *RmrList ) { 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; + RMR_LIST_NODE *NewNode; UINT32 Count; UINT32 MemRangeIndex; - if (IortTable == NULL) { - DEBUG ((DEBUG_ERROR, "%a: IORT table not available\n", __func__)); - return NULL; + if ((IortTable == NULL) || (RmrList == NULL)) { + DEBUG ((DEBUG_ERROR, "%a: Invalid parameter\n", __func__)); + 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); - 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) { @@ -172,25 +180,18 @@ GetIortAcpiTableRmrList ( 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)); + NewNode = AllocateZeroPool (sizeof (RMR_LIST_NODE)); if (NewNode == NULL) { - DEBUG ((DEBUG_ERROR, "%a: Failed to allocate RMRListNode\n", __func__)); - // Return what we have so far - return Head; + DEBUG ((DEBUG_ERROR, "%a: Failed to allocate RMR_LIST_NODE\n", __func__)); + // Leave already-appended entries in RmrList; caller must free them. + return EFI_OUT_OF_RESOURCES; } + NewNode->Signature = RMR_LIST_NODE_SIGNATURE; 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; - } + + InsertTailList (RmrList, &NewNode->Link); } } } @@ -199,5 +200,5 @@ GetIortAcpiTableRmrList ( Node = (EFI_ACPI_6_0_IO_REMAPPING_NODE *)((UINT8 *)Node + Node->Length); } - return Head; + return EFI_SUCCESS; } From 62575d1d438c1764801c11f198128eeadab9d9c6 Mon Sep 17 00:00:00 2001 From: eeshanl Date: Mon, 3 Aug 2026 13:23:43 -0700 Subject: [PATCH 6/6] reserved memory type only for RMR regions --- .../UEFI/SMMU/DMAProtectionTestArch.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c index 8f36b2f6de..6a636b8f6e 100644 --- a/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c +++ b/UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c @@ -9,8 +9,7 @@ This file contains architecture specific DMA protection tests for ARM SMMU (SMMU 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 found in the EFI memory map - and marked with an acceptable memory type (EfiReservedMemoryType or - EfiRuntimeServicesData). + and marked with an acceptable memory type (EfiReservedMemoryType) Copyright (c) Microsoft Corporation. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent @@ -42,7 +41,6 @@ SPDX-License-Identifier: BSD-2-Clause-Patent 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. @@ -158,9 +156,7 @@ CheckExcludedRegions ( EfiMemNext->Type ); - if ((EfiMemNext->Type == EfiReservedMemoryType) || - (EfiMemNext->Type == EfiRuntimeServicesData)) - { + if (EfiMemNext->Type == EfiReservedMemoryType) { Found = TRUE; } @@ -186,7 +182,7 @@ CheckExcludedRegions ( if (!Found) { UT_LOG_ERROR ( - "RMR between 0x%lX and 0x%lX NOT found with an acceptable memory type (Reserved or RuntimeServicesData)! Memory type found: %d\n", + "RMR between 0x%lX and 0x%lX NOT found with an acceptable memory type (Reserved)! Memory type found: %d\n", RmrEntry->BaseAddress, RmrEntry->BaseAddress + RmrEntry->Length, FoundMemoryType