Skip to content

Commit 7a34ce7

Browse files
committed
update test conditions
1 parent c2d89b3 commit 7a34ce7

5 files changed

Lines changed: 204 additions & 39 deletions

File tree

UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/DMAProtectionUnitTestApp.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,10 +317,8 @@ CheckBMETeardown (
317317
for (i = 0; i < (PreVarSize)/(sizeof (BOOLEAN)); i++) {
318318
// BME Enabled before exit boot services
319319
UT_LOG_INFO (PreBuffer[i] ? "Pre-EBS BME %d: True\n" : "Pre-EBS BME %d: False\n", i);
320-
DEBUG ((DEBUG_INFO, PreBuffer[i] ? "%a: Pre-EBS BME %d: True\n" : "%a: Pre-EBS BME %d: False\n", __func__, i));
321320
// BME Disabled after exit boot services
322321
UT_LOG_INFO (PostBuffer[i] ? "Post-EBS BME %d: True\n" : "Post-EBS BME %d: False\n", i);
323-
DEBUG ((DEBUG_INFO, PostBuffer[i] ? "%a: Post-EBS BME %d: True\n" : "%a: Post-EBS BME %d: False\n", __func__, i));
324322
UT_ASSERT_FALSE (PostBuffer[i]);
325323
}
326324

@@ -351,7 +349,6 @@ CheckBMETeardown (
351349
}
352350

353351
UT_LOG_INFO ("PASSED: BME test.\n");
354-
DEBUG ((DEBUG_INFO, "PASSED: BME test.\n"));
355352

356353
return UNIT_TEST_PASSED;
357354
} // CheckBMETeardown()

UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Shell based UEFI unit test based off of the UnitTestFrameworkPkg that test for:
1616
3. All excluded regions are set as (EfiReservedMemoryType or EfiACPIMemoryNVS) for IVRS.
1717
4. Bus mastering enabled (BME) is disabled on ExitBootServices. Because we can no longer write to file after ExitBootServices
1818
a variable is used to store the test state and the machine.
19+
5. For ARM SMMUv3, each SMMU unit in the IORT has translation enabled (or is otherwise DMA-safe via GBPA abort / IORT RMR).
1920

2021
Note: this unit test requires a restart to finish its testing. If you plan to use this unit test in automation make sure
2122
to set up your startup.nsh script properly.

UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DMAProtectionTestArch.c

Lines changed: 114 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
/** @file -- DMAProtectionTestArch.c
22
33
This 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).
59
2) Check that Command Queue is enabled (CMDQEN bit in CR0)
610
3) Check that Event Queue is enabled (EVTQEN bit in CR0)
711
4) Check that Stream Table Base is configured (STRTAB_BASE is not NULL)
812
5) 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
1117
Copyright (c) Microsoft Corporation. All rights reserved.
1218
SPDX-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
**/
4256
UNIT_TEST_STATUS
4357
EFIAPI
@@ -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
**/
210252
UNIT_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()

UefiTestingPkg/AuditTests/DMAProtectionAudit/UEFI/SMMU/DmaProtection.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
2323
//
2424
#define SMMU_CR0 0x0020
2525
#define SMMU_CR0ACK 0x0024
26+
#define SMMU_GBPA 0x0044
2627
#define SMMU_GERROR 0x0060
2728
#define SMMU_STRTAB_BASE 0x0080
2829

@@ -33,6 +34,11 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
3334
#define SMMU_CR0_EVTQEN BIT2 // Event Queue Enable bit
3435
#define SMMU_CR0_CMDQEN BIT3 // Command Queue Enable bit
3536

37+
//
38+
// SMMUv3 GBPA Register Bits
39+
//
40+
#define SMMU_GBPA_ABORT BIT20 // Global Bypass Abort bit (abort all DMA when SMMUEN == 0)
41+
3642
//
3743
// STRTAB_BASE lower bits mask (bits [5:0] are reserved/config)
3844
//
@@ -100,4 +106,22 @@ GetIortAcpiTableRmrList (
100106
IN EFI_ACPI_DESCRIPTION_HEADER *IortTable
101107
);
102108

109+
/**
110+
Determine whether the given SMMUv3 (identified by its base address) has at
111+
least one Reserved Memory Range (RMR) node associated with it via the RMR
112+
node's ID mappings.
113+
114+
@param[in] IortTable Pointer to the IORT table.
115+
@param[in] SmmuBase Base address of the SMMUv3 to check.
116+
117+
@retval TRUE At least one RMR node references the specified SMMUv3.
118+
@retval FALSE No RMR node references the specified SMMUv3, or inputs invalid.
119+
**/
120+
BOOLEAN
121+
EFIAPI
122+
SmmuHasAssociatedRmr (
123+
IN EFI_ACPI_DESCRIPTION_HEADER *IortTable,
124+
IN UINT64 SmmuBase
125+
);
126+
103127
#endif // _DMA_PROTECTION_H_

0 commit comments

Comments
 (0)