diff --git a/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTest.c b/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTest.c new file mode 100644 index 0000000000..123e1c741a --- /dev/null +++ b/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTest.c @@ -0,0 +1,1139 @@ +/** @file + Unit tests of the MtrrLib instance of the MtrrLib class + + Copyright (c) 2020, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "MtrrLibUnitTest.h" + +STATIC CONST MTRR_LIB_SYSTEM_PARAMETER mDefaultSystemParameter = { + 42, TRUE, TRUE, CacheUncacheable, 12 +}; + +STATIC MTRR_LIB_SYSTEM_PARAMETER mSystemParameters[] = { + { 38, TRUE, TRUE, CacheUncacheable, 12 }, + { 38, TRUE, TRUE, CacheWriteBack, 12 }, + { 38, TRUE, TRUE, CacheWriteThrough, 12 }, + { 38, TRUE, TRUE, CacheWriteProtected, 12 }, + { 38, TRUE, TRUE, CacheWriteCombining, 12 }, + + { 42, TRUE, TRUE, CacheUncacheable, 12 }, + { 42, TRUE, TRUE, CacheWriteBack, 12 }, + { 42, TRUE, TRUE, CacheWriteThrough, 12 }, + { 42, TRUE, TRUE, CacheWriteProtected, 12 }, + { 42, TRUE, TRUE, CacheWriteCombining, 12 }, + + { 48, TRUE, TRUE, CacheUncacheable, 12 }, + { 48, TRUE, TRUE, CacheWriteBack, 12 }, + { 48, TRUE, TRUE, CacheWriteThrough, 12 }, + { 48, TRUE, TRUE, CacheWriteProtected, 12 }, + { 48, TRUE, TRUE, CacheWriteCombining, 12 }, +}; + +UINT32 mFixedMtrrsIndex[] = { + MSR_IA32_MTRR_FIX64K_00000, + MSR_IA32_MTRR_FIX16K_80000, + MSR_IA32_MTRR_FIX16K_A0000, + MSR_IA32_MTRR_FIX4K_C0000, + MSR_IA32_MTRR_FIX4K_C8000, + MSR_IA32_MTRR_FIX4K_D0000, + MSR_IA32_MTRR_FIX4K_D8000, + MSR_IA32_MTRR_FIX4K_E0000, + MSR_IA32_MTRR_FIX4K_E8000, + MSR_IA32_MTRR_FIX4K_F0000, + MSR_IA32_MTRR_FIX4K_F8000 +}; +STATIC_ASSERT ( + (ARRAY_SIZE (mFixedMtrrsIndex) == MTRR_NUMBER_OF_FIXED_MTRR), + "gFixedMtrrIndex does NOT contain all the fixed MTRRs!" + ); + +// +// Context structure to be used for most of the test cases. +// +typedef struct { + CONST MTRR_LIB_SYSTEM_PARAMETER *SystemParameter; +} MTRR_LIB_TEST_CONTEXT; + +// +// Context structure to be used for GetFirmwareVariableMtrrCount() test. +// +typedef struct { + UINT32 NumberOfReservedVariableMtrrs; + CONST MTRR_LIB_SYSTEM_PARAMETER *SystemParameter; +} MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT; + +STATIC CHAR8 *mCacheDescription[] = { "UC", "WC", "N/A", "N/A", "WT", "WP", "WB" }; + +/** + Compare the actual memory ranges against expected memory ranges and return PASS when they match. + + @param ExpectedMemoryRanges Expected memory ranges. + @param ExpectedMemoryRangeCount Count of expected memory ranges. + @param ActualRanges Actual memory ranges. + @param ActualRangeCount Count of actual memory ranges. + + @retval UNIT_TEST_PASSED Test passed. + @retval others Test failed. +**/ +UNIT_TEST_STATUS +VerifyMemoryRanges ( + IN MTRR_MEMORY_RANGE *ExpectedMemoryRanges, + IN UINTN ExpectedMemoryRangeCount, + IN MTRR_MEMORY_RANGE *ActualRanges, + IN UINTN ActualRangeCount + ) +{ + UINTN Index; + UT_ASSERT_EQUAL (ExpectedMemoryRangeCount, ActualRangeCount); + for (Index = 0; Index < ExpectedMemoryRangeCount; Index++) { + UT_ASSERT_EQUAL (ExpectedMemoryRanges[Index].BaseAddress, ActualRanges[Index].BaseAddress); + UT_ASSERT_EQUAL (ExpectedMemoryRanges[Index].Length, ActualRanges[Index].Length); + UT_ASSERT_EQUAL (ExpectedMemoryRanges[Index].Type, ActualRanges[Index].Type); + } + + return UNIT_TEST_PASSED; +} + +/** + Dump the memory ranges. + + @param Ranges Memory ranges to dump. + @param RangeCount Count of memory ranges. +**/ +VOID +DumpMemoryRanges ( + MTRR_MEMORY_RANGE *Ranges, + UINTN RangeCount + ) +{ + UINTN Index; + for (Index = 0; Index < RangeCount; Index++) { + UT_LOG_INFO ("\t{ 0x%016llx, 0x%016llx, %a },\n", Ranges[Index].BaseAddress, Ranges[Index].Length, mCacheDescription[Ranges[Index].Type]); + } +} + +/** +**/ + +/** + Generate random count of MTRRs for each cache type. + + @param TotalCount Total MTRR count. + @param UcCount Return count of Uncacheable type. + @param WtCount Return count of Write Through type. + @param WbCount Return count of Write Back type. + @param WpCount Return count of Write Protected type. + @param WcCount Return count of Write Combining type. +**/ +VOID +GenerateRandomMemoryTypeCombination ( + IN UINT32 TotalCount, + OUT UINT32 *UcCount, + OUT UINT32 *WtCount, + OUT UINT32 *WbCount, + OUT UINT32 *WpCount, + OUT UINT32 *WcCount + ) +{ + UINTN Index; + UINT32 TotalMtrrCount; + UINT32 *CountPerType[5]; + + CountPerType[0] = UcCount; + CountPerType[1] = WtCount; + CountPerType[2] = WbCount; + CountPerType[3] = WpCount; + CountPerType[4] = WcCount; + + // + // Initialize the count of each cache type to 0. + // + for (Index = 0; Index < ARRAY_SIZE (CountPerType); Index++) { + *(CountPerType[Index]) = 0; + } + + // + // Pick a random count of MTRRs + // + TotalMtrrCount = Random32 (1, TotalCount); + for (Index = 0; Index < TotalMtrrCount; Index++) { + // + // For each of them, pick a random cache type. + // + (*(CountPerType[Random32 (0, ARRAY_SIZE (CountPerType) - 1)]))++; + } +} + +/** + Unit test of MtrrLib service MtrrSetMemoryAttribute() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrSetMemoryAttributesInMtrrSettings ( + IN UNIT_TEST_CONTEXT Context + ) +{ + CONST MTRR_LIB_SYSTEM_PARAMETER *SystemParameter; + RETURN_STATUS Status; + UINT32 UcCount; + UINT32 WtCount; + UINT32 WbCount; + UINT32 WpCount; + UINT32 WcCount; + + UINT32 MtrrIndex; + UINT8 *Scratch; + UINTN ScratchSize; + MTRR_SETTINGS LocalMtrrs; + + MTRR_MEMORY_RANGE RawMtrrRange[MTRR_NUMBER_OF_VARIABLE_MTRR]; + MTRR_MEMORY_RANGE ExpectedMemoryRanges[MTRR_NUMBER_OF_FIXED_MTRR * sizeof (UINT64) + 2 * MTRR_NUMBER_OF_VARIABLE_MTRR + 1]; + UINT32 ExpectedVariableMtrrUsage; + UINTN ExpectedMemoryRangesCount; + + MTRR_MEMORY_RANGE ActualMemoryRanges[MTRR_NUMBER_OF_FIXED_MTRR * sizeof (UINT64) + 2 * MTRR_NUMBER_OF_VARIABLE_MTRR + 1]; + UINT32 ActualVariableMtrrUsage; + UINTN ActualMemoryRangesCount; + + MTRR_SETTINGS *Mtrrs[2]; + + SystemParameter = (MTRR_LIB_SYSTEM_PARAMETER *) Context; + GenerateRandomMemoryTypeCombination ( + SystemParameter->VariableMtrrCount - PatchPcdGet32 (PcdCpuNumberOfReservedVariableMtrrs), + &UcCount, &WtCount, &WbCount, &WpCount, &WcCount + ); + GenerateValidAndConfigurableMtrrPairs ( + SystemParameter->PhysicalAddressBits, RawMtrrRange, + UcCount, WtCount, WbCount, WpCount, WcCount + ); + + ExpectedVariableMtrrUsage = UcCount + WtCount + WbCount + WpCount + WcCount; + ExpectedMemoryRangesCount = ARRAY_SIZE (ExpectedMemoryRanges); + GetEffectiveMemoryRanges ( + SystemParameter->DefaultCacheType, + SystemParameter->PhysicalAddressBits, + RawMtrrRange, ExpectedVariableMtrrUsage, + ExpectedMemoryRanges, &ExpectedMemoryRangesCount + ); + + UT_LOG_INFO ( + "Total MTRR [%d]: UC=%d, WT=%d, WB=%d, WP=%d, WC=%d\n", + ExpectedVariableMtrrUsage, UcCount, WtCount, WbCount, WpCount, WcCount + ); + UT_LOG_INFO ("--- Expected Memory Ranges [%d] ---\n", ExpectedMemoryRangesCount); + DumpMemoryRanges (ExpectedMemoryRanges, ExpectedMemoryRangesCount); + + // + // Default cache type is always an INPUT + // + ZeroMem (&LocalMtrrs, sizeof (LocalMtrrs)); + LocalMtrrs.MtrrDefType = MtrrGetDefaultMemoryType (); + ScratchSize = SCRATCH_BUFFER_SIZE; + Mtrrs[0] = &LocalMtrrs; + Mtrrs[1] = NULL; + + for (MtrrIndex = 0; MtrrIndex < ARRAY_SIZE (Mtrrs); MtrrIndex++) { + Scratch = calloc (ScratchSize, sizeof (UINT8)); + Status = MtrrSetMemoryAttributesInMtrrSettings (Mtrrs[MtrrIndex], Scratch, &ScratchSize, ExpectedMemoryRanges, ExpectedMemoryRangesCount); + if (Status == RETURN_BUFFER_TOO_SMALL) { + Scratch = realloc (Scratch, ScratchSize); + Status = MtrrSetMemoryAttributesInMtrrSettings (Mtrrs[MtrrIndex], Scratch, &ScratchSize, ExpectedMemoryRanges, ExpectedMemoryRangesCount); + } + UT_ASSERT_STATUS_EQUAL (Status, RETURN_SUCCESS); + + if (Mtrrs[MtrrIndex] == NULL) { + ZeroMem (&LocalMtrrs, sizeof (LocalMtrrs)); + MtrrGetAllMtrrs (&LocalMtrrs); + } + ActualMemoryRangesCount = ARRAY_SIZE (ActualMemoryRanges); + CollectTestResult ( + SystemParameter->DefaultCacheType, SystemParameter->PhysicalAddressBits, SystemParameter->VariableMtrrCount, + &LocalMtrrs, ActualMemoryRanges, &ActualMemoryRangesCount, &ActualVariableMtrrUsage + ); + + UT_LOG_INFO ("--- Actual Memory Ranges [%d] ---\n", ActualMemoryRangesCount); + DumpMemoryRanges (ActualMemoryRanges, ActualMemoryRangesCount); + VerifyMemoryRanges (ExpectedMemoryRanges, ExpectedMemoryRangesCount, ActualMemoryRanges, ActualMemoryRangesCount); + UT_ASSERT_TRUE (ExpectedVariableMtrrUsage >= ActualVariableMtrrUsage); + + ZeroMem (&LocalMtrrs, sizeof (LocalMtrrs)); + } + + free (Scratch); + + return UNIT_TEST_PASSED; +} + +/** + Test routine to check whether invalid base/size can be rejected. + + @param Context Pointer to MTRR_LIB_SYSTEM_PARAMETER. + + @return Test status. +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestInvalidMemoryLayouts ( + IN UNIT_TEST_CONTEXT Context + ) +{ + CONST MTRR_LIB_SYSTEM_PARAMETER *SystemParameter; + MTRR_MEMORY_RANGE Ranges[MTRR_NUMBER_OF_VARIABLE_MTRR * 2 + 1]; + UINTN RangeCount; + UINT64 MaxAddress; + UINT32 Index; + UINT64 BaseAddress; + UINT64 Length; + RETURN_STATUS Status; + UINTN ScratchSize; + + SystemParameter = (MTRR_LIB_SYSTEM_PARAMETER *) Context; + + RangeCount = Random32 (1, ARRAY_SIZE (Ranges)); + MaxAddress = 1ull << SystemParameter->PhysicalAddressBits; + + for (Index = 0; Index < RangeCount; Index++) { + do { + BaseAddress = Random64 (0, MaxAddress); + Length = Random64 (1, MaxAddress - BaseAddress); + } while (((BaseAddress & 0xFFF) == 0) || ((Length & 0xFFF) == 0)); + + Ranges[Index].BaseAddress = BaseAddress; + Ranges[Index].Length = Length; + Ranges[Index].Type = GenerateRandomCacheType (); + + Status = MtrrSetMemoryAttribute ( + Ranges[Index].BaseAddress, Ranges[Index].Length, Ranges[Index].Type + ); + UT_ASSERT_TRUE (RETURN_ERROR (Status)); + } + + ScratchSize = 0; + Status = MtrrSetMemoryAttributesInMtrrSettings (NULL, NULL, &ScratchSize, Ranges, RangeCount); + UT_ASSERT_TRUE (RETURN_ERROR (Status)); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service IsMtrrSupported() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestIsMtrrSupported ( + IN UNIT_TEST_CONTEXT Context + ) +{ + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + MTRR_LIB_TEST_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_TEST_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + // + // MTRR capability off in CPUID leaf. + // + SystemParameter.MtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + UT_ASSERT_FALSE (IsMtrrSupported ()); + + // + // MTRR capability on in CPUID leaf, but no variable or fixed MTRRs. + // + SystemParameter.MtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = 0; + SystemParameter.FixedMtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + UT_ASSERT_FALSE (IsMtrrSupported ()); + + // + // MTRR capability on in CPUID leaf, but no variable MTRRs. + // + SystemParameter.MtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = 0; + SystemParameter.FixedMtrrSupported = TRUE; + InitializeMtrrRegs (&SystemParameter); + UT_ASSERT_FALSE (IsMtrrSupported ()); + + // + // MTRR capability on in CPUID leaf, but no fixed MTRRs. + // + SystemParameter.MtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = 7; + SystemParameter.FixedMtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + UT_ASSERT_FALSE (IsMtrrSupported ()); + + // + // MTRR capability on in CPUID leaf with both variable and fixed MTRRs. + // + SystemParameter.MtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = 7; + SystemParameter.FixedMtrrSupported = TRUE; + InitializeMtrrRegs (&SystemParameter); + UT_ASSERT_TRUE (IsMtrrSupported ()); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service GetVariableMtrrCount() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestGetVariableMtrrCount ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT32 Result; + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + MTRR_LIB_TEST_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_TEST_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + // + // If MTRR capability off in CPUID leaf, then the count is always 0. + // + SystemParameter.MtrrSupported = FALSE; + for (SystemParameter.VariableMtrrCount = 1; SystemParameter.VariableMtrrCount <= MTRR_NUMBER_OF_VARIABLE_MTRR; SystemParameter.VariableMtrrCount++) { + InitializeMtrrRegs (&SystemParameter); + Result = GetVariableMtrrCount (); + UT_ASSERT_EQUAL (Result, 0); + } + + // + // Try all supported variable MTRR counts. + // If variable MTRR count is > MTRR_NUMBER_OF_VARIABLE_MTRR, then an ASSERT() + // is generated. + // + SystemParameter.MtrrSupported = TRUE; + for (SystemParameter.VariableMtrrCount = 1; SystemParameter.VariableMtrrCount <= MTRR_NUMBER_OF_VARIABLE_MTRR; SystemParameter.VariableMtrrCount++) { + InitializeMtrrRegs (&SystemParameter); + Result = GetVariableMtrrCount (); + UT_ASSERT_EQUAL (Result, SystemParameter.VariableMtrrCount); + } + + // + // Expect ASSERT() if variable MTRR count is > MTRR_NUMBER_OF_VARIABLE_MTRR + // + SystemParameter.VariableMtrrCount = MTRR_NUMBER_OF_VARIABLE_MTRR + 1; + InitializeMtrrRegs (&SystemParameter); + UT_EXPECT_ASSERT_FAILURE (GetVariableMtrrCount (), NULL); + + SystemParameter.MtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = MAX_UINT8; + InitializeMtrrRegs (&SystemParameter); + UT_EXPECT_ASSERT_FAILURE (GetVariableMtrrCount (), NULL); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service GetFirmwareVariableMtrrCount() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestGetFirmwareVariableMtrrCount ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT32 Result; + UINT32 ReservedMtrrs; + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + + InitializeMtrrRegs (&SystemParameter); + // + // Positive test cases for VCNT = 10 and Reserved PCD in range 0..10 + // + for (ReservedMtrrs = 0; ReservedMtrrs <= SystemParameter.VariableMtrrCount; ReservedMtrrs++) { + PatchPcdSet32 (PcdCpuNumberOfReservedVariableMtrrs, ReservedMtrrs); + Result = GetFirmwareVariableMtrrCount (); + UT_ASSERT_EQUAL (Result, SystemParameter.VariableMtrrCount - ReservedMtrrs); + } + + // + // Negative test cases when Reserved PCD is larger than VCNT + // + for (ReservedMtrrs = SystemParameter.VariableMtrrCount + 1; ReservedMtrrs <= 255; ReservedMtrrs++) { + PatchPcdSet32 (PcdCpuNumberOfReservedVariableMtrrs, ReservedMtrrs); + Result = GetFirmwareVariableMtrrCount (); + UT_ASSERT_EQUAL (Result, 0); + } + + // + // Negative test cases when Reserved PCD is larger than VCNT + // + PatchPcdSet32 (PcdCpuNumberOfReservedVariableMtrrs, MAX_UINT32); + Result = GetFirmwareVariableMtrrCount (); + UT_ASSERT_EQUAL (Result, 0); + + // + // Negative test case when MTRRs are not supported + // + SystemParameter.MtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + PatchPcdSet32 (PcdCpuNumberOfReservedVariableMtrrs, 2); + Result = GetFirmwareVariableMtrrCount (); + UT_ASSERT_EQUAL (Result, 0); + + // + // Negative test case when Fixed MTRRs are not supported + // + SystemParameter.MtrrSupported = TRUE; + SystemParameter.FixedMtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + PatchPcdSet32 (PcdCpuNumberOfReservedVariableMtrrs, 2); + Result = GetFirmwareVariableMtrrCount (); + UT_ASSERT_EQUAL (Result, 0); + + // + // Expect ASSERT() if variable MTRR count is > MTRR_NUMBER_OF_VARIABLE_MTRR + // + SystemParameter.FixedMtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = MTRR_NUMBER_OF_VARIABLE_MTRR + 1; + InitializeMtrrRegs (&SystemParameter); + UT_EXPECT_ASSERT_FAILURE (GetFirmwareVariableMtrrCount (), NULL); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrGetMemoryAttribute() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrGetMemoryAttribute ( + IN UNIT_TEST_CONTEXT Context + ) +{ + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrGetFixedMtrr() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrGetFixedMtrr ( + IN UNIT_TEST_CONTEXT Context + ) +{ + MTRR_FIXED_SETTINGS *Result; + MTRR_FIXED_SETTINGS ExpectedFixedSettings; + MTRR_FIXED_SETTINGS FixedSettings; + UINTN Index; + UINTN MsrIndex; + UINTN ByteIndex; + UINT64 MsrValue; + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + MTRR_LIB_TEST_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_TEST_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + InitializeMtrrRegs (&SystemParameter); + // + // Set random cache type to different ranges under 1MB and make sure + // the fixed MTRR settings are expected. + // Try 100 times. + // + for (Index = 0; Index < 100; Index++) { + for (MsrIndex = 0; MsrIndex < ARRAY_SIZE (mFixedMtrrsIndex); MsrIndex++) { + MsrValue = 0; + for (ByteIndex = 0; ByteIndex < sizeof (UINT64); ByteIndex++) { + MsrValue = MsrValue | LShiftU64 (GenerateRandomCacheType (), ByteIndex * 8); + } + ExpectedFixedSettings.Mtrr[MsrIndex] = MsrValue; + AsmWriteMsr64 (mFixedMtrrsIndex[MsrIndex], MsrValue); + } + + Result = MtrrGetFixedMtrr (&FixedSettings); + UT_ASSERT_EQUAL (Result, &FixedSettings); + UT_ASSERT_MEM_EQUAL (&FixedSettings, &ExpectedFixedSettings, sizeof (FixedSettings)); + } + + // + // Negative test case when MTRRs are not supported + // + SystemParameter.MtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + + ZeroMem (&FixedSettings, sizeof (FixedSettings)); + ZeroMem (&ExpectedFixedSettings, sizeof (ExpectedFixedSettings)); + Result = MtrrGetFixedMtrr (&FixedSettings); + UT_ASSERT_EQUAL (Result, &FixedSettings); + UT_ASSERT_MEM_EQUAL (&ExpectedFixedSettings, &FixedSettings, sizeof (ExpectedFixedSettings)); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrGetAllMtrrs() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrGetAllMtrrs ( + IN UNIT_TEST_CONTEXT Context + ) +{ + MTRR_SETTINGS *Result; + MTRR_SETTINGS Mtrrs; + MTRR_SETTINGS ExpectedMtrrs; + MTRR_VARIABLE_SETTING VariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR]; + UINT32 Index; + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + MTRR_LIB_TEST_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_TEST_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + InitializeMtrrRegs (&SystemParameter); + + for (Index = 0; Index < SystemParameter.VariableMtrrCount; Index++) { + GenerateRandomMtrrPair (SystemParameter.PhysicalAddressBits, GenerateRandomCacheType (), &VariableMtrr[Index], NULL); + AsmWriteMsr64 (MSR_IA32_MTRR_PHYSBASE0 + (Index << 1), VariableMtrr[Index].Base); + AsmWriteMsr64 (MSR_IA32_MTRR_PHYSMASK0 + (Index << 1), VariableMtrr[Index].Mask); + } + Result = MtrrGetAllMtrrs (&Mtrrs); + UT_ASSERT_EQUAL (Result, &Mtrrs); + UT_ASSERT_MEM_EQUAL (Mtrrs.Variables.Mtrr, VariableMtrr, sizeof (MTRR_VARIABLE_SETTING) * SystemParameter.VariableMtrrCount); + + // + // Negative test case when MTRRs are not supported + // + ZeroMem (&ExpectedMtrrs, sizeof (ExpectedMtrrs)); + ZeroMem (&Mtrrs, sizeof (Mtrrs)); + + SystemParameter.MtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + Result = MtrrGetAllMtrrs (&Mtrrs); + UT_ASSERT_EQUAL (Result, &Mtrrs); + UT_ASSERT_MEM_EQUAL (&ExpectedMtrrs, &Mtrrs, sizeof (ExpectedMtrrs)); + + // + // Expect ASSERT() if variable MTRR count is > MTRR_NUMBER_OF_VARIABLE_MTRR + // + SystemParameter.MtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = MTRR_NUMBER_OF_VARIABLE_MTRR + 1; + InitializeMtrrRegs (&SystemParameter); + UT_EXPECT_ASSERT_FAILURE (MtrrGetAllMtrrs (&Mtrrs), NULL); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrSetAllMtrrs() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrSetAllMtrrs ( + IN UNIT_TEST_CONTEXT Context + ) +{ + MTRR_SETTINGS *Result; + MTRR_SETTINGS Mtrrs; + UINT32 Index; + MSR_IA32_MTRR_DEF_TYPE_REGISTER Default; + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + MTRR_LIB_TEST_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_TEST_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + InitializeMtrrRegs (&SystemParameter); + + Default.Uint64 = 0; + Default.Bits.E = 1; + Default.Bits.FE = 1; + Default.Bits.Type = GenerateRandomCacheType (); + + ZeroMem (&Mtrrs, sizeof (Mtrrs)); + Mtrrs.MtrrDefType = Default.Uint64; + for (Index = 0; Index < SystemParameter.VariableMtrrCount; Index++) { + GenerateRandomMtrrPair (SystemParameter.PhysicalAddressBits, GenerateRandomCacheType (), &Mtrrs.Variables.Mtrr[Index], NULL); + } + Result = MtrrSetAllMtrrs (&Mtrrs); + UT_ASSERT_EQUAL (Result, &Mtrrs); + + UT_ASSERT_EQUAL (AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE), Mtrrs.MtrrDefType); + for (Index = 0; Index < SystemParameter.VariableMtrrCount; Index++) { + UT_ASSERT_EQUAL (AsmReadMsr64 (MSR_IA32_MTRR_PHYSBASE0 + (Index << 1)), Mtrrs.Variables.Mtrr[Index].Base); + UT_ASSERT_EQUAL (AsmReadMsr64 (MSR_IA32_MTRR_PHYSMASK0 + (Index << 1)), Mtrrs.Variables.Mtrr[Index].Mask); + } + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrGetMemoryAttributeInVariableMtrr() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrGetMemoryAttributeInVariableMtrr ( + IN UNIT_TEST_CONTEXT Context + ) +{ + MTRR_LIB_TEST_CONTEXT *LocalContext; + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + UINT32 Result; + MTRR_VARIABLE_SETTING VariableSetting[MTRR_NUMBER_OF_VARIABLE_MTRR]; + VARIABLE_MTRR VariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR]; + UINT64 ValidMtrrBitsMask; + UINT64 ValidMtrrAddressMask; + UINT32 Index; + MSR_IA32_MTRR_PHYSBASE_REGISTER Base; + MSR_IA32_MTRR_PHYSMASK_REGISTER Mask; + + LocalContext = (MTRR_LIB_TEST_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + + InitializeMtrrRegs (&SystemParameter); + + ValidMtrrBitsMask = (1ull << SystemParameter.PhysicalAddressBits) - 1; + ValidMtrrAddressMask = ValidMtrrBitsMask & 0xfffffffffffff000ULL; + + for (Index = 0; Index < SystemParameter.VariableMtrrCount; Index++) { + GenerateRandomMtrrPair (SystemParameter.PhysicalAddressBits, GenerateRandomCacheType (), &VariableSetting[Index], NULL); + AsmWriteMsr64 (MSR_IA32_MTRR_PHYSBASE0 + (Index << 1), VariableSetting[Index].Base); + AsmWriteMsr64 (MSR_IA32_MTRR_PHYSMASK0 + (Index << 1), VariableSetting[Index].Mask); + } + Result = MtrrGetMemoryAttributeInVariableMtrr (ValidMtrrBitsMask, ValidMtrrAddressMask, VariableMtrr); + UT_ASSERT_EQUAL (Result, SystemParameter.VariableMtrrCount); + + for (Index = 0; Index < SystemParameter.VariableMtrrCount; Index++) { + Base.Uint64 = VariableMtrr[Index].BaseAddress; + Base.Bits.Type = (UINT32) VariableMtrr[Index].Type; + UT_ASSERT_EQUAL (Base.Uint64, VariableSetting[Index].Base); + + Mask.Uint64 = ~(VariableMtrr[Index].Length - 1) & ValidMtrrBitsMask; + Mask.Bits.V = 1; + UT_ASSERT_EQUAL (Mask.Uint64, VariableSetting[Index].Mask); + } + + // + // Negative test case when MTRRs are not supported + // + SystemParameter.MtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + Result = MtrrGetMemoryAttributeInVariableMtrr (ValidMtrrBitsMask, ValidMtrrAddressMask, VariableMtrr); + UT_ASSERT_EQUAL (Result, 0); + + // + // Expect ASSERT() if variable MTRR count is > MTRR_NUMBER_OF_VARIABLE_MTRR + // + SystemParameter.MtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = MTRR_NUMBER_OF_VARIABLE_MTRR + 1; + InitializeMtrrRegs (&SystemParameter); + UT_EXPECT_ASSERT_FAILURE (MtrrGetMemoryAttributeInVariableMtrr (ValidMtrrBitsMask, ValidMtrrAddressMask, VariableMtrr), NULL); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrDebugPrintAllMtrrs() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrDebugPrintAllMtrrs ( + IN UNIT_TEST_CONTEXT Context + ) +{ + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrGetDefaultMemoryType(). + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrGetDefaultMemoryType ( + IN UNIT_TEST_CONTEXT Context + ) +{ + MTRR_LIB_TEST_CONTEXT *LocalContext; + UINTN Index; + MTRR_MEMORY_CACHE_TYPE Result; + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + MTRR_MEMORY_CACHE_TYPE CacheType[5]; + + CacheType[0] = CacheUncacheable; + CacheType[1] = CacheWriteCombining; + CacheType[2] = CacheWriteThrough; + CacheType[3] = CacheWriteProtected; + CacheType[4] = CacheWriteBack; + + LocalContext = (MTRR_LIB_TEST_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + // + // If MTRRs are supported, then always return the cache type in the MSR + // MSR_IA32_MTRR_DEF_TYPE + // + for (Index = 0; Index < ARRAY_SIZE (CacheType); Index++) { + SystemParameter.DefaultCacheType = CacheType[Index]; + InitializeMtrrRegs (&SystemParameter); + Result = MtrrGetDefaultMemoryType (); + UT_ASSERT_EQUAL (Result, SystemParameter.DefaultCacheType); + } + + // + // If MTRRs are not supported, then always return CacheUncacheable + // + SystemParameter.MtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + Result = MtrrGetDefaultMemoryType (); + UT_ASSERT_EQUAL (Result, CacheUncacheable); + + SystemParameter.MtrrSupported = TRUE; + SystemParameter.FixedMtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + Result = MtrrGetDefaultMemoryType (); + UT_ASSERT_EQUAL (Result, CacheUncacheable); + + SystemParameter.MtrrSupported = TRUE; + SystemParameter.FixedMtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = 0; + InitializeMtrrRegs (&SystemParameter); + Result = MtrrGetDefaultMemoryType (); + UT_ASSERT_EQUAL (Result, CacheUncacheable); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrSetMemoryAttributeInMtrrSettings(). + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrSetMemoryAttributeInMtrrSettings ( + IN UNIT_TEST_CONTEXT Context + ) +{ + CONST MTRR_LIB_SYSTEM_PARAMETER *SystemParameter; + RETURN_STATUS Status; + UINT32 UcCount; + UINT32 WtCount; + UINT32 WbCount; + UINT32 WpCount; + UINT32 WcCount; + + UINTN MtrrIndex; + UINTN Index; + MTRR_SETTINGS LocalMtrrs; + + MTRR_MEMORY_RANGE RawMtrrRange[MTRR_NUMBER_OF_VARIABLE_MTRR]; + MTRR_MEMORY_RANGE ExpectedMemoryRanges[MTRR_NUMBER_OF_FIXED_MTRR * sizeof (UINT64) + 2 * MTRR_NUMBER_OF_VARIABLE_MTRR + 1]; + UINT32 ExpectedVariableMtrrUsage; + UINTN ExpectedMemoryRangesCount; + + MTRR_MEMORY_RANGE ActualMemoryRanges[MTRR_NUMBER_OF_FIXED_MTRR * sizeof (UINT64) + 2 * MTRR_NUMBER_OF_VARIABLE_MTRR + 1]; + UINT32 ActualVariableMtrrUsage; + UINTN ActualMemoryRangesCount; + + MTRR_SETTINGS *Mtrrs[2]; + + SystemParameter = (MTRR_LIB_SYSTEM_PARAMETER *) Context; + GenerateRandomMemoryTypeCombination ( + SystemParameter->VariableMtrrCount - PatchPcdGet32 (PcdCpuNumberOfReservedVariableMtrrs), + &UcCount, &WtCount, &WbCount, &WpCount, &WcCount + ); + GenerateValidAndConfigurableMtrrPairs ( + SystemParameter->PhysicalAddressBits, RawMtrrRange, + UcCount, WtCount, WbCount, WpCount, WcCount + ); + + ExpectedVariableMtrrUsage = UcCount + WtCount + WbCount + WpCount + WcCount; + ExpectedMemoryRangesCount = ARRAY_SIZE (ExpectedMemoryRanges); + GetEffectiveMemoryRanges ( + SystemParameter->DefaultCacheType, + SystemParameter->PhysicalAddressBits, + RawMtrrRange, ExpectedVariableMtrrUsage, + ExpectedMemoryRanges, &ExpectedMemoryRangesCount + ); + + UT_LOG_INFO ("--- Expected Memory Ranges [%d] ---\n", ExpectedMemoryRangesCount); + DumpMemoryRanges (ExpectedMemoryRanges, ExpectedMemoryRangesCount); + // + // Default cache type is always an INPUT + // + ZeroMem (&LocalMtrrs, sizeof (LocalMtrrs)); + LocalMtrrs.MtrrDefType = MtrrGetDefaultMemoryType (); + Mtrrs[0] = &LocalMtrrs; + Mtrrs[1] = NULL; + + for (MtrrIndex = 0; MtrrIndex < ARRAY_SIZE (Mtrrs); MtrrIndex++) { + for (Index = 0; Index < ExpectedMemoryRangesCount; Index++) { + Status = MtrrSetMemoryAttributeInMtrrSettings ( + Mtrrs[MtrrIndex], + ExpectedMemoryRanges[Index].BaseAddress, + ExpectedMemoryRanges[Index].Length, + ExpectedMemoryRanges[Index].Type + ); + UT_ASSERT_TRUE (Status == RETURN_SUCCESS || Status == RETURN_OUT_OF_RESOURCES || Status == RETURN_BUFFER_TOO_SMALL); + if (Status == RETURN_OUT_OF_RESOURCES || Status == RETURN_BUFFER_TOO_SMALL) { + return UNIT_TEST_SKIPPED; + } + } + + if (Mtrrs[MtrrIndex] == NULL) { + ZeroMem (&LocalMtrrs, sizeof (LocalMtrrs)); + MtrrGetAllMtrrs (&LocalMtrrs); + } + ActualMemoryRangesCount = ARRAY_SIZE (ActualMemoryRanges); + CollectTestResult ( + SystemParameter->DefaultCacheType, SystemParameter->PhysicalAddressBits, SystemParameter->VariableMtrrCount, + &LocalMtrrs, ActualMemoryRanges, &ActualMemoryRangesCount, &ActualVariableMtrrUsage + ); + UT_LOG_INFO ("--- Actual Memory Ranges [%d] ---\n", ActualMemoryRangesCount); + DumpMemoryRanges (ActualMemoryRanges, ActualMemoryRangesCount); + VerifyMemoryRanges (ExpectedMemoryRanges, ExpectedMemoryRangesCount, ActualMemoryRanges, ActualMemoryRangesCount); + UT_ASSERT_TRUE (ExpectedVariableMtrrUsage >= ActualVariableMtrrUsage); + + ZeroMem (&LocalMtrrs, sizeof (LocalMtrrs)); + } + + return UNIT_TEST_PASSED; +} + + +/** + Prep routine for UnitTestGetFirmwareVariableMtrrCount(). + + @param Context Point to a UINT32 data to save the PcdCpuNumberOfReservedVariableMtrrs. +**/ +UNIT_TEST_STATUS +EFIAPI +SavePcdValue ( + UNIT_TEST_CONTEXT Context + ) +{ + MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT *) Context; + LocalContext->NumberOfReservedVariableMtrrs = PatchPcdGet32 (PcdCpuNumberOfReservedVariableMtrrs); + return UNIT_TEST_PASSED; +} + +/** + Clean up routine for UnitTestGetFirmwareVariableMtrrCount(). + + @param Context Point to a UINT32 data to save the PcdCpuNumberOfReservedVariableMtrrs. +**/ +VOID +EFIAPI +RestorePcdValue ( + UNIT_TEST_CONTEXT Context + ) +{ + MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT *) Context; + PatchPcdSet32 (PcdCpuNumberOfReservedVariableMtrrs, LocalContext->NumberOfReservedVariableMtrrs); +} + +/** + Initialize the unit test framework, suite, and unit tests for the + ResetSystemLib and run the ResetSystemLib unit test. + + @param Iteration Iteration of testing MtrrSetMemoryAttributeInMtrrSettings + and MtrrSetMemoryAttributesInMtrrSettings using random inputs. + + @retval EFI_SUCCESS All test cases were dispatched. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to + initialize the unit tests. +**/ +STATIC +EFI_STATUS +EFIAPI +UnitTestingEntry ( + UINTN Iteration + ) +{ + EFI_STATUS Status; + UNIT_TEST_FRAMEWORK_HANDLE Framework; + UNIT_TEST_SUITE_HANDLE MtrrApiTests; + UINTN Index; + UINTN SystemIndex; + MTRR_LIB_TEST_CONTEXT Context; + MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT GetFirmwareVariableMtrrCountContext; + + Context.SystemParameter = &mDefaultSystemParameter; + GetFirmwareVariableMtrrCountContext.SystemParameter = &mDefaultSystemParameter; + Framework = NULL; + + DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION)); + + // + // Setup the test framework for running the tests. + // + Status = InitUnitTestFramework (&Framework, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status)); + goto EXIT; + } + + // + // --------------Suite-----------Description--------------Name----------Function--------Pre---Post-------------------Context----------- + // + + // + // Populate the MtrrLib API Unit Test Suite. + // + Status = CreateUnitTestSuite (&MtrrApiTests, Framework, "MtrrLib API Tests", "MtrrLib.MtrrLib", NULL, NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for MtrrLib API Tests\n")); + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + AddTestCase (MtrrApiTests, "Test IsMtrrSupported", "MtrrSupported", UnitTestIsMtrrSupported, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test GetVariableMtrrCount", "GetVariableMtrrCount", UnitTestGetVariableMtrrCount, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test GetFirmwareVariableMtrrCount", "GetFirmwareVariableMtrrCount", UnitTestGetFirmwareVariableMtrrCount, SavePcdValue, RestorePcdValue, &GetFirmwareVariableMtrrCountContext); + AddTestCase (MtrrApiTests, "Test MtrrGetMemoryAttribute", "MtrrGetMemoryAttribute", UnitTestMtrrGetMemoryAttribute, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test MtrrGetFixedMtrr", "MtrrGetFixedMtrr", UnitTestMtrrGetFixedMtrr, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test MtrrGetAllMtrrs", "MtrrGetAllMtrrs", UnitTestMtrrGetAllMtrrs, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test MtrrSetAllMtrrs", "MtrrSetAllMtrrs", UnitTestMtrrSetAllMtrrs, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test MtrrGetMemoryAttributeInVariableMtrr", "MtrrGetMemoryAttributeInVariableMtrr", UnitTestMtrrGetMemoryAttributeInVariableMtrr, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test MtrrDebugPrintAllMtrrs", "MtrrDebugPrintAllMtrrs", UnitTestMtrrDebugPrintAllMtrrs, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test MtrrGetDefaultMemoryType", "MtrrGetDefaultMemoryType", UnitTestMtrrGetDefaultMemoryType, NULL, NULL, &Context); + + for (SystemIndex = 0; SystemIndex < ARRAY_SIZE (mSystemParameters); SystemIndex++) { + for (Index = 0; Index < Iteration; Index++) { + AddTestCase (MtrrApiTests, "Test InvalidMemoryLayouts", "InvalidMemoryLayouts", UnitTestInvalidMemoryLayouts, InitializeSystem, NULL, &mSystemParameters[SystemIndex]); + AddTestCase (MtrrApiTests, "Test MtrrSetMemoryAttributeInMtrrSettings", "MtrrSetMemoryAttributeInMtrrSettings", UnitTestMtrrSetMemoryAttributeInMtrrSettings, InitializeSystem, NULL, &mSystemParameters[SystemIndex]); + AddTestCase (MtrrApiTests, "Test MtrrSetMemoryAttributesInMtrrSettings", "MtrrSetMemoryAttributesInMtrrSettings", UnitTestMtrrSetMemoryAttributesInMtrrSettings, InitializeSystem, NULL, &mSystemParameters[SystemIndex]); + } + } + // + // Execute the tests. + // + srand ((unsigned int) time (NULL)); + Status = RunAllTestSuites (Framework); + +EXIT: + if (Framework != NULL) { + FreeUnitTestFramework (Framework); + } + + return Status; +} + +/** + Standard POSIX C entry point for host based unit test execution. + + @param Argc Number of arguments. + @param Argv Array of arguments. + + @return Test application exit code. +**/ +INT32 +main ( + INT32 Argc, + CHAR8 *Argv[] + ) +{ + UINTN Iteration; + + // + // First parameter specifies the test iterations. + // Default is 10. + // + Iteration = 10; + if (Argc == 2) { + Iteration = atoi (Argv[1]); + } + return UnitTestingEntry (Iteration); +} diff --git a/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTest.h b/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTest.h new file mode 100644 index 0000000000..9750523133 --- /dev/null +++ b/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTest.h @@ -0,0 +1,182 @@ +/** @file + + Copyright (c) 2020, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _MTRR_SUPPORT_H_ +#define _MTRR_SUPPORT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define UNIT_TEST_APP_NAME "MtrrLib Unit Tests" +#define UNIT_TEST_APP_VERSION "1.0" + +#define SCRATCH_BUFFER_SIZE SIZE_16KB + +typedef struct { + UINT8 PhysicalAddressBits; + BOOLEAN MtrrSupported; + BOOLEAN FixedMtrrSupported; + MTRR_MEMORY_CACHE_TYPE DefaultCacheType; + UINT32 VariableMtrrCount; +} MTRR_LIB_SYSTEM_PARAMETER; + +extern UINT32 mFixedMtrrsIndex[]; + +/** + Initialize the MTRR registers. + + @param SystemParameter System parameter that controls the MTRR registers initialization. +**/ +UNIT_TEST_STATUS +EFIAPI +InitializeMtrrRegs ( + IN MTRR_LIB_SYSTEM_PARAMETER *SystemParameter + ); + +/** + Initialize the MTRR registers. + + @param Context System parameter that controls the MTRR registers initialization. +**/ +UNIT_TEST_STATUS +EFIAPI +InitializeSystem ( + IN UNIT_TEST_CONTEXT Context + ); + +/** + Return a random memory cache type. +**/ +MTRR_MEMORY_CACHE_TYPE +GenerateRandomCacheType ( + VOID + ); + +/** + Generate random MTRRs. + + @param PhysicalAddressBits Physical address bits. + @param RawMemoryRanges Return the randomly generated MTRRs. + @param UcCount Count of Uncacheable MTRRs. + @param WtCount Count of Write Through MTRRs. + @param WbCount Count of Write Back MTRRs. + @param WpCount Count of Write Protected MTRRs. + @param WcCount Count of Write Combining MTRRs. +**/ +VOID +GenerateValidAndConfigurableMtrrPairs ( + IN UINT32 PhysicalAddressBits, + IN OUT MTRR_MEMORY_RANGE *RawMemoryRanges, + IN UINT32 UcCount, + IN UINT32 WtCount, + IN UINT32 WbCount, + IN UINT32 WpCount, + IN UINT32 WcCount + ); + +/** + Convert the MTRR BASE/MASK array to memory ranges. + + @param DefaultType Default memory type. + @param PhysicalAddressBits Physical address bits. + @param RawMemoryRanges Raw memory ranges. + @param RawMemoryRangeCount Count of raw memory ranges. + @param MemoryRanges Memory ranges. + @param MemoryRangeCount Count of memory ranges. +**/ +VOID +GetEffectiveMemoryRanges ( + IN MTRR_MEMORY_CACHE_TYPE DefaultType, + IN UINT32 PhysicalAddressBits, + IN MTRR_MEMORY_RANGE *RawMemoryRanges, + IN UINT32 RawMemoryRangeCount, + OUT MTRR_MEMORY_RANGE *MemoryRanges, + OUT UINTN *MemoryRangeCount + ); + +/** + Generate random MTRR BASE/MASK for a specified type. + + @param PhysicalAddressBits Physical address bits. + @param CacheType Cache type. + @param MtrrPair Return the random MTRR. + @param MtrrMemoryRange Return the random memory range. +**/ +VOID +GenerateRandomMtrrPair ( + IN UINT32 PhysicalAddressBits, + IN MTRR_MEMORY_CACHE_TYPE CacheType, + OUT MTRR_VARIABLE_SETTING *MtrrPair, OPTIONAL + OUT MTRR_MEMORY_RANGE *MtrrMemoryRange OPTIONAL + ); + +/** + Collect the test result. + + @param DefaultType Default memory type. + @param PhysicalAddressBits Physical address bits. + @param VariableMtrrCount Count of variable MTRRs. + @param Mtrrs MTRR settings to collect from. + @param Ranges Return the memory ranges. + @param RangeCount Return the count of memory ranges. + @param MtrrCount Return the count of variable MTRRs being used. +**/ +VOID +CollectTestResult ( + IN MTRR_MEMORY_CACHE_TYPE DefaultType, + IN UINT32 PhysicalAddressBits, + IN UINT32 VariableMtrrCount, + IN MTRR_SETTINGS *Mtrrs, + OUT MTRR_MEMORY_RANGE *Ranges, + IN OUT UINTN *RangeCount, + OUT UINT32 *MtrrCount + ); + +/** + Return a 64bit random number. + + @param Start Start of the random number range. + @param Limit Limit of the random number range. + @return 64bit random number +**/ +UINT64 +Random64 ( + UINT64 Start, + UINT64 Limit + ); + +/** + Return a 32bit random number. + + @param Start Start of the random number range. + @param Limit Limit of the random number range. + @return 32bit random number +**/ +UINT32 +Random32 ( + UINT32 Start, + UINT32 Limit + ); +#endif diff --git a/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTestHost.inf b/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTestHost.inf new file mode 100644 index 0000000000..447238dc81 --- /dev/null +++ b/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTestHost.inf @@ -0,0 +1,39 @@ +## @file +# Unit tests of the MtrrLib instance of the MtrrLib class +# +# Copyright (c) 2020, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = MtrrLibUnitTestHost + FILE_GUID = A1542D84-B64D-4847-885E-0509084376AB + MODULE_TYPE = HOST_APPLICATION + VERSION_STRING = 1.0 + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + MtrrLibUnitTest.c + MtrrLibUnitTest.h + Support.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + MtrrLib + UnitTestLib + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuNumberOfReservedVariableMtrrs ## SOMETIMES_CONSUMES diff --git a/UefiCpuPkg/Library/MtrrLib/UnitTest/Support.c b/UefiCpuPkg/Library/MtrrLib/UnitTest/Support.c new file mode 100644 index 0000000000..a7eed45940 --- /dev/null +++ b/UefiCpuPkg/Library/MtrrLib/UnitTest/Support.c @@ -0,0 +1,923 @@ +/** @file + Unit tests of the MtrrLib instance of the MtrrLib class + + Copyright (c) 2018 - 2020, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "MtrrLibUnitTest.h" + +MTRR_MEMORY_CACHE_TYPE mMemoryCacheTypes[] = { + CacheUncacheable, CacheWriteCombining, CacheWriteThrough, CacheWriteProtected, CacheWriteBack + }; + +UINT64 mFixedMtrrsValue[MTRR_NUMBER_OF_FIXED_MTRR]; +MSR_IA32_MTRR_PHYSBASE_REGISTER mVariableMtrrsPhysBase[MTRR_NUMBER_OF_VARIABLE_MTRR]; +MSR_IA32_MTRR_PHYSMASK_REGISTER mVariableMtrrsPhysMask[MTRR_NUMBER_OF_VARIABLE_MTRR]; +MSR_IA32_MTRR_DEF_TYPE_REGISTER mDefTypeMsr; +MSR_IA32_MTRRCAP_REGISTER mMtrrCapMsr; +CPUID_VERSION_INFO_EDX mCpuidVersionInfoEdx; +CPUID_VIR_PHY_ADDRESS_SIZE_EAX mCpuidVirPhyAddressSizeEax; + +/** + Retrieves CPUID information. + + Executes the CPUID instruction with EAX set to the value specified by Index. + This function always returns Index. + If Eax is not NULL, then the value of EAX after CPUID is returned in Eax. + If Ebx is not NULL, then the value of EBX after CPUID is returned in Ebx. + If Ecx is not NULL, then the value of ECX after CPUID is returned in Ecx. + If Edx is not NULL, then the value of EDX after CPUID is returned in Edx. + This function is only available on IA-32 and x64. + + @param Index The 32-bit value to load into EAX prior to invoking the CPUID + instruction. + @param Eax The pointer to the 32-bit EAX value returned by the CPUID + instruction. This is an optional parameter that may be NULL. + @param Ebx The pointer to the 32-bit EBX value returned by the CPUID + instruction. This is an optional parameter that may be NULL. + @param Ecx The pointer to the 32-bit ECX value returned by the CPUID + instruction. This is an optional parameter that may be NULL. + @param Edx The pointer to the 32-bit EDX value returned by the CPUID + instruction. This is an optional parameter that may be NULL. + + @return Index. + +**/ +UINT32 +EFIAPI +UnitTestMtrrLibAsmCpuid ( + IN UINT32 Index, + OUT UINT32 *Eax, OPTIONAL + OUT UINT32 *Ebx, OPTIONAL + OUT UINT32 *Ecx, OPTIONAL + OUT UINT32 *Edx OPTIONAL + ) +{ + switch (Index) { + case CPUID_VERSION_INFO: + if (Edx != NULL) { + *Edx = mCpuidVersionInfoEdx.Uint32; + } + return Index; + break; + case CPUID_EXTENDED_FUNCTION: + if (Eax != NULL) { + *Eax = CPUID_VIR_PHY_ADDRESS_SIZE; + } + return Index; + break; + case CPUID_VIR_PHY_ADDRESS_SIZE: + if (Eax != NULL) { + *Eax = mCpuidVirPhyAddressSizeEax.Uint32; + } + return Index; + break; + } + + // + // Should never fall through to here + // + ASSERT(FALSE); + return Index; +} + +/** + Returns a 64-bit Machine Specific Register(MSR). + + Reads and returns the 64-bit MSR specified by Index. No parameter checking is + performed on Index, and some Index values may cause CPU exceptions. The + caller must either guarantee that Index is valid, or the caller must set up + exception handlers to catch the exceptions. This function is only available + on IA-32 and x64. + + @param MsrIndex The 32-bit MSR index to read. + + @return The value of the MSR identified by MsrIndex. + +**/ +UINT64 +EFIAPI +UnitTestMtrrLibAsmReadMsr64( + IN UINT32 MsrIndex + ) +{ + UINT32 Index; + + for (Index = 0; Index < ARRAY_SIZE (mFixedMtrrsValue); Index++) { + if (MsrIndex == mFixedMtrrsIndex[Index]) { + return mFixedMtrrsValue[Index]; + } + } + + if ((MsrIndex >= MSR_IA32_MTRR_PHYSBASE0) && + (MsrIndex <= MSR_IA32_MTRR_PHYSMASK0 + (MTRR_NUMBER_OF_VARIABLE_MTRR << 1))) { + if (MsrIndex % 2 == 0) { + Index = (MsrIndex - MSR_IA32_MTRR_PHYSBASE0) >> 1; + return mVariableMtrrsPhysBase[Index].Uint64; + } else { + Index = (MsrIndex - MSR_IA32_MTRR_PHYSMASK0) >> 1; + return mVariableMtrrsPhysMask[Index].Uint64; + } + } + + if (MsrIndex == MSR_IA32_MTRR_DEF_TYPE) { + return mDefTypeMsr.Uint64; + } + + if (MsrIndex == MSR_IA32_MTRRCAP) { + return mMtrrCapMsr.Uint64; + } + + // + // Should never fall through to here + // + ASSERT(FALSE); + return 0; +} + +/** + Writes a 64-bit value to a Machine Specific Register(MSR), and returns the + value. + + Writes the 64-bit value specified by Value to the MSR specified by Index. The + 64-bit value written to the MSR is returned. No parameter checking is + performed on Index or Value, and some of these may cause CPU exceptions. The + caller must either guarantee that Index and Value are valid, or the caller + must establish proper exception handlers. This function is only available on + IA-32 and x64. + + @param MsrIndex The 32-bit MSR index to write. + @param Value The 64-bit value to write to the MSR. + + @return Value + +**/ +UINT64 +EFIAPI +UnitTestMtrrLibAsmWriteMsr64( + IN UINT32 MsrIndex, + IN UINT64 Value + ) +{ + UINT32 Index; + + for (Index = 0; Index < ARRAY_SIZE (mFixedMtrrsValue); Index++) { + if (MsrIndex == mFixedMtrrsIndex[Index]) { + mFixedMtrrsValue[Index] = Value; + return Value; + } + } + + if ((MsrIndex >= MSR_IA32_MTRR_PHYSBASE0) && + (MsrIndex <= MSR_IA32_MTRR_PHYSMASK0 + (MTRR_NUMBER_OF_VARIABLE_MTRR << 1))) { + if (MsrIndex % 2 == 0) { + Index = (MsrIndex - MSR_IA32_MTRR_PHYSBASE0) >> 1; + mVariableMtrrsPhysBase[Index].Uint64 = Value; + return Value; + } else { + Index = (MsrIndex - MSR_IA32_MTRR_PHYSMASK0) >> 1; + mVariableMtrrsPhysMask[Index].Uint64 = Value; + return Value; + } + } + + if (MsrIndex == MSR_IA32_MTRR_DEF_TYPE) { + mDefTypeMsr.Uint64 = Value; + return Value; + } + + if (MsrIndex == MSR_IA32_MTRRCAP) { + mMtrrCapMsr.Uint64 = Value; + return Value; + } + + // + // Should never fall through to here + // + ASSERT(FALSE); + return 0; +} + +/** + Initialize the MTRR registers. + + @param SystemParameter System parameter that controls the MTRR registers initialization. +**/ +UNIT_TEST_STATUS +EFIAPI +InitializeMtrrRegs ( + IN MTRR_LIB_SYSTEM_PARAMETER *SystemParameter + ) +{ + UINT32 Index; + + SetMem (mFixedMtrrsValue, sizeof (mFixedMtrrsValue), SystemParameter->DefaultCacheType); + + for (Index = 0; Index < ARRAY_SIZE (mVariableMtrrsPhysBase); Index++) { + mVariableMtrrsPhysBase[Index].Uint64 = 0; + mVariableMtrrsPhysBase[Index].Bits.Type = SystemParameter->DefaultCacheType; + mVariableMtrrsPhysBase[Index].Bits.Reserved1 = 0; + + mVariableMtrrsPhysMask[Index].Uint64 = 0; + mVariableMtrrsPhysMask[Index].Bits.V = 0; + mVariableMtrrsPhysMask[Index].Bits.Reserved1 = 0; + } + + mDefTypeMsr.Bits.E = 1; + mDefTypeMsr.Bits.FE = 1; + mDefTypeMsr.Bits.Type = SystemParameter->DefaultCacheType; + mDefTypeMsr.Bits.Reserved1 = 0; + mDefTypeMsr.Bits.Reserved2 = 0; + mDefTypeMsr.Bits.Reserved3 = 0; + + mMtrrCapMsr.Bits.SMRR = 0; + mMtrrCapMsr.Bits.WC = 0; + mMtrrCapMsr.Bits.VCNT = SystemParameter->VariableMtrrCount; + mMtrrCapMsr.Bits.FIX = SystemParameter->FixedMtrrSupported; + mMtrrCapMsr.Bits.Reserved1 = 0; + mMtrrCapMsr.Bits.Reserved2 = 0; + mMtrrCapMsr.Bits.Reserved3 = 0; + + mCpuidVersionInfoEdx.Bits.MTRR = SystemParameter->MtrrSupported; + mCpuidVirPhyAddressSizeEax.Bits.PhysicalAddressBits = SystemParameter->PhysicalAddressBits; + + // + // Hook BaseLib functions used by MtrrLib that require some emulation. + // + gUnitTestHostBaseLib.X86->AsmCpuid = UnitTestMtrrLibAsmCpuid; + gUnitTestHostBaseLib.X86->AsmReadMsr64 = UnitTestMtrrLibAsmReadMsr64; + gUnitTestHostBaseLib.X86->AsmWriteMsr64 = UnitTestMtrrLibAsmWriteMsr64; + + return UNIT_TEST_PASSED; +} + +/** + Initialize the MTRR registers. + + @param Context System parameter that controls the MTRR registers initialization. +**/ +UNIT_TEST_STATUS +EFIAPI +InitializeSystem ( + IN UNIT_TEST_CONTEXT Context + ) +{ + return InitializeMtrrRegs ((MTRR_LIB_SYSTEM_PARAMETER *) Context); +} + +/** + Collect the test result. + + @param DefaultType Default memory type. + @param PhysicalAddressBits Physical address bits. + @param VariableMtrrCount Count of variable MTRRs. + @param Mtrrs MTRR settings to collect from. + @param Ranges Return the memory ranges. + @param RangeCount Return the count of memory ranges. + @param MtrrCount Return the count of variable MTRRs being used. +**/ +VOID +CollectTestResult ( + IN MTRR_MEMORY_CACHE_TYPE DefaultType, + IN UINT32 PhysicalAddressBits, + IN UINT32 VariableMtrrCount, + IN MTRR_SETTINGS *Mtrrs, + OUT MTRR_MEMORY_RANGE *Ranges, + IN OUT UINTN *RangeCount, + OUT UINT32 *MtrrCount + ) +{ + UINTN Index; + UINT64 MtrrValidBitsMask; + UINT64 MtrrValidAddressMask; + MTRR_MEMORY_RANGE RawMemoryRanges[ARRAY_SIZE (Mtrrs->Variables.Mtrr)]; + + ASSERT (Mtrrs != NULL); + ASSERT (VariableMtrrCount <= ARRAY_SIZE (Mtrrs->Variables.Mtrr)); + + MtrrValidBitsMask = (1ull << PhysicalAddressBits) - 1; + MtrrValidAddressMask = MtrrValidBitsMask & ~0xFFFull; + + *MtrrCount = 0; + for (Index = 0; Index < VariableMtrrCount; Index++) { + if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *) &Mtrrs->Variables.Mtrr[Index].Mask)->Bits.V == 1) { + RawMemoryRanges[*MtrrCount].BaseAddress = Mtrrs->Variables.Mtrr[Index].Base & MtrrValidAddressMask; + RawMemoryRanges[*MtrrCount].Type = + ((MSR_IA32_MTRR_PHYSBASE_REGISTER *) &Mtrrs->Variables.Mtrr[Index].Base)->Bits.Type; + RawMemoryRanges[*MtrrCount].Length = + ((~(Mtrrs->Variables.Mtrr[Index].Mask & MtrrValidAddressMask)) & MtrrValidBitsMask) + 1; + (*MtrrCount)++; + } + } + + GetEffectiveMemoryRanges (DefaultType, PhysicalAddressBits, RawMemoryRanges, *MtrrCount, Ranges, RangeCount); +} + +/** + Return a 32bit random number. + + @param Start Start of the random number range. + @param Limit Limit of the random number range. + @return 32bit random number +**/ +UINT32 +Random32 ( + UINT32 Start, + UINT32 Limit + ) +{ + return (UINT32) (((double) rand () / RAND_MAX) * (Limit - Start)) + Start; +} + +/** + Return a 64bit random number. + + @param Start Start of the random number range. + @param Limit Limit of the random number range. + @return 64bit random number +**/ +UINT64 +Random64 ( + UINT64 Start, + UINT64 Limit + ) +{ + return (UINT64) (((double) rand () / RAND_MAX) * (Limit - Start)) + Start; +} + +/** + Generate random MTRR BASE/MASK for a specified type. + + @param PhysicalAddressBits Physical address bits. + @param CacheType Cache type. + @param MtrrPair Return the random MTRR. + @param MtrrMemoryRange Return the random memory range. +**/ +VOID +GenerateRandomMtrrPair ( + IN UINT32 PhysicalAddressBits, + IN MTRR_MEMORY_CACHE_TYPE CacheType, + OUT MTRR_VARIABLE_SETTING *MtrrPair, OPTIONAL + OUT MTRR_MEMORY_RANGE *MtrrMemoryRange OPTIONAL + ) +{ + MSR_IA32_MTRR_PHYSBASE_REGISTER PhysBase; + MSR_IA32_MTRR_PHYSMASK_REGISTER PhysMask; + UINT32 SizeShift; + UINT32 BaseShift; + UINT64 RandomBoundary; + UINT64 MaxPhysicalAddress; + UINT64 RangeSize; + UINT64 RangeBase; + UINT64 PhysBasePhyMaskValidBitsMask; + + MaxPhysicalAddress = 1ull << PhysicalAddressBits; + do { + SizeShift = Random32 (12, PhysicalAddressBits - 1); + RangeSize = 1ull << SizeShift; + + BaseShift = Random32 (SizeShift, PhysicalAddressBits - 1); + RandomBoundary = Random64 (0, 1ull << (PhysicalAddressBits - BaseShift)); + RangeBase = RandomBoundary << BaseShift; + } while (RangeBase < SIZE_1MB || RangeBase > MaxPhysicalAddress - 1); + + PhysBasePhyMaskValidBitsMask = (MaxPhysicalAddress - 1) & 0xfffffffffffff000ULL; + + PhysBase.Uint64 = 0; + PhysBase.Bits.Type = CacheType; + PhysBase.Uint64 |= RangeBase & PhysBasePhyMaskValidBitsMask; + PhysMask.Uint64 = 0; + PhysMask.Bits.V = 1; + PhysMask.Uint64 |= ((~RangeSize) + 1) & PhysBasePhyMaskValidBitsMask; + + if (MtrrPair != NULL) { + MtrrPair->Base = PhysBase.Uint64; + MtrrPair->Mask = PhysMask.Uint64; + } + + if (MtrrMemoryRange != NULL) { + MtrrMemoryRange->BaseAddress = RangeBase; + MtrrMemoryRange->Length = RangeSize; + MtrrMemoryRange->Type = CacheType; + } +} + + +/** + Check whether the Range overlaps with any one in Ranges. + + @param Range The memory range to check. + @param Ranges The memory ranges. + @param Count Count of memory ranges. + + @return TRUE when overlap exists. +**/ +BOOLEAN +RangesOverlap ( + IN MTRR_MEMORY_RANGE *Range, + IN MTRR_MEMORY_RANGE *Ranges, + IN UINTN Count + ) +{ + while (Count-- != 0) { + // + // Two ranges overlap when: + // 1. range#2.base is in the middle of range#1 + // 2. range#1.base is in the middle of range#2 + // + if ((Range->BaseAddress <= Ranges[Count].BaseAddress && Ranges[Count].BaseAddress < Range->BaseAddress + Range->Length) + || (Ranges[Count].BaseAddress <= Range->BaseAddress && Range->BaseAddress < Ranges[Count].BaseAddress + Ranges[Count].Length)) { + return TRUE; + } + } + return FALSE; +} + +/** + Generate random MTRRs. + + @param PhysicalAddressBits Physical address bits. + @param RawMemoryRanges Return the randomly generated MTRRs. + @param UcCount Count of Uncacheable MTRRs. + @param WtCount Count of Write Through MTRRs. + @param WbCount Count of Write Back MTRRs. + @param WpCount Count of Write Protected MTRRs. + @param WcCount Count of Write Combine MTRRs. +**/ +VOID +GenerateValidAndConfigurableMtrrPairs ( + IN UINT32 PhysicalAddressBits, + IN OUT MTRR_MEMORY_RANGE *RawMemoryRanges, + IN UINT32 UcCount, + IN UINT32 WtCount, + IN UINT32 WbCount, + IN UINT32 WpCount, + IN UINT32 WcCount + ) +{ + UINT32 Index; + + // + // 1. Generate UC, WT, WB in order. + // + for (Index = 0; Index < UcCount; Index++) { + GenerateRandomMtrrPair (PhysicalAddressBits, CacheUncacheable, NULL, &RawMemoryRanges[Index]); + } + + for (Index = UcCount; Index < UcCount + WtCount; Index++) { + GenerateRandomMtrrPair (PhysicalAddressBits, CacheWriteThrough, NULL, &RawMemoryRanges[Index]); + } + + for (Index = UcCount + WtCount; Index < UcCount + WtCount + WbCount; Index++) { + GenerateRandomMtrrPair (PhysicalAddressBits, CacheWriteBack, NULL, &RawMemoryRanges[Index]); + } + + // + // 2. Generate WP MTRR and DO NOT overlap with WT, WB. + // + for (Index = UcCount + WtCount + WbCount; Index < UcCount + WtCount + WbCount + WpCount; Index++) { + GenerateRandomMtrrPair (PhysicalAddressBits, CacheWriteProtected, NULL, &RawMemoryRanges[Index]); + while (RangesOverlap (&RawMemoryRanges[Index], &RawMemoryRanges[UcCount], WtCount + WbCount)) { + GenerateRandomMtrrPair (PhysicalAddressBits, CacheWriteProtected, NULL, &RawMemoryRanges[Index]); + } + } + + // + // 3. Generate WC MTRR and DO NOT overlap with WT, WB, WP. + // + for (Index = UcCount + WtCount + WbCount + WpCount; Index < UcCount + WtCount + WbCount + WpCount + WcCount; Index++) { + GenerateRandomMtrrPair (PhysicalAddressBits, CacheWriteCombining, NULL, &RawMemoryRanges[Index]); + while (RangesOverlap (&RawMemoryRanges[Index], &RawMemoryRanges[UcCount], WtCount + WbCount + WpCount)) { + GenerateRandomMtrrPair (PhysicalAddressBits, CacheWriteCombining, NULL, &RawMemoryRanges[Index]); + } + } +} + +/** + Return a random memory cache type. +**/ +MTRR_MEMORY_CACHE_TYPE +GenerateRandomCacheType ( + VOID + ) +{ + return mMemoryCacheTypes[Random32 (0, ARRAY_SIZE (mMemoryCacheTypes) - 1)]; +} + +/** + Compare function used by qsort(). +**/ + +/** + Compare function used by qsort(). + + @param Left Left operand to compare. + @param Right Right operand to compare. + + @retval 0 Left == Right + @retval -1 Left < Right + @retval 1 Left > Right +**/ +INT32 +CompareFuncUint64 ( + CONST VOID * Left, + CONST VOID * Right + ) +{ + INT64 Delta; + Delta = (*(UINT64*)Left - *(UINT64*)Right); + if (Delta > 0) { + return 1; + } else if (Delta == 0) { + return 0; + } else { + return -1; + } +} + +/** + Determin the memory cache type for the Range. + + @param DefaultType Default cache type. + @param Range The memory range to determin the cache type. + @param Ranges The entire memory ranges. + @param RangeCount Count of the entire memory ranges. +**/ +VOID +DetermineMemoryCacheType ( + IN MTRR_MEMORY_CACHE_TYPE DefaultType, + IN OUT MTRR_MEMORY_RANGE *Range, + IN MTRR_MEMORY_RANGE *Ranges, + IN UINT32 RangeCount + ) +{ + UINT32 Index; + Range->Type = CacheInvalid; + for (Index = 0; Index < RangeCount; Index++) { + if (RangesOverlap (Range, &Ranges[Index], 1)) { + if (Ranges[Index].Type < Range->Type) { + Range->Type = Ranges[Index].Type; + } + } + } + + if (Range->Type == CacheInvalid) { + Range->Type = DefaultType; + } +} + +/** + Get the index of the element that does NOT equals to Array[Index]. + + @param Index Current element. + @param Array Array to scan. + @param Count Count of the array. + + @return Next element that doesn't equal to current one. +**/ +UINT32 +GetNextDifferentElementInSortedArray ( + IN UINT32 Index, + IN UINT64 *Array, + IN UINT32 Count + ) +{ + UINT64 CurrentElement; + CurrentElement = Array[Index]; + while (CurrentElement == Array[Index] && Index < Count) { + Index++; + } + return Index; +} + +/** + Remove the duplicates from the array. + + @param Array The array to operate on. + @param Count Count of the array. +**/ +VOID +RemoveDuplicatesInSortedArray ( + IN OUT UINT64 *Array, + IN OUT UINT32 *Count + ) +{ + UINT32 Index; + UINT32 NewCount; + + Index = 0; + NewCount = 0; + while (Index < *Count) { + Array[NewCount] = Array[Index]; + NewCount++; + Index = GetNextDifferentElementInSortedArray (Index, Array, *Count); + } + *Count = NewCount; +} + +/** + Return TRUE when Address is in the Range. + + @param Address The address to check. + @param Range The range to check. + @return TRUE when Address is in the Range. +**/ +BOOLEAN +AddressInRange ( + IN UINT64 Address, + IN MTRR_MEMORY_RANGE Range + ) +{ + return (Address >= Range.BaseAddress) && (Address <= Range.BaseAddress + Range.Length - 1); +} + +/** + Get the overlap bit flag. + + @param RawMemoryRanges Raw memory ranges. + @param RawMemoryRangeCount Count of raw memory ranges. + @param Address The address to check. +**/ +UINT64 +GetOverlapBitFlag ( + IN MTRR_MEMORY_RANGE *RawMemoryRanges, + IN UINT32 RawMemoryRangeCount, + IN UINT64 Address + ) +{ + UINT64 OverlapBitFlag; + UINT32 Index; + OverlapBitFlag = 0; + for (Index = 0; Index < RawMemoryRangeCount; Index++) { + if (AddressInRange (Address, RawMemoryRanges[Index])) { + OverlapBitFlag |= (1ull << Index); + } + } + + return OverlapBitFlag; +} + +/** + Return the relationship between flags. + + @param Flag1 Flag 1 + @param Flag2 Flag 2 + + @retval 0 Flag1 == Flag2 + @retval 1 Flag1 is a subset of Flag2 + @retval 2 Flag2 is a subset of Flag1 + @retval 3 No subset relations between Flag1 and Flag2. +**/ +UINT32 +CheckOverlapBitFlagsRelation ( + IN UINT64 Flag1, + IN UINT64 Flag2 + ) +{ + if (Flag1 == Flag2) return 0; + if ((Flag1 | Flag2) == Flag2) return 1; + if ((Flag1 | Flag2) == Flag1) return 2; + return 3; +} + +/** + Return TRUE when the Endpoint is in any of the Ranges. + + @param Endpoint The endpoint to check. + @param Ranges The memory ranges. + @param RangeCount Count of memory ranges. + + @retval TRUE Endpoint is in one of the range. + @retval FALSE Endpoint is not in any of the ranges. +**/ +BOOLEAN +IsEndpointInRanges ( + IN UINT64 Endpoint, + IN MTRR_MEMORY_RANGE *Ranges, + IN UINTN RangeCount + ) +{ + UINT32 Index; + for (Index = 0; Index < RangeCount; Index++) { + if (AddressInRange (Endpoint, Ranges[Index])) { + return TRUE; + } + } + return FALSE; +} + + +/** + Compact adjacent ranges of the same type. + + @param DefaultType Default memory type. + @param PhysicalAddressBits Physical address bits. + @param EffectiveMtrrMemoryRanges Memory ranges to compact. + @param EffectiveMtrrMemoryRangesCount Return the new count of memory ranges. +**/ +VOID +CompactAndExtendEffectiveMtrrMemoryRanges ( + IN MTRR_MEMORY_CACHE_TYPE DefaultType, + IN UINT32 PhysicalAddressBits, + IN OUT MTRR_MEMORY_RANGE **EffectiveMtrrMemoryRanges, + IN OUT UINTN *EffectiveMtrrMemoryRangesCount + ) +{ + UINT64 MaxAddress; + UINTN NewRangesCountAtMost; + MTRR_MEMORY_RANGE *NewRanges; + UINTN NewRangesCountActual; + MTRR_MEMORY_RANGE *CurrentRangeInNewRanges; + MTRR_MEMORY_CACHE_TYPE CurrentRangeTypeInOldRanges; + + MTRR_MEMORY_RANGE *OldRanges; + MTRR_MEMORY_RANGE OldLastRange; + UINTN OldRangesIndex; + + NewRangesCountActual = 0; + NewRangesCountAtMost = *EffectiveMtrrMemoryRangesCount + 2; // At most with 2 more range entries. + NewRanges = (MTRR_MEMORY_RANGE *) calloc (NewRangesCountAtMost, sizeof (MTRR_MEMORY_RANGE)); + OldRanges = *EffectiveMtrrMemoryRanges; + if (OldRanges[0].BaseAddress > 0) { + NewRanges[NewRangesCountActual].BaseAddress = 0; + NewRanges[NewRangesCountActual].Length = OldRanges[0].BaseAddress; + NewRanges[NewRangesCountActual].Type = DefaultType; + NewRangesCountActual++; + } + + OldRangesIndex = 0; + while (OldRangesIndex < *EffectiveMtrrMemoryRangesCount) { + CurrentRangeTypeInOldRanges = OldRanges[OldRangesIndex].Type; + CurrentRangeInNewRanges = NULL; + if (NewRangesCountActual > 0) // We need to check CurrentNewRange first before generate a new NewRange. + { + CurrentRangeInNewRanges = &NewRanges[NewRangesCountActual - 1]; + } + if (CurrentRangeInNewRanges != NULL && CurrentRangeInNewRanges->Type == CurrentRangeTypeInOldRanges) { + CurrentRangeInNewRanges->Length += OldRanges[OldRangesIndex].Length; + } else { + NewRanges[NewRangesCountActual].BaseAddress = OldRanges[OldRangesIndex].BaseAddress; + NewRanges[NewRangesCountActual].Length += OldRanges[OldRangesIndex].Length; + NewRanges[NewRangesCountActual].Type = CurrentRangeTypeInOldRanges; + while (OldRangesIndex + 1 < *EffectiveMtrrMemoryRangesCount && OldRanges[OldRangesIndex + 1].Type == CurrentRangeTypeInOldRanges) + { + OldRangesIndex++; + NewRanges[NewRangesCountActual].Length += OldRanges[OldRangesIndex].Length; + } + NewRangesCountActual++; + } + + OldRangesIndex++; + } + + MaxAddress = (1ull << PhysicalAddressBits) - 1; + OldLastRange = OldRanges[(*EffectiveMtrrMemoryRangesCount) - 1]; + CurrentRangeInNewRanges = &NewRanges[NewRangesCountActual - 1]; + if (OldLastRange.BaseAddress + OldLastRange.Length - 1 < MaxAddress) { + if (CurrentRangeInNewRanges->Type == DefaultType) { + CurrentRangeInNewRanges->Length = MaxAddress - CurrentRangeInNewRanges->BaseAddress + 1; + } else { + NewRanges[NewRangesCountActual].BaseAddress = OldLastRange.BaseAddress + OldLastRange.Length; + NewRanges[NewRangesCountActual].Length = MaxAddress - NewRanges[NewRangesCountActual].BaseAddress + 1; + NewRanges[NewRangesCountActual].Type = DefaultType; + NewRangesCountActual++; + } + } + + free (*EffectiveMtrrMemoryRanges); + *EffectiveMtrrMemoryRanges = NewRanges; + *EffectiveMtrrMemoryRangesCount = NewRangesCountActual; +} + +/** + Collect all the endpoints in the raw memory ranges. + + @param Endpoints Return the collected endpoints. + @param EndPointCount Return the count of endpoints. + @param RawMemoryRanges Raw memory ranges. + @param RawMemoryRangeCount Count of raw memory ranges. +**/ +VOID +CollectEndpoints ( + IN OUT UINT64 *Endpoints, + IN OUT UINT32 *EndPointCount, + IN MTRR_MEMORY_RANGE *RawMemoryRanges, + IN UINT32 RawMemoryRangeCount + ) +{ + UINT32 Index; + UINT32 RawRangeIndex; + + ASSERT ((RawMemoryRangeCount << 1) == *EndPointCount); + + for (Index = 0; Index < *EndPointCount; Index += 2) { + RawRangeIndex = Index >> 1; + Endpoints[Index] = RawMemoryRanges[RawRangeIndex].BaseAddress; + Endpoints[Index + 1] = RawMemoryRanges[RawRangeIndex].BaseAddress + RawMemoryRanges[RawRangeIndex].Length - 1; + } + + qsort (Endpoints, *EndPointCount, sizeof (UINT64), CompareFuncUint64); + RemoveDuplicatesInSortedArray (Endpoints, EndPointCount); +} + +/** + Convert the MTRR BASE/MASK array to memory ranges. + + @param DefaultType Default memory type. + @param PhysicalAddressBits Physical address bits. + @param RawMemoryRanges Raw memory ranges. + @param RawMemoryRangeCount Count of raw memory ranges. + @param MemoryRanges Memory ranges. + @param MemoryRangeCount Count of memory ranges. +**/ +VOID +GetEffectiveMemoryRanges ( + IN MTRR_MEMORY_CACHE_TYPE DefaultType, + IN UINT32 PhysicalAddressBits, + IN MTRR_MEMORY_RANGE *RawMemoryRanges, + IN UINT32 RawMemoryRangeCount, + OUT MTRR_MEMORY_RANGE *MemoryRanges, + OUT UINTN *MemoryRangeCount + ) +{ + UINTN Index; + UINT32 AllEndPointsCount; + UINT64 *AllEndPointsInclusive; + UINT32 AllRangePiecesCountMax; + MTRR_MEMORY_RANGE *AllRangePieces; + UINTN AllRangePiecesCountActual; + UINT64 OverlapBitFlag1; + UINT64 OverlapBitFlag2; + INT32 OverlapFlagRelation; + + if (RawMemoryRangeCount == 0) { + MemoryRanges[0].BaseAddress = 0; + MemoryRanges[0].Length = (1ull << PhysicalAddressBits); + MemoryRanges[0].Type = DefaultType; + *MemoryRangeCount = 1; + return; + } + + AllEndPointsCount = RawMemoryRangeCount << 1; + AllEndPointsInclusive = calloc (AllEndPointsCount, sizeof (UINT64)); + AllRangePiecesCountMax = RawMemoryRangeCount * 3 + 1; + AllRangePieces = calloc (AllRangePiecesCountMax, sizeof (MTRR_MEMORY_RANGE)); + CollectEndpoints (AllEndPointsInclusive, &AllEndPointsCount, RawMemoryRanges, RawMemoryRangeCount); + + for (Index = 0, AllRangePiecesCountActual = 0; Index < AllEndPointsCount - 1; Index++) { + OverlapBitFlag1 = GetOverlapBitFlag (RawMemoryRanges, RawMemoryRangeCount, AllEndPointsInclusive[Index]); + OverlapBitFlag2 = GetOverlapBitFlag (RawMemoryRanges, RawMemoryRangeCount, AllEndPointsInclusive[Index + 1]); + OverlapFlagRelation = CheckOverlapBitFlagsRelation (OverlapBitFlag1, OverlapBitFlag2); + switch (OverlapFlagRelation) { + case 0: // [1, 2] + AllRangePieces[AllRangePiecesCountActual].BaseAddress = AllEndPointsInclusive[Index]; + AllRangePieces[AllRangePiecesCountActual].Length = AllEndPointsInclusive[Index + 1] - AllEndPointsInclusive[Index] + 1; + AllRangePiecesCountActual++; + break; + case 1: // [1, 2) + AllRangePieces[AllRangePiecesCountActual].BaseAddress = AllEndPointsInclusive[Index]; + AllRangePieces[AllRangePiecesCountActual].Length = (AllEndPointsInclusive[Index + 1] - 1) - AllEndPointsInclusive[Index] + 1; + AllRangePiecesCountActual++; + break; + case 2: // (1, 2] + AllRangePieces[AllRangePiecesCountActual].BaseAddress = AllEndPointsInclusive[Index] + 1; + AllRangePieces[AllRangePiecesCountActual].Length = AllEndPointsInclusive[Index + 1] - (AllEndPointsInclusive[Index] + 1) + 1; + AllRangePiecesCountActual++; + + if (!IsEndpointInRanges (AllEndPointsInclusive[Index], AllRangePieces, AllRangePiecesCountActual)) { + AllRangePieces[AllRangePiecesCountActual].BaseAddress = AllEndPointsInclusive[Index]; + AllRangePieces[AllRangePiecesCountActual].Length = 1; + AllRangePiecesCountActual++; + } + break; + case 3: // (1, 2) + AllRangePieces[AllRangePiecesCountActual].BaseAddress = AllEndPointsInclusive[Index] + 1; + AllRangePieces[AllRangePiecesCountActual].Length = (AllEndPointsInclusive[Index + 1] - 1) - (AllEndPointsInclusive[Index] + 1) + 1; + if (AllRangePieces[AllRangePiecesCountActual].Length == 0) // Only in case 3 can exists Length=0, we should skip such "segment". + break; + AllRangePiecesCountActual++; + if (!IsEndpointInRanges (AllEndPointsInclusive[Index], AllRangePieces, AllRangePiecesCountActual)) { + AllRangePieces[AllRangePiecesCountActual].BaseAddress = AllEndPointsInclusive[Index]; + AllRangePieces[AllRangePiecesCountActual].Length = 1; + AllRangePiecesCountActual++; + } + break; + default: + ASSERT (FALSE); + } + } + + for (Index = 0; Index < AllRangePiecesCountActual; Index++) { + DetermineMemoryCacheType (DefaultType, &AllRangePieces[Index], RawMemoryRanges, RawMemoryRangeCount); + } + + CompactAndExtendEffectiveMtrrMemoryRanges (DefaultType, PhysicalAddressBits, &AllRangePieces, &AllRangePiecesCountActual); + ASSERT (*MemoryRangeCount >= AllRangePiecesCountActual); + memcpy (MemoryRanges, AllRangePieces, AllRangePiecesCountActual * sizeof (MTRR_MEMORY_RANGE)); + *MemoryRangeCount = AllRangePiecesCountActual; + + free (AllEndPointsInclusive); + free (AllRangePieces); +} diff --git a/UefiCpuPkg/Test/UefiCpuPkgHostTest.dsc b/UefiCpuPkg/Test/UefiCpuPkgHostTest.dsc new file mode 100644 index 0000000000..8a5c456830 --- /dev/null +++ b/UefiCpuPkg/Test/UefiCpuPkgHostTest.dsc @@ -0,0 +1,31 @@ +## @file +# UefiCpuPkg DSC file used to build host-based unit tests. +# +# Copyright (c) 2020, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + PLATFORM_NAME = UefiCpuPkgHostTest + PLATFORM_GUID = E00B9599-5B74-4FF7-AB9F-8183FB13B2F9 + PLATFORM_VERSION = 0.1 + DSC_SPECIFICATION = 0x00010005 + OUTPUT_DIRECTORY = Build/UefiCpuPkg/HostTest + SUPPORTED_ARCHITECTURES = IA32|X64 + BUILD_TARGETS = NOOPT + SKUID_IDENTIFIER = DEFAULT + +!include UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc + +[LibraryClasses] + MtrrLib|UefiCpuPkg/Library/MtrrLib/MtrrLib.inf + +[PcdsPatchableInModule] + gUefiCpuPkgTokenSpaceGuid.PcdCpuNumberOfReservedVariableMtrrs|0 + +[Components] + # + # Build HOST_APPLICATION that tests the MtrrLib + # + UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTestHost.inf diff --git a/UefiCpuPkg/UefiCpuPkg.ci.yaml b/UefiCpuPkg/UefiCpuPkg.ci.yaml index d54651d438..140cb5a1b6 100644 --- a/UefiCpuPkg/UefiCpuPkg.ci.yaml +++ b/UefiCpuPkg/UefiCpuPkg.ci.yaml @@ -11,6 +11,10 @@ "CompilerPlugin": { "DscPath": "UefiCpuPkg.dsc" }, + ## options defined ci/Plugin/HostUnitTestCompilerPlugin + "HostUnitTestCompilerPlugin": { + "DscPath": "Test/UefiCpuPkgHostTest.dsc" + }, "CharEncodingCheck": { "IgnoreFiles": [] }, @@ -21,7 +25,9 @@ "UefiCpuPkg/UefiCpuPkg.dec" ], # For host based unit tests - "AcceptableDependencies-HOST_APPLICATION":[], + "AcceptableDependencies-HOST_APPLICATION":[ + "UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec" + ], # For UEFI shell based apps "AcceptableDependencies-UEFI_APPLICATION":[], "IgnoreInf": [] @@ -33,6 +39,10 @@ "UefiCpuPkg/ResetVector/Vtf0/Vtf0.inf" ] }, + "HostUnitTestDscCompleteCheck": { + "IgnoreInf": [""], + "DscPath": "Test/UefiCpuPkgHostTest.dsc" + }, "GuidCheck": { "IgnoreGuidName": ["SecCore", "ResetVector"], # Expected duplication for gEfiFirmwareVolumeTopFileGuid "IgnoreGuidValue": [],