/** @file | |
Random test case for Unit tests of the CpuPageTableLib instance of the CpuPageTableLib class | |
Copyright (c) 2022 - 2023, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "CpuPageTableLibUnitTest.h" | |
#include "RandomTest.h" | |
UINTN RandomNumber = 0; | |
extern IA32_PAGING_ENTRY mValidMaskNoLeaf[6]; | |
extern IA32_PAGING_ENTRY mValidMaskLeaf[6]; | |
extern IA32_PAGING_ENTRY mValidMaskLeafFlag[6]; | |
UINTN mRandomOption; | |
IA32_MAP_ATTRIBUTE mSupportedBit; | |
extern UINTN mNumberCount; | |
extern UINT8 mNumbers[]; | |
UINTN mNumberIndex; | |
UINT64 AlignedTable[] = { | |
~((UINT64)SIZE_4KB - 1), | |
~((UINT64)SIZE_2MB - 1), | |
~((UINT64)SIZE_1GB - 1) | |
}; | |
/** | |
Generates a pseudorandom byte stream of the specified size. | |
Return FALSE to indicate this interface is not supported. | |
@param[out] Output Pointer to buffer to receive random value. | |
@param[in] Size Size of random bytes to generate. | |
@retval TRUE Always return TRUE | |
**/ | |
BOOLEAN | |
EFIAPI | |
RandomBytesUsingArray ( | |
OUT UINT8 *Output, | |
IN UINTN Size | |
) | |
{ | |
UINTN Index; | |
for (Index = 0; Index < Size; Index++) { | |
if (mNumberIndex >= mNumberCount) { | |
mNumberIndex = 0; | |
} | |
Output[Index] = mNumbers[mNumberIndex]; | |
mNumberIndex++; | |
} | |
return TRUE; | |
} | |
/** | |
Generates a pseudorandom byte stream of the specified size. | |
Return FALSE to indicate this interface is not supported. | |
@param[out] Output Pointer to buffer to receive random value. | |
@param[in] Size Size of random bytes to generate. | |
@retval TRUE Pseudorandom byte stream generated successfully. | |
@retval FALSE Pseudorandom number generator fails | |
**/ | |
BOOLEAN | |
EFIAPI | |
LocalRandomBytes ( | |
OUT UINT8 *Output, | |
IN UINTN Size | |
) | |
{ | |
if (mRandomOption & USE_RANDOM_ARRAY) { | |
return RandomBytesUsingArray (Output, Size); | |
} else { | |
return RandomBytes (Output, Size); | |
} | |
} | |
/** | |
Return a 32bit random number. | |
@param Start Start of the random number range. | |
@param Limit Limit of the random number range, and return value can be Limit. | |
@return 32bit random number | |
**/ | |
UINT32 | |
Random32 ( | |
UINT32 Start, | |
UINT32 Limit | |
) | |
{ | |
UINT64 Value; | |
LocalRandomBytes ((UINT8 *)&Value, sizeof (UINT64)); | |
return (UINT32)(Value % (Limit - Start + 1)) + Start; | |
} | |
/** | |
Return a 64bit random number. | |
@param Start Start of the random number range. | |
@param Limit Limit of the random number range, and return value can be Limit. | |
@return 64bit random number | |
**/ | |
UINT64 | |
Random64 ( | |
UINT64 Start, | |
UINT64 Limit | |
) | |
{ | |
UINT64 Value; | |
LocalRandomBytes ((UINT8 *)&Value, sizeof (UINT64)); | |
if (Limit - Start == MAX_UINT64) { | |
return (UINT64)(Value); | |
} | |
return (UINT64)(Value % (Limit - Start + 1)) + Start; | |
} | |
/** | |
Returns true with the percentage of input Probability. | |
@param[in] Probability The percentage to return true. | |
@return boolean | |
**/ | |
BOOLEAN | |
RandomBoolean ( | |
UINT8 Probability | |
) | |
{ | |
return ((Probability > ((UINT8)Random64 (0, 100))) ? TRUE : FALSE); | |
} | |
/** | |
Set 8K stack as random value. | |
**/ | |
VOID | |
SetRandomStack ( | |
VOID | |
) | |
{ | |
UINT64 Buffer[SIZE_1KB]; | |
UINTN Index; | |
for (Index = 0; Index < SIZE_1KB; Index++) { | |
Buffer[Index] = Random64 (0, MAX_UINT64); | |
Buffer[Index] = Buffer[Index]; | |
} | |
} | |
/** | |
Check if the Page table entry is valid | |
@param[in] PagingEntry The entry in page table to verify | |
@param[in] Level the level of PagingEntry. | |
@param[in] MaxLeafLevel Max leaf entry level. | |
@param[in] LinearAddress The linear address verified. | |
@retval Leaf entry. | |
**/ | |
UNIT_TEST_STATUS | |
ValidateAndRandomeModifyPageTablePageTableEntry ( | |
IN IA32_PAGING_ENTRY *PagingEntry, | |
IN UINTN Level, | |
IN UINTN MaxLeafLevel, | |
IN UINT64 Address | |
) | |
{ | |
UINT64 Index; | |
UINT32 PageTableBaseAddressLow; | |
UINT32 PageTableBaseAddressHigh; | |
IA32_PAGING_ENTRY *ChildPageEntry; | |
UNIT_TEST_STATUS Status; | |
if (PagingEntry->Pce.Present == 0) { | |
return UNIT_TEST_PASSED; | |
} | |
if ((PagingEntry->Uint64 & mValidMaskLeafFlag[Level].Uint64) == mValidMaskLeafFlag[Level].Uint64) { | |
// | |
// It is a Leaf | |
// | |
if (Level > MaxLeafLevel) { | |
UT_ASSERT_TRUE (Level <= MaxLeafLevel); | |
} | |
if ((PagingEntry->Uint64 & mValidMaskLeaf[Level].Uint64) != PagingEntry->Uint64) { | |
UT_ASSERT_EQUAL ((PagingEntry->Uint64 & mValidMaskLeaf[Level].Uint64), PagingEntry->Uint64); | |
} | |
if ((RandomNumber < 100) && RandomBoolean (50)) { | |
RandomNumber++; | |
if (Level == 1) { | |
PageTableBaseAddressLow = PagingEntry->Pte4K.Bits.PageTableBaseAddressLow; | |
PageTableBaseAddressHigh = PagingEntry->Pte4K.Bits.PageTableBaseAddressHigh; | |
} else { | |
PageTableBaseAddressLow = PagingEntry->PleB.Bits.PageTableBaseAddressLow; | |
PageTableBaseAddressHigh = PagingEntry->PleB.Bits.PageTableBaseAddressHigh; | |
} | |
PagingEntry->Uint64 = (Random64 (0, MAX_UINT64) & mValidMaskLeaf[Level].Uint64) | mValidMaskLeafFlag[Level].Uint64; | |
PagingEntry->Pte4K.Bits.Present = 1; | |
if (Level == 1) { | |
PagingEntry->Pte4K.Bits.PageTableBaseAddressLow = PageTableBaseAddressLow; | |
PagingEntry->Pte4K.Bits.PageTableBaseAddressHigh = PageTableBaseAddressHigh; | |
} else { | |
PagingEntry->PleB.Bits.PageTableBaseAddressLow = PageTableBaseAddressLow; | |
PagingEntry->PleB.Bits.PageTableBaseAddressHigh = PageTableBaseAddressHigh; | |
} | |
if ((PagingEntry->Uint64 & mValidMaskLeaf[Level].Uint64) != PagingEntry->Uint64) { | |
UT_ASSERT_EQUAL ((PagingEntry->Uint64 & mValidMaskLeaf[Level].Uint64), PagingEntry->Uint64); | |
} | |
} | |
return UNIT_TEST_PASSED; | |
} | |
// | |
// Not a leaf | |
// | |
UT_ASSERT_NOT_EQUAL (Level, 1); | |
if ((PagingEntry->Uint64 & mValidMaskNoLeaf[Level].Uint64) != PagingEntry->Uint64) { | |
DEBUG ((DEBUG_ERROR, "ERROR: Level %d no Leaf entry is 0x%lx, which reserved bit is set \n", Level, PagingEntry->Uint64)); | |
UT_ASSERT_EQUAL ((PagingEntry->Uint64 & mValidMaskNoLeaf[Level].Uint64), PagingEntry->Uint64); | |
} | |
if ((RandomNumber < 100) && RandomBoolean (50)) { | |
RandomNumber++; | |
PageTableBaseAddressLow = PagingEntry->PleB.Bits.PageTableBaseAddressLow; | |
PageTableBaseAddressHigh = PagingEntry->PleB.Bits.PageTableBaseAddressHigh; | |
PagingEntry->Uint64 = Random64 (0, MAX_UINT64) & mValidMaskNoLeaf[Level].Uint64; | |
PagingEntry->Pnle.Bits.Present = 1; | |
PagingEntry->PleB.Bits.PageTableBaseAddressLow = PageTableBaseAddressLow; | |
PagingEntry->PleB.Bits.PageTableBaseAddressHigh = PageTableBaseAddressHigh; | |
ASSERT ((PagingEntry->Uint64 & mValidMaskLeafFlag[Level].Uint64) != mValidMaskLeafFlag[Level].Uint64); | |
} | |
ChildPageEntry = (IA32_PAGING_ENTRY *)(UINTN)(IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&PagingEntry->Pnle)); | |
for (Index = 0; Index < 512; Index++) { | |
Status = ValidateAndRandomeModifyPageTablePageTableEntry (&ChildPageEntry[Index], Level-1, MaxLeafLevel, Address + (Index<<(9*(Level-1) + 3))); | |
if (Status != UNIT_TEST_PASSED) { | |
return Status; | |
} | |
} | |
return UNIT_TEST_PASSED; | |
} | |
/** | |
Check if the Page table is valid | |
@param[in] PageTable The pointer to the page table. | |
@param[in] PagingMode The paging mode. | |
@retval UNIT_TEST_PASSED It is a valid Page Table | |
**/ | |
UNIT_TEST_STATUS | |
ValidateAndRandomeModifyPageTable ( | |
IN UINTN PageTable, | |
IN PAGING_MODE PagingMode | |
) | |
{ | |
UINTN MaxLevel; | |
UINTN MaxLeafLevel; | |
UINT64 Index; | |
UNIT_TEST_STATUS Status; | |
IA32_PAGING_ENTRY *PagingEntry; | |
if ((PagingMode == Paging32bit) || (PagingMode >= PagingModeMax)) { | |
// | |
// 32bit paging is never supported. | |
// | |
return UNIT_TEST_ERROR_TEST_FAILED; | |
} | |
MaxLeafLevel = (UINT8)PagingMode; | |
MaxLevel = (UINT8)(PagingMode >> 8); | |
PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)PageTable; | |
for (Index = 0; Index < 512; Index++) { | |
Status = ValidateAndRandomeModifyPageTablePageTableEntry (&PagingEntry[Index], MaxLevel, MaxLeafLevel, Index << (9 * MaxLevel + 3)); | |
if (Status != UNIT_TEST_PASSED) { | |
return Status; | |
} | |
} | |
return Status; | |
} | |
/** | |
Remove the last MAP_ENTRY in MapEntrys. | |
@param MapEntrys Pointer to MapEntrys buffer | |
**/ | |
VOID | |
RemoveLastMapEntry ( | |
IN OUT MAP_ENTRYS *MapEntrys | |
) | |
{ | |
UINTN MapsIndex; | |
if (MapEntrys->Count == 0) { | |
return; | |
} | |
MapsIndex = MapEntrys->Count - 1; | |
ZeroMem (&(MapEntrys->Maps[MapsIndex]), sizeof (MAP_ENTRY)); | |
MapEntrys->Count = MapsIndex; | |
} | |
/** | |
Generate single random map entry. | |
The map entry can be the input of function PageTableMap | |
the LinearAddress and length is aligned to aligned table. | |
@param MaxAddress Max Address. | |
@param MapEntrys Output MapEntrys contains all parameter as input of function PageTableMap | |
**/ | |
VOID | |
GenerateSingleRandomMapEntry ( | |
IN UINT64 MaxAddress, | |
IN OUT MAP_ENTRYS *MapEntrys | |
) | |
{ | |
UINTN MapsIndex; | |
UINT64 FormerLinearAddress; | |
UINT64 FormerLinearAddressBottom; | |
UINT64 FormerLinearAddressTop; | |
MapsIndex = MapEntrys->Count; | |
ASSERT (MapsIndex < MapEntrys->MaxCount); | |
// | |
// use AlignedTable to avoid that a random number can be very hard to be 1G or 2M aligned | |
// | |
if ((MapsIndex != 0) && (RandomBoolean (50))) { | |
FormerLinearAddress = MapEntrys->Maps[Random32 (0, (UINT32)MapsIndex-1)].LinearAddress; | |
if (FormerLinearAddress < 2 * (UINT64)SIZE_1GB) { | |
FormerLinearAddressBottom = 0; | |
} else { | |
FormerLinearAddressBottom = FormerLinearAddress - 2 * (UINT64)SIZE_1GB; | |
} | |
if (FormerLinearAddress + 2 * (UINT64)SIZE_1GB > MaxAddress) { | |
FormerLinearAddressTop = MaxAddress; | |
} else { | |
FormerLinearAddressTop = FormerLinearAddress + 2 * (UINT64)SIZE_1GB; | |
} | |
MapEntrys->Maps[MapsIndex].LinearAddress = Random64 (FormerLinearAddressBottom, FormerLinearAddressTop) & AlignedTable[Random32 (0, ARRAY_SIZE (AlignedTable) -1)]; | |
} else { | |
MapEntrys->Maps[MapsIndex].LinearAddress = Random64 (0, MaxAddress) & AlignedTable[Random32 (0, ARRAY_SIZE (AlignedTable) -1)]; | |
} | |
// | |
// To have better performance, limit the size less than 10G | |
// | |
MapEntrys->Maps[MapsIndex].Length = Random64 (0, MIN (MaxAddress - MapEntrys->Maps[MapsIndex].LinearAddress, 10 * (UINT64)SIZE_1GB)) & AlignedTable[Random32 (0, ARRAY_SIZE (AlignedTable) -1)]; | |
if ((MapsIndex != 0) && (RandomBoolean (50))) { | |
MapEntrys->Maps[MapsIndex].Attribute.Uint64 = MapEntrys->Maps[Random32 (0, (UINT32)MapsIndex-1)].Attribute.Uint64; | |
MapEntrys->Maps[MapsIndex].Mask.Uint64 = MapEntrys->Maps[Random32 (0, (UINT32)MapsIndex-1)].Mask.Uint64; | |
} else { | |
MapEntrys->Maps[MapsIndex].Attribute.Uint64 = Random64 (0, MAX_UINT64) & mSupportedBit.Uint64; | |
if (RandomBoolean (5)) { | |
// | |
// The probability to get random Mask should be small since all bits of a random number | |
// have a high probability of containing 0, which may be a invalid input. | |
// | |
MapEntrys->Maps[MapsIndex].Mask.Uint64 = Random64 (0, MAX_UINT64) & mSupportedBit.Uint64; | |
} else { | |
MapEntrys->Maps[MapsIndex].Mask.Uint64 = MAX_UINT64; | |
} | |
if (MapEntrys->Maps[MapsIndex].Mask.Bits.ProtectionKey != 0) { | |
MapEntrys->Maps[MapsIndex].Mask.Bits.ProtectionKey = 0xF; | |
} | |
} | |
if (mRandomOption & ONLY_ONE_ONE_MAPPING) { | |
MapEntrys->Maps[MapsIndex].Attribute.Uint64 &= (~IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS_MASK); | |
MapEntrys->Maps[MapsIndex].Attribute.Uint64 |= MapEntrys->Maps[MapsIndex].LinearAddress; | |
MapEntrys->Maps[MapsIndex].Mask.Uint64 |= IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS_MASK; | |
} else { | |
MapEntrys->Maps[MapsIndex].Attribute.Uint64 &= (~IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS_MASK); | |
MapEntrys->Maps[MapsIndex].Attribute.Uint64 |= (Random64 (0, (((UINT64)1)<<52) - 1) & AlignedTable[Random32 (0, ARRAY_SIZE (AlignedTable) -1)]); | |
} | |
MapEntrys->Count += 1; | |
} | |
/** | |
Compare the attribute for one point. | |
MapEntrys records every memory ranges that is used as input | |
Map and MapCount are gotten from Page table | |
Compare if this point have same attribute. | |
@param[in] Address Address of one Point. | |
@param[in] MapEntrys Record every memory ranges that is used as input | |
@param[in] Map Pointer to an array that describes multiple linear address ranges. | |
@param[in] MapCount Pointer to a UINTN that hold the number of entries in the Map. | |
@param[in] InitMap Pointer to an array that describes init map entries. | |
@param[in] InitMapCount Pointer to a UINTN that hold the number of init map entries. | |
@retval TRUE At least one byte of data is available to be read | |
@retval FALSE No data is available to be read | |
**/ | |
BOOLEAN | |
CompareEntrysforOnePoint ( | |
IN UINT64 Address, | |
IN MAP_ENTRYS *MapEntrys, | |
IN IA32_MAP_ENTRY *Map, | |
IN UINTN MapCount, | |
IN IA32_MAP_ENTRY *InitMap, | |
IN UINTN InitMapCount | |
) | |
{ | |
UINTN Index; | |
IA32_MAP_ATTRIBUTE AttributeInInitMap; | |
IA32_MAP_ATTRIBUTE AttributeInMap; | |
IA32_MAP_ATTRIBUTE AttributeInMapEntrys; | |
IA32_MAP_ATTRIBUTE MaskInMapEntrys; | |
AttributeInMap.Uint64 = 0; | |
AttributeInMapEntrys.Uint64 = 0; | |
AttributeInInitMap.Uint64 = 0; | |
MaskInMapEntrys.Uint64 = 0; | |
// | |
// Assume every entry in maps does not overlap with each other | |
// | |
for (Index = 0; Index < MapCount; Index++) { | |
if ((Address >= Map[Index].LinearAddress) && (Address < (Map[Index].LinearAddress + Map[Index].Length))) { | |
AttributeInMap.Uint64 = (Map[Index].Attribute.Uint64 & mSupportedBit.Uint64); | |
AttributeInMap.Uint64 &= (~IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS_MASK); | |
AttributeInMap.Uint64 |= (Address - Map[Index].LinearAddress + IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&Map[Index].Attribute)) & IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS_MASK; | |
break; | |
} | |
} | |
// | |
// Assume every entry in maps does not overlap with each other | |
// | |
for (Index = 0; Index < InitMapCount; Index++) { | |
if ((Address >= InitMap[Index].LinearAddress) && (Address < (InitMap[Index].LinearAddress + InitMap[Index].Length))) { | |
AttributeInInitMap.Uint64 = (InitMap[Index].Attribute.Uint64 & mSupportedBit.Uint64); | |
AttributeInInitMap.Uint64 &= (~IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS_MASK); | |
AttributeInInitMap.Uint64 |= (Address - InitMap[Index].LinearAddress + IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&InitMap[Index].Attribute)) & IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS_MASK; | |
break; | |
} | |
} | |
AttributeInMapEntrys.Uint64 = AttributeInInitMap.Uint64; | |
for (Index = MapEntrys->InitCount; Index < MapEntrys->Count; Index++) { | |
if ((Address >= MapEntrys->Maps[Index].LinearAddress) && (Address < (MapEntrys->Maps[Index].LinearAddress + MapEntrys->Maps[Index].Length))) { | |
if (AttributeInMapEntrys.Bits.Present == 0) { | |
AttributeInMapEntrys.Uint64 = 0; | |
MaskInMapEntrys.Uint64 = 0; | |
} | |
MaskInMapEntrys.Uint64 |= MapEntrys->Maps[Index].Mask.Uint64; | |
AttributeInMapEntrys.Uint64 &= (~MapEntrys->Maps[Index].Mask.Uint64); | |
AttributeInMapEntrys.Uint64 |= (MapEntrys->Maps[Index].Attribute.Uint64 & MapEntrys->Maps[Index].Mask.Uint64); | |
if (IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&MapEntrys->Maps[Index].Mask) != 0) { | |
AttributeInMapEntrys.Uint64 &= (~IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS_MASK); | |
AttributeInMapEntrys.Uint64 |= (Address - MapEntrys->Maps[Index].LinearAddress + IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&MapEntrys->Maps[Index].Attribute)) & IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS_MASK; | |
} | |
} | |
} | |
if (AttributeInMap.Bits.Present == 0) { | |
if (AttributeInMapEntrys.Bits.Present == 0) { | |
return TRUE; | |
} | |
} | |
if ((AttributeInMap.Uint64 & MaskInMapEntrys.Uint64) != (AttributeInMapEntrys.Uint64 & MaskInMapEntrys.Uint64)) { | |
DEBUG ((DEBUG_INFO, "======detailed information begin=====\n")); | |
DEBUG ((DEBUG_INFO, "\nError: Detect different attribute on a point with linear address: 0x%lx\n", Address)); | |
DEBUG ((DEBUG_INFO, "By parsing page table, the point has Attribute 0x%lx, and map to physical address 0x%lx\n", IA32_MAP_ATTRIBUTE_ATTRIBUTES (&AttributeInMap) & MaskInMapEntrys.Uint64, IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&AttributeInMap))); | |
DEBUG ((DEBUG_INFO, "While according to inputs, the point should Attribute 0x%lx, and should map to physical address 0x%lx\n", IA32_MAP_ATTRIBUTE_ATTRIBUTES (&AttributeInMapEntrys) & MaskInMapEntrys.Uint64, IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&AttributeInMapEntrys))); | |
DEBUG ((DEBUG_INFO, "The total Mask is 0x%lx\n", MaskInMapEntrys.Uint64)); | |
if (MapEntrys->InitCount != 0) { | |
DEBUG ((DEBUG_INFO, "Below is the initialization status:\n")); | |
for (Index = 0; Index < InitMapCount; Index++) { | |
if ((Address >= InitMap[Index].LinearAddress) && (Address < (InitMap[Index].LinearAddress + InitMap[Index].Length))) { | |
DEBUG ((DEBUG_INFO, " *")); | |
} else { | |
DEBUG ((DEBUG_INFO, " ")); | |
} | |
DEBUG ((DEBUG_INFO, " %02d: {0x%lx, 0x%lx, 0x%lx}\n", Index, InitMap[Index].LinearAddress, InitMap[Index].LinearAddress + InitMap[Index].Length, InitMap[Index].Attribute.Uint64)); | |
} | |
} | |
DEBUG ((DEBUG_INFO, "Below is the inputs:\n")); | |
DEBUG ((DEBUG_INFO, " Index: {LinearAddress, LinearLimit, Mask, Attribute}\n")); | |
for (Index = MapEntrys->InitCount; Index < MapEntrys->Count; Index++) { | |
if ((Address >= MapEntrys->Maps[Index].LinearAddress) && (Address < (MapEntrys->Maps[Index].LinearAddress + MapEntrys->Maps[Index].Length))) { | |
DEBUG ((DEBUG_INFO, " *")); | |
} else { | |
DEBUG ((DEBUG_INFO, " ")); | |
} | |
DEBUG (( | |
DEBUG_INFO, | |
" %02d: {0x%lx, 0x%lx, 0x%lx,0x%lx}\n", | |
Index, | |
MapEntrys->Maps[Index].LinearAddress, | |
MapEntrys->Maps[Index].LinearAddress + MapEntrys->Maps[Index].Length, | |
MapEntrys->Maps[Index].Mask.Uint64, | |
MapEntrys->Maps[Index].Attribute.Uint64 | |
)); | |
} | |
DEBUG ((DEBUG_INFO, "Below is the dumped from pagetable:\n")); | |
for (Index = 0; Index < MapCount; Index++) { | |
if ((Address >= Map[Index].LinearAddress) && (Address < (Map[Index].LinearAddress + Map[Index].Length))) { | |
DEBUG ((DEBUG_INFO, " *")); | |
} else { | |
DEBUG ((DEBUG_INFO, " ")); | |
} | |
DEBUG ((DEBUG_INFO, "%02d: {0x%lx, 0x%lx, 0x%lx}\n", Index, Map[Index].LinearAddress, Map[Index].LinearAddress + Map[Index].Length, Map[Index].Attribute.Uint64)); | |
} | |
DEBUG ((DEBUG_INFO, "======detailed information done=====\n")); | |
return FALSE; | |
} | |
return TRUE; | |
} | |
/** | |
Append key point of a given address to Buffer | |
if buffer is NULL, only count needed count | |
@param[in, out] Buffer Buffer to contains all key point. | |
@param[in, out] Count Count of the key point. | |
@param[in] Address given address | |
**/ | |
VOID | |
AppendKeyPointToBuffer ( | |
IN OUT UINT64 *Buffer, | |
IN OUT UINTN *Count, | |
IN UINT64 Address | |
) | |
{ | |
if ( Buffer != NULL) { | |
Buffer[*Count] = Address; | |
(*Count)++; | |
Buffer[*Count] = Address+1; | |
(*Count)++; | |
Buffer[*Count] = Address-1; | |
(*Count)++; | |
} else { | |
(*Count) = (*Count) +3; | |
} | |
} | |
/** | |
Get all key points from a buffer | |
if buffer is NULL, only count needed count | |
@param[in] MapEntrys Record every memory ranges that is used as input | |
@param[in] Map Pointer to an array that describes multiple linear address ranges. | |
@param[in] MapCount Pointer to a UINTN that hold the actual number of entries in the Map. | |
@param[in, out] Buffer Buffer to contains all key point. | |
@param[in, out] Count Count of the key point. | |
**/ | |
VOID | |
GetKeyPointList ( | |
IN MAP_ENTRYS *MapEntrys, | |
IN IA32_MAP_ENTRY *Map, | |
IN UINTN MapCount, | |
IN OUT UINT64 *Buffer, | |
IN OUT UINTN *Count | |
) | |
{ | |
UINTN TemCount; | |
UINTN Index1; | |
UINTN Index2; | |
TemCount = 0; | |
for (Index1 = 0; Index1 < MapEntrys->Count; Index1++) { | |
AppendKeyPointToBuffer (Buffer, &TemCount, MapEntrys->Maps[Index1].LinearAddress); | |
AppendKeyPointToBuffer (Buffer, &TemCount, MapEntrys->Maps[Index1].LinearAddress + MapEntrys->Maps[Index1].Length); | |
} | |
for (Index2 = 0; Index2 < MapCount; Index2++) { | |
if (Buffer != NULL) { | |
for (Index1 = 0; Index1 < TemCount; Index1++) { | |
if (Buffer[Index1] == Map[Index2].LinearAddress) { | |
break; | |
} | |
} | |
if (Index1 < TemCount) { | |
continue; | |
} | |
} | |
AppendKeyPointToBuffer (Buffer, &TemCount, Map[Index2].LinearAddress); | |
} | |
for (Index2 = 0; Index2 < MapCount; Index2++) { | |
if (Buffer != NULL) { | |
for (Index1 = 0; Index1 < TemCount; Index1++) { | |
if (Buffer[Index1] == (Map[Index2].LinearAddress + Map[Index2].Length)) { | |
break; | |
} | |
} | |
if (Index1 < TemCount) { | |
continue; | |
} | |
} | |
AppendKeyPointToBuffer (Buffer, &TemCount, Map[Index2].LinearAddress + Map[Index2].Length); | |
} | |
*Count = TemCount; | |
} | |
/** | |
Generate random one range with randome attribute, and add it into pagetable | |
Compare the key point has same attribute | |
@param[in, out] PageTable The pointer to the page table to update, or pointer to NULL if a new page table is to be created. | |
@param[in] PagingMode The paging mode. | |
@param[in] MaxAddress Max Address. | |
@param[in] MapEntrys Record every memory ranges that is used as input | |
@param[in] PagesRecord Used to record memory usage for page table. | |
@param[in] InitMap Pointer to an array that describes init map entries. | |
@param[in] InitMapCount Pointer to a UINTN that hold the number of init map entries. | |
@retval UNIT_TEST_PASSED The test is successful. | |
**/ | |
UNIT_TEST_STATUS | |
SingleMapEntryTest ( | |
IN OUT UINTN *PageTable, | |
IN PAGING_MODE PagingMode, | |
IN UINT64 MaxAddress, | |
IN MAP_ENTRYS *MapEntrys, | |
IN ALLOCATE_PAGE_RECORDS *PagesRecord, | |
IN IA32_MAP_ENTRY *InitMap, | |
IN UINTN InitMapCount | |
) | |
{ | |
UINTN MapsIndex; | |
RETURN_STATUS Status; | |
UINTN PageTableBufferSize; | |
VOID *Buffer; | |
IA32_MAP_ENTRY *Map; | |
UINTN MapCount; | |
IA32_MAP_ENTRY *Map2; | |
UINTN MapCount2; | |
UINTN Index; | |
UINTN KeyPointCount; | |
UINTN NewKeyPointCount; | |
UINT64 *KeyPointBuffer; | |
UINTN Level; | |
UINT64 Value; | |
UNIT_TEST_STATUS TestStatus; | |
MAP_ENTRY *LastMapEntry; | |
IA32_MAP_ATTRIBUTE *Mask; | |
IA32_MAP_ATTRIBUTE *Attribute; | |
UINT64 LastNotPresentRegionStart; | |
BOOLEAN IsNotPresent; | |
BOOLEAN IsModified; | |
MapsIndex = MapEntrys->Count; | |
MapCount = 0; | |
LastNotPresentRegionStart = 0; | |
IsNotPresent = FALSE; | |
IsModified = FALSE; | |
SetRandomStack (); | |
GenerateSingleRandomMapEntry (MaxAddress, MapEntrys); | |
LastMapEntry = &MapEntrys->Maps[MapsIndex]; | |
Status = PageTableParse (*PageTable, PagingMode, NULL, &MapCount); | |
if (MapCount != 0) { | |
UT_ASSERT_EQUAL (Status, RETURN_BUFFER_TOO_SMALL); | |
Map = AllocatePages (EFI_SIZE_TO_PAGES (MapCount * sizeof (IA32_MAP_ENTRY))); | |
ASSERT (Map != NULL); | |
Status = PageTableParse (*PageTable, PagingMode, Map, &MapCount); | |
} | |
// | |
// Check if the generated MapEntrys->Maps[MapsIndex] contains not-present range. | |
// | |
if (LastMapEntry->Length > 0) { | |
for (Index = 0; Index < MapCount; Index++) { | |
if ((LastNotPresentRegionStart < Map[Index].LinearAddress) && | |
(LastMapEntry->LinearAddress < Map[Index].LinearAddress) && (LastMapEntry->LinearAddress + LastMapEntry->Length > LastNotPresentRegionStart)) | |
{ | |
// | |
// MapEntrys->Maps[MapsIndex] contains not-present range in exsiting page table. | |
// | |
break; | |
} | |
LastNotPresentRegionStart = Map[Index].LinearAddress + Map[Index].Length; | |
} | |
// | |
// Either LastMapEntry overlaps with the not-present region in the very end | |
// Or it overlaps with one in the middle | |
if (LastNotPresentRegionStart < LastMapEntry->LinearAddress + LastMapEntry->Length) { | |
IsNotPresent = TRUE; | |
} | |
} | |
PageTableBufferSize = 0; | |
Status = PageTableMap ( | |
PageTable, | |
PagingMode, | |
NULL, | |
&PageTableBufferSize, | |
LastMapEntry->LinearAddress, | |
LastMapEntry->Length, | |
&LastMapEntry->Attribute, | |
&LastMapEntry->Mask, | |
&IsModified | |
); | |
Attribute = &LastMapEntry->Attribute; | |
Mask = &LastMapEntry->Mask; | |
// | |
// If set [LinearAddress, LinearAddress+Attribute] to not preset, all | |
// other attributes should not be provided. | |
// | |
if ((LastMapEntry->Length > 0) && (Attribute->Bits.Present == 0) && (Mask->Bits.Present == 1) && (Mask->Uint64 > 1)) { | |
RemoveLastMapEntry (MapEntrys); | |
UT_ASSERT_EQUAL (Status, RETURN_INVALID_PARAMETER); | |
return UNIT_TEST_PASSED; | |
} | |
// | |
// Return Status for non-present range also should be InvalidParameter when: | |
// 1. Some of attributes are not provided when mapping non-present range to present. | |
// 2. Set any other attribute without setting the non-present range to Present. | |
// | |
if (IsNotPresent) { | |
if ((Mask->Bits.Present == 1) && (Attribute->Bits.Present == 1)) { | |
// | |
// Creating new page table or remapping non-present range to present. | |
// | |
if ((Mask->Bits.ReadWrite == 0) || (Mask->Bits.UserSupervisor == 0) || (Mask->Bits.WriteThrough == 0) || (Mask->Bits.CacheDisabled == 0) || | |
(Mask->Bits.Accessed == 0) || (Mask->Bits.Dirty == 0) || (Mask->Bits.Pat == 0) || (Mask->Bits.Global == 0) || | |
((Mask->Bits.PageTableBaseAddressLow == 0) && (Mask->Bits.PageTableBaseAddressHigh == 0)) || (Mask->Bits.ProtectionKey == 0) || (Mask->Bits.Nx == 0)) | |
{ | |
RemoveLastMapEntry (MapEntrys); | |
UT_ASSERT_EQUAL (Status, RETURN_INVALID_PARAMETER); | |
return UNIT_TEST_PASSED; | |
} | |
} else if ((Mask->Bits.Present == 0) && (Mask->Uint64 > 1)) { | |
// | |
// Only change other attributes for non-present range is not permitted. | |
// | |
RemoveLastMapEntry (MapEntrys); | |
UT_ASSERT_EQUAL (Status, RETURN_INVALID_PARAMETER); | |
return UNIT_TEST_PASSED; | |
} | |
} | |
if (PageTableBufferSize != 0) { | |
UT_ASSERT_EQUAL (Status, RETURN_BUFFER_TOO_SMALL); | |
// | |
// Allocate memory for Page table | |
// Note the memory is used in one complete Random test. | |
// | |
Buffer = PagesRecord->AllocatePagesForPageTable (PagesRecord, EFI_SIZE_TO_PAGES (PageTableBufferSize)); | |
UT_ASSERT_NOT_EQUAL (Buffer, NULL); | |
Status = PageTableMap ( | |
PageTable, | |
PagingMode, | |
Buffer, | |
&PageTableBufferSize, | |
LastMapEntry->LinearAddress, | |
LastMapEntry->Length, | |
&LastMapEntry->Attribute, | |
&LastMapEntry->Mask, | |
&IsModified | |
); | |
} | |
if (Status != RETURN_SUCCESS ) { | |
UT_ASSERT_EQUAL (Status, RETURN_SUCCESS); | |
} | |
UT_ASSERT_EQUAL (Status, RETURN_SUCCESS); | |
TestStatus = IsPageTableValid (*PageTable, PagingMode); | |
if (TestStatus != UNIT_TEST_PASSED) { | |
return TestStatus; | |
} | |
MapCount2 = 0; | |
Status = PageTableParse (*PageTable, PagingMode, NULL, &MapCount2); | |
if (MapCount2 != 0) { | |
UT_ASSERT_EQUAL (Status, RETURN_BUFFER_TOO_SMALL); | |
// | |
// Allocate memory for Map2 | |
// Note the memory is only used in this one Single MapEntry Test | |
// | |
Map2 = AllocatePages (EFI_SIZE_TO_PAGES (MapCount2 * sizeof (IA32_MAP_ENTRY))); | |
ASSERT (Map2 != NULL); | |
Status = PageTableParse (*PageTable, PagingMode, Map2, &MapCount2); | |
} | |
// | |
// Check if PageTable has been modified. | |
// | |
if (MapCount2 != MapCount) { | |
UT_ASSERT_EQUAL (IsModified, TRUE); | |
} else { | |
if (CompareMem (Map, Map2, MapCount2 * sizeof (IA32_MAP_ENTRY)) != 0) { | |
UT_ASSERT_EQUAL (IsModified, TRUE); | |
} else { | |
UT_ASSERT_EQUAL (IsModified, FALSE); | |
} | |
} | |
UT_ASSERT_EQUAL (Status, RETURN_SUCCESS); | |
// | |
// Allocate memory to record all key point | |
// Note the memory is only used in this one Single MapEntry Test | |
// | |
KeyPointCount = 0; | |
GetKeyPointList (MapEntrys, Map2, MapCount2, NULL, &KeyPointCount); | |
KeyPointBuffer = AllocatePages (EFI_SIZE_TO_PAGES (KeyPointCount * sizeof (UINT64))); | |
ASSERT (KeyPointBuffer != NULL); | |
NewKeyPointCount = 0; | |
GetKeyPointList (MapEntrys, Map2, MapCount2, KeyPointBuffer, &NewKeyPointCount); | |
// | |
// Compare all key point's attribute | |
// | |
for (Index = 0; Index < NewKeyPointCount; Index++) { | |
if (!CompareEntrysforOnePoint (KeyPointBuffer[Index], MapEntrys, Map2, MapCount2, InitMap, InitMapCount)) { | |
DEBUG ((DEBUG_INFO, "Error happens at below key point\n")); | |
DEBUG ((DEBUG_INFO, "Index = %d KeyPointBuffer[Index] = 0x%lx\n", Index, KeyPointBuffer[Index])); | |
Value = GetEntryFromPageTable (*PageTable, PagingMode, KeyPointBuffer[Index], &Level); | |
DEBUG ((DEBUG_INFO, "From Page table, this key point is in level %d entry, with entry value is 0x%lx\n", Level, Value)); | |
UT_ASSERT_TRUE (FALSE); | |
} | |
} | |
FreePages (KeyPointBuffer, EFI_SIZE_TO_PAGES (KeyPointCount * sizeof (UINT64))); | |
if (MapCount != 0) { | |
FreePages (Map, EFI_SIZE_TO_PAGES (MapCount * sizeof (IA32_MAP_ENTRY))); | |
} | |
if (MapCount2 != 0) { | |
FreePages (Map2, EFI_SIZE_TO_PAGES (MapCount2 * sizeof (IA32_MAP_ENTRY))); | |
} | |
return UNIT_TEST_PASSED; | |
} | |
/** | |
Allocate page and record the information in PagesRecord | |
@param[in] PagesRecord Point to a struct to record memory usage | |
@param[in] Pages Page count needed to allocate | |
@return A pointer to the allocated buffer or NULL if allocation fails. | |
**/ | |
VOID * | |
EFIAPI | |
RecordAllocatePages ( | |
IN ALLOCATE_PAGE_RECORDS *PagesRecord, | |
IN UINTN Pages | |
) | |
{ | |
VOID *Buffer; | |
Buffer = NULL; | |
if (PagesRecord->Count < PagesRecord->MaxCount) { | |
Buffer = AllocatePages (Pages); | |
PagesRecord->Records[PagesRecord->Count].Buffer = Buffer; | |
PagesRecord->Records[PagesRecord->Count].Pages = Pages; | |
PagesRecord->Count++; | |
} | |
ASSERT (Buffer != NULL); | |
return Buffer; | |
} | |
/** | |
The function is a whole Random test, it will call SingleMapEntryTest for ExpctedEntryNumber times | |
@param[in] ExpctedEntryNumber The count of random entry | |
@param[in] PagingMode The paging mode. | |
@retval UNIT_TEST_PASSED The test is successful. | |
**/ | |
UNIT_TEST_STATUS | |
MultipleMapEntryTest ( | |
IN UINTN ExpctedEntryNumber, | |
IN PAGING_MODE PagingMode | |
) | |
{ | |
UINTN PageTable; | |
UINT64 MaxAddress; | |
MAP_ENTRYS *MapEntrys; | |
ALLOCATE_PAGE_RECORDS *PagesRecord; | |
UINTN Index; | |
UNIT_TEST_STATUS TestStatus; | |
RETURN_STATUS Status; | |
IA32_MAP_ENTRY *InitMap; | |
UINTN InitMapCount; | |
MaxAddress = GetMaxAddress (PagingMode); | |
PageTable = 0; | |
MapEntrys = AllocatePages (EFI_SIZE_TO_PAGES (1000*sizeof (MAP_ENTRY) + sizeof (MAP_ENTRYS))); | |
ASSERT (MapEntrys != NULL); | |
MapEntrys->Count = 0; | |
MapEntrys->InitCount = 0; | |
MapEntrys->MaxCount = 1000; | |
PagesRecord = AllocatePages (EFI_SIZE_TO_PAGES (1000*sizeof (ALLOCATE_PAGE_RECORD) + sizeof (ALLOCATE_PAGE_RECORDS))); | |
ASSERT (PagesRecord != NULL); | |
PagesRecord->Count = 0; | |
PagesRecord->MaxCount = 1000; | |
PagesRecord->AllocatePagesForPageTable = RecordAllocatePages; | |
if (mRandomOption & MANUAL_CHANGE_PAGE_TABLE) { | |
ExpctedEntryNumber = ExpctedEntryNumber/2; | |
} | |
for (Index = 0; Index < ExpctedEntryNumber; Index++) { | |
TestStatus = SingleMapEntryTest ( | |
&PageTable, | |
PagingMode, | |
MaxAddress, | |
MapEntrys, | |
PagesRecord, | |
NULL, | |
0 | |
); | |
if (TestStatus != UNIT_TEST_PASSED) { | |
return TestStatus; | |
} | |
} | |
if ((mRandomOption & MANUAL_CHANGE_PAGE_TABLE) != 0) { | |
MapEntrys->InitCount = ExpctedEntryNumber; | |
TestStatus = ValidateAndRandomeModifyPageTable (PageTable, PagingMode); | |
RandomNumber = 0; | |
if (TestStatus != UNIT_TEST_PASSED) { | |
return TestStatus; | |
} | |
InitMapCount = 0; | |
Status = PageTableParse (PageTable, PagingMode, NULL, &InitMapCount); | |
if (InitMapCount != 0) { | |
UT_ASSERT_EQUAL (Status, RETURN_BUFFER_TOO_SMALL); | |
// | |
// Allocate memory for Maps | |
// Note the memory is only used in this one Single MapEntry Test | |
// | |
InitMap = AllocatePages (EFI_SIZE_TO_PAGES (InitMapCount * sizeof (IA32_MAP_ENTRY))); | |
ASSERT (InitMap != NULL); | |
Status = PageTableParse (PageTable, PagingMode, InitMap, &InitMapCount); | |
} | |
UT_ASSERT_EQUAL (Status, RETURN_SUCCESS); | |
for (Index = 0; Index < ExpctedEntryNumber; Index++) { | |
TestStatus = SingleMapEntryTest ( | |
&PageTable, | |
PagingMode, | |
MaxAddress, | |
MapEntrys, | |
PagesRecord, | |
InitMap, | |
InitMapCount | |
); | |
if (TestStatus != UNIT_TEST_PASSED) { | |
return TestStatus; | |
} | |
} | |
if (InitMapCount != 0) { | |
FreePages (InitMap, EFI_SIZE_TO_PAGES (InitMapCount*sizeof (IA32_MAP_ENTRY))); | |
} | |
} | |
FreePages ( | |
MapEntrys, | |
EFI_SIZE_TO_PAGES (1000*sizeof (MAP_ENTRY) + sizeof (MAP_ENTRYS)) | |
); | |
for (Index = 0; Index < PagesRecord->Count; Index++) { | |
FreePages (PagesRecord->Records[Index].Buffer, PagesRecord->Records[Index].Pages); | |
} | |
FreePages (PagesRecord, EFI_SIZE_TO_PAGES (1000*sizeof (ALLOCATE_PAGE_RECORD) + sizeof (ALLOCATE_PAGE_RECORDS))); | |
return UNIT_TEST_PASSED; | |
} | |
/** | |
Random Test | |
@param[in] Context [Optional] An optional parameter that enables: | |
1) test-case reuse with varied parameters and | |
2) test-case re-entry for Target tests that need a | |
reboot. This parameter is a VOID* and it is the | |
responsibility of the test author to ensure that the | |
contents are well understood by all test cases that may | |
consume it. | |
@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 | |
TestCaseforRandomTest ( | |
IN UNIT_TEST_CONTEXT Context | |
) | |
{ | |
UNIT_TEST_STATUS Status; | |
UINTN Index; | |
UT_ASSERT_EQUAL (RandomSeed (NULL, 0), TRUE); | |
UT_ASSERT_EQUAL (Random32 (100, 100), 100); | |
UT_ASSERT_EQUAL (Random64 (100, 100), 100); | |
UT_ASSERT_TRUE ((Random32 (9, 10) >= 9) & (Random32 (9, 10) <= 10)); | |
UT_ASSERT_TRUE ((Random64 (9, 10) >= 9) & (Random64 (9, 10) <= 10)); | |
mSupportedBit.Uint64 = 0; | |
mSupportedBit.Bits.Present = 1; | |
mSupportedBit.Bits.ReadWrite = 1; | |
mSupportedBit.Bits.UserSupervisor = 1; | |
mSupportedBit.Bits.WriteThrough = 1; | |
mSupportedBit.Bits.CacheDisabled = 1; | |
mSupportedBit.Bits.Accessed = 1; | |
mSupportedBit.Bits.Dirty = 1; | |
mSupportedBit.Bits.Pat = 1; | |
mSupportedBit.Bits.Global = 1; | |
mSupportedBit.Bits.ProtectionKey = 0xF; | |
if (((CPU_PAGE_TABLE_LIB_RANDOM_TEST_CONTEXT *)Context)->PagingMode == PagingPae) { | |
mSupportedBit.Bits.ProtectionKey = 0; | |
} | |
mSupportedBit.Bits.Nx = 1; | |
mRandomOption = ((CPU_PAGE_TABLE_LIB_RANDOM_TEST_CONTEXT *)Context)->RandomOption; | |
mNumberIndex = 0; | |
for (Index = 0; Index < ((CPU_PAGE_TABLE_LIB_RANDOM_TEST_CONTEXT *)Context)->TestCount; Index++) { | |
Status = MultipleMapEntryTest ( | |
((CPU_PAGE_TABLE_LIB_RANDOM_TEST_CONTEXT *)Context)->TestRangeCount, | |
((CPU_PAGE_TABLE_LIB_RANDOM_TEST_CONTEXT *)Context)->PagingMode | |
); | |
if (Status != UNIT_TEST_PASSED) { | |
return Status; | |
} | |
DEBUG ((DEBUG_INFO, ".")); | |
} | |
DEBUG ((DEBUG_INFO, "\n")); | |
return UNIT_TEST_PASSED; | |
} |