11/** @file -- DMAProtectionTestArch.c
22
33This file contains architecture specific DMA protection tests for ARM SMMU (SMMUv3):
4- 1) Check the CR0 registers of the SMMUv3 nodes to verify SMMU translation is enabled
4+ 1) Check the CR0 registers of the SMMUv3 nodes to verify SMMU translation is enabled.
5+ An SMMU that is not enabled (SMMUEN == 0) is still considered DMA-safe if:
6+ a) It is configured for global abort (GBPA.ABORT == 1), so all DMA is aborted, or
7+ b) It is in global bypass but has a Reserved Memory Range (RMR) associated with
8+ it in the IORT (the RMR describes memory expected to bypass translation).
592) Check that Command Queue is enabled (CMDQEN bit in CR0)
6103) Check that Event Queue is enabled (EVTQEN bit in CR0)
7114) Check that Stream Table Base is configured (STRTAB_BASE is not NULL)
8125) Check that GERROR register is 0 (no global errors)
9- 6) Check RMR (Reserved Memory Range) regions from IORT are set as reserved in memory map
13+ 6) Check RMR (Reserved Memory Range) regions from IORT are found in the EFI memory map
14+ and marked with an acceptable memory type (EfiReservedMemoryType or
15+ EfiRuntimeServicesData).
1016
1117Copyright (c) Microsoft Corporation. All rights reserved.
1218SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -32,12 +38,20 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
3238
3339/**
3440 Test to verify that the Reserved Memory Range (RMR) regions defined in the IORT
35- are properly marked as reserved in the EFI memory map.
41+ are found in the EFI memory map and marked with an acceptable memory type.
42+
43+ For each RMR region, the test verifies that:
44+ 1) An EFI memory map descriptor fully encompasses the RMR region, and
45+ 2) That descriptor's memory type is acceptable, i.e. EfiReservedMemoryType
46+ or EfiRuntimeServicesData.
47+
48+ If an RMR region is not found in the memory map, or is found but is not one of
49+ the acceptable memory types, the test fails.
3650
3751 @param[in] Context The unit test context (not used).
3852
39- @retval UNIT_TEST_PASSED All RMR regions are properly marked as reserved .
40- @retval UNIT_TEST_ERROR_TEST_FAILED A RMR region was not found or not marked as reserved .
53+ @retval UNIT_TEST_PASSED All RMR regions were found with an acceptable memory type .
54+ @retval UNIT_TEST_ERROR_TEST_FAILED A RMR region was not found, or had an unacceptable memory type .
4155**/
4256UNIT_TEST_STATUS
4357EFIAPI
@@ -57,6 +71,8 @@ CheckExcludedRegions (
5771 RMRListNode * Head ;
5872 RMRListNode * Current ;
5973 BOOLEAN Found ;
74+ BOOLEAN FoundInMemoryMap ;
75+ UINT32 FoundMemoryType ;
6076 UNIT_TEST_STATUS TestStatus ;
6177
6278 //
@@ -105,6 +121,8 @@ CheckExcludedRegions (
105121 } else {
106122 UT_LOG_ERROR ("GetMemoryMap Failed\n" );
107123 DEBUG ((DEBUG_ERROR , "%a: GetMemoryMap Failed\n" , __func__ ));
124+ TestStatus = UNIT_TEST_ERROR_TEST_FAILED ;
125+ UT_ASSERT_STATUS_EQUAL (Status , TestStatus );
108126 return UNIT_TEST_ERROR_TEST_FAILED ;
109127 }
110128
@@ -116,8 +134,10 @@ CheckExcludedRegions (
116134 TestStatus = UNIT_TEST_PASSED ;
117135
118136 while (Current != NULL ) {
119- Found = FALSE;
120- EfiMemNext = EfiMemoryMap ;
137+ Found = FALSE;
138+ FoundInMemoryMap = FALSE;
139+ FoundMemoryType = 0 ;
140+ EfiMemNext = EfiMemoryMap ;
121141
122142 UT_LOG_INFO ("Checking RMR region: Base=0x%lX, Length=0x%lX\n" , Current -> BaseAddress , Current -> Length );
123143 DEBUG ((DEBUG_INFO , "%a: Checking RMR region: Base=0x%lX, Length=0x%lX\n" , __func__ , Current -> BaseAddress , Current -> Length ));
@@ -127,6 +147,9 @@ CheckExcludedRegions (
127147 if ((EfiMemNext -> PhysicalStart <= Current -> BaseAddress ) &&
128148 ((EfiMemNext -> PhysicalStart + (EFI_PAGE_SIZE * EfiMemNext -> NumberOfPages )) >= (Current -> BaseAddress + Current -> Length )))
129149 {
150+ FoundInMemoryMap = TRUE;
151+ FoundMemoryType = EfiMemNext -> Type ;
152+
130153 UT_LOG_INFO (
131154 "Found encompassing memory range: Base=0x%lX, Length=0x%lX, Type=%d\n" ,
132155 EfiMemNext -> PhysicalStart ,
@@ -142,22 +165,9 @@ CheckExcludedRegions (
142165 EfiMemNext -> Type
143166 ));
144167
145- // Verify memory range is marked as reserved
146- if (EfiMemNext -> Type == EfiReservedMemoryType ) {
147- UT_LOG_INFO (
148- "RMR between 0x%lX and 0x%lX found with reserved memory type %d\n" ,
149- Current -> BaseAddress ,
150- Current -> BaseAddress + Current -> Length ,
151- EfiMemNext -> Type
152- );
153- DEBUG ((
154- DEBUG_INFO ,
155- "%a: RMR between 0x%lX and 0x%lX found with reserved memory type %d\n" ,
156- __func__ ,
157- Current -> BaseAddress ,
158- Current -> BaseAddress + Current -> Length ,
159- EfiMemNext -> Type
160- ));
168+ if ((EfiMemNext -> Type == EfiReservedMemoryType ) ||
169+ (EfiMemNext -> Type == EfiRuntimeServicesData ))
170+ {
161171 Found = TRUE;
162172 }
163173
@@ -168,43 +178,75 @@ CheckExcludedRegions (
168178 EfiMemNext = NEXT_MEMORY_DESCRIPTOR (EfiMemNext , EfiDescriptorSize );
169179 }
170180
181+ //
182+ // Report whether the RMR region was located in the UEFI memory map,
183+ // and if found, the memory type (even if it is not reserved).
184+ //
185+ if (!FoundInMemoryMap ) {
186+ UT_LOG_INFO (
187+ "RMR region Base=0x%lX, Length=0x%lX was NOT found in the UEFI memory map\n" ,
188+ Current -> BaseAddress ,
189+ Current -> Length
190+ );
191+ DEBUG ((
192+ DEBUG_INFO ,
193+ "%a: RMR region Base=0x%lX, Length=0x%lX was NOT found in the UEFI memory map\n" ,
194+ __func__ ,
195+ Current -> BaseAddress ,
196+ Current -> Length
197+ ));
198+ TestStatus = UNIT_TEST_ERROR_TEST_FAILED ;
199+ }
200+
171201 if (!Found ) {
172202 UT_LOG_ERROR (
173- "RMR between 0x%lX and 0x%lX NOT found with reserved memory type! \n" ,
203+ "RMR between 0x%lX and 0x%lX NOT found with an acceptable memory type (Reserved or RuntimeServicesData)! Memory type found: %d \n" ,
174204 Current -> BaseAddress ,
175- Current -> BaseAddress + Current -> Length
205+ Current -> BaseAddress + Current -> Length ,
206+ FoundMemoryType
176207 );
177208 DEBUG ((
178209 DEBUG_ERROR ,
179- "%a: RMR between 0x%lX and 0x%lX NOT found with reserved memory type! \n" ,
210+ "%a: RMR between 0x%lX and 0x%lX NOT found with an acceptable memory type (Reserved or RuntimeServicesData)! Memory type found: %d \n" ,
180211 __func__ ,
181212 Current -> BaseAddress ,
182- Current -> BaseAddress + Current -> Length
213+ Current -> BaseAddress + Current -> Length ,
214+ FoundMemoryType
183215 ));
184216 TestStatus = UNIT_TEST_ERROR_TEST_FAILED ;
185217 }
186218
187219 Current = Current -> Next ;
188220 }
189221
190- UT_LOG_INFO ("%a: Result=%d\n" , __func__ , TestStatus );
191- DEBUG ((DEBUG_INFO , "%a: Result=%d\n" , __func__ , TestStatus ));
222+ UT_LOG_INFO ("%a: Result=%d (%a) \n" , __func__ , TestStatus , ( TestStatus == UNIT_TEST_PASSED ) ? "PASSED" : "FAILED" );
223+ DEBUG ((DEBUG_INFO , "%a: Result=%d (%a) \n" , __func__ , TestStatus , ( TestStatus == UNIT_TEST_PASSED ) ? "PASSED" : "FAILED" ));
192224
225+ UT_ASSERT_STATUS_EQUAL (TestStatus , UNIT_TEST_PASSED );
193226 return TestStatus ;
194227} // CheckExcludedRegions()
195228
196229/**
197- Test to verify that all SMMUv3 units found in the IORT have translation enabled.
198- This checks:
230+ Test to verify that all SMMUv3 units found in the IORT are configured to be
231+ DMA-safe.
232+
233+ For each SMMUv3 unit, if translation is enabled (CR0.SMMUEN == 1) this checks:
199234 1) CR0 register's SMMUEN bit to confirm the SMMU is actively translating
200235 2) CR0 register's CMDQEN bit to confirm the command queue is enabled
201236 3) CR0 register's EVTQEN bit to confirm the event queue is enabled
202237 4) STRTAB_BASE register is not NULL (stream table must be configured)
203238 5) GERROR register is 0 (no global errors)
204239
240+ If translation is not enabled (CR0.SMMUEN == 0), the SMMU is still considered
241+ DMA-safe (and the checks above are skipped) when either:
242+ a) It is configured for global abort (GBPA.ABORT == 1), so all DMA is aborted, or
243+ b) It is in global bypass but has a Reserved Memory Range (RMR) associated with
244+ it in the IORT (the RMR describes memory expected to bypass translation).
245+ Otherwise the SMMU is considered unsafe and the test fails.
246+
205247 @param[in] Context The unit test context (not used).
206248
207- @retval UNIT_TEST_PASSED All SMMU units are properly configured.
249+ @retval UNIT_TEST_PASSED All SMMU units are properly configured.
208250 @retval UNIT_TEST_ERROR_TEST_FAILED An SMMU unit is not properly configured.
209251**/
210252UNIT_TEST_STATUS
@@ -225,6 +267,8 @@ CheckIOMMUEnabled (
225267 UINT64 StrTabBase ;
226268 UINT64 StrTabBaseAddr ;
227269 UINT32 GError ;
270+ UINT32 GbpaValue ;
271+ UINT32 AbortBit ;
228272 UNIT_TEST_STATUS TestStatus ;
229273
230274 //
@@ -253,6 +297,7 @@ CheckIOMMUEnabled (
253297
254298 //
255299 // Step 4: For each SMMU, check:
300+ // - SMMU GBPA Set (GBPA.ABORT == 1), or:
256301 // - SMMU Enable bit (SMMUEN) in CR0 register
257302 // - Command Queue Enable bit (CMDQEN) in CR0 register
258303 // - Event Queue Enable bit (EVTQEN) in CR0 register
@@ -277,9 +322,41 @@ CheckIOMMUEnabled (
277322 UT_LOG_INFO ("SMMUEN bit: %d\n" , SmmuEnBit );
278323 DEBUG ((DEBUG_INFO , "%a: SMMUEN bit: %d\n" , __func__ , SmmuEnBit ));
279324 if (SmmuEnBit == 0 ) {
280- UT_LOG_ERROR ("SMMUEN bit is disabled for SMMUv3 at base address 0x%lX\n" , SmmuBaseAddresses [Iterator ]);
281- DEBUG ((DEBUG_ERROR , "%a: SMMUEN bit is disabled for SMMUv3 at base address 0x%lX\n" , __func__ , SmmuBaseAddresses [Iterator ]));
325+ //
326+ // SMMU translation is not enabled. The SMMU is still DMA-safe if it is
327+ // configured for global abort (GBPA.ABORT == 1).
328+ //
329+ GbpaValue = MmioRead32 ((UINTN )(SmmuBaseAddresses [Iterator ] + SMMU_GBPA ));
330+ AbortBit = GbpaValue & SMMU_GBPA_ABORT ;
331+ UT_LOG_INFO ("GBPA Register Value: 0x%X, ABORT bit: %d\n" , GbpaValue , AbortBit ? 1 : 0 );
332+ DEBUG ((DEBUG_INFO , "%a: GBPA Register Value: 0x%X, ABORT bit: %d\n" , __func__ , GbpaValue , AbortBit ? 1 : 0 ));
333+
334+ if (AbortBit != 0 ) {
335+ //
336+ // Global abort is set: all DMA is aborted, so this SMMU is DMA-safe.
337+ // Skip the remaining translation-related checks for this SMMU.
338+ //
339+ 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 ]);
340+ 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 ]));
341+ continue ;
342+ }
343+
344+ //
345+ // SMMU is in global bypass (not abort) and not enabled. This is permitted
346+ // only if the SMMU has a Reserved Memory Range (RMR) associated with it in
347+ // the IORT, since the RMR describes memory that is expected to bypass
348+ // translation.
349+ //
350+ if (SmmuHasAssociatedRmr (IortTable , SmmuBaseAddresses [Iterator ])) {
351+ UT_LOG_INFO ("SMMUEN is disabled and SMMU is in global bypass, but an RMR is associated with SMMUv3 at base address 0x%lX. This is permitted.\n" , SmmuBaseAddresses [Iterator ]);
352+ DEBUG ((DEBUG_INFO , "%a: SMMUEN is disabled and SMMU is in global bypass, but an RMR is associated with SMMUv3 at base address 0x%lX. This is permitted.\n" , __func__ , SmmuBaseAddresses [Iterator ]));
353+ continue ;
354+ }
355+
356+ UT_LOG_ERROR ("SMMUEN bit is disabled, global abort is not set, and no RMR is associated for SMMUv3 at base address 0x%lX\n" , SmmuBaseAddresses [Iterator ]);
357+ DEBUG ((DEBUG_ERROR , "%a: SMMUEN bit is disabled, global abort is not set, and no RMR is associated for SMMUv3 at base address 0x%lX\n" , __func__ , SmmuBaseAddresses [Iterator ]));
282358 TestStatus = UNIT_TEST_ERROR_TEST_FAILED ;
359+ continue ;
283360 }
284361
285362 //
@@ -337,8 +414,9 @@ CheckIOMMUEnabled (
337414 }
338415 }
339416
340- UT_LOG_INFO ("%a: Result=%d\n" , __func__ , TestStatus );
341- DEBUG ((DEBUG_INFO , "%a: Result=%d\n" , __func__ , TestStatus ));
417+ UT_LOG_INFO ("%a: Result=%d (%a) \n" , __func__ , TestStatus , ( TestStatus == UNIT_TEST_PASSED ) ? "PASSED" : "FAILED" );
418+ DEBUG ((DEBUG_INFO , "%a: Result=%d (%a) \n" , __func__ , TestStatus , ( TestStatus == UNIT_TEST_PASSED ) ? "PASSED" : "FAILED" ));
342419
420+ UT_ASSERT_STATUS_EQUAL (TestStatus , UNIT_TEST_PASSED );
343421 return TestStatus ;
344422} // CheckIOMMUEnabled()
0 commit comments