/** @file | |
Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "LightMemoryTest.h" | |
// | |
// Global: | |
// Since this driver will only ever produce one instance of the memory test | |
// protocol, so we do not need to dynamically allocate the PrivateData. | |
// | |
EFI_PHYSICAL_ADDRESS mCurrentAddress; | |
LIST_ENTRY *mCurrentLink; | |
NONTESTED_MEMORY_RANGE *mCurrentRange; | |
UINT64 mTestedSystemMemory; | |
UINT64 mNonTestedSystemMemory; | |
UINT32 GenericMemoryTestMonoPattern[GENERIC_CACHELINE_SIZE / 4] = { | |
0x5a5a5a5a, | |
0xa5a5a5a5, | |
0x5a5a5a5a, | |
0xa5a5a5a5, | |
0x5a5a5a5a, | |
0xa5a5a5a5, | |
0x5a5a5a5a, | |
0xa5a5a5a5, | |
0x5a5a5a5a, | |
0xa5a5a5a5, | |
0x5a5a5a5a, | |
0xa5a5a5a5, | |
0x5a5a5a5a, | |
0xa5a5a5a5, | |
0x5a5a5a5a, | |
0xa5a5a5a5 | |
}; | |
/** | |
Compares the contents of two buffers. | |
This function compares Length bytes of SourceBuffer to Length bytes of DestinationBuffer. | |
If all Length bytes of the two buffers are identical, then 0 is returned. Otherwise, the | |
value returned is the first mismatched byte in SourceBuffer subtracted from the first | |
mismatched byte in DestinationBuffer. | |
If Length = 0, then ASSERT(). | |
@param[in] DestinationBuffer The pointer to the destination buffer to compare. | |
@param[in] SourceBuffer The pointer to the source buffer to compare. | |
@param[in] Length The number of bytes to compare. | |
@return 0 All Length bytes of the two buffers are identical. | |
@retval Non-zero The first mismatched byte in SourceBuffer subtracted from the first | |
mismatched byte in DestinationBuffer. | |
**/ | |
INTN | |
EFIAPI | |
CompareMemWithoutCheckArgument ( | |
IN CONST VOID *DestinationBuffer, | |
IN CONST VOID *SourceBuffer, | |
IN UINTN Length | |
) | |
{ | |
ASSERT (Length > 0); | |
while ((--Length != 0) && | |
(*(INT8 *)DestinationBuffer == *(INT8 *)SourceBuffer)) | |
{ | |
DestinationBuffer = (INT8 *)DestinationBuffer + 1; | |
SourceBuffer = (INT8 *)SourceBuffer + 1; | |
} | |
return (INTN)*(UINT8 *)DestinationBuffer - (INTN)*(UINT8 *)SourceBuffer; | |
} | |
/** | |
Construct the system base memory range through GCD service. | |
@param[in] Private Point to generic memory test driver's private data. | |
@retval EFI_SUCCESS Successful construct the base memory range through GCD service. | |
@retval EFI_OUT_OF_RESOURCE Could not allocate needed resource from base memory. | |
@retval Others Failed to construct base memory range through GCD service. | |
**/ | |
EFI_STATUS | |
ConstructBaseMemoryRange ( | |
IN GENERIC_MEMORY_TEST_PRIVATE *Private | |
) | |
{ | |
UINTN NumberOfDescriptors; | |
EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; | |
UINTN Index; | |
// | |
// Base memory will always below 4G | |
// | |
gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap); | |
for (Index = 0; Index < NumberOfDescriptors; Index++) { | |
if ((MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeSystemMemory) || | |
(MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeMoreReliable)) | |
{ | |
Private->BaseMemorySize += MemorySpaceMap[Index].Length; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Destroy the link list base on the correspond link list type. | |
@param[in] Private Point to generic memory test driver's private data. | |
**/ | |
VOID | |
DestroyLinkList ( | |
IN GENERIC_MEMORY_TEST_PRIVATE *Private | |
) | |
{ | |
LIST_ENTRY *Link; | |
NONTESTED_MEMORY_RANGE *NontestedRange; | |
Link = Private->NonTestedMemRanList.BackLink; | |
while (Link != &Private->NonTestedMemRanList) { | |
RemoveEntryList (Link); | |
NontestedRange = NONTESTED_MEMORY_RANGE_FROM_LINK (Link); | |
gBS->FreePool (NontestedRange); | |
Link = Private->NonTestedMemRanList.BackLink; | |
} | |
} | |
/** | |
Convert the memory range to tested. | |
@param BaseAddress Base address of the memory range. | |
@param Length Length of the memory range. | |
@param Capabilities Capabilities of the memory range. | |
@retval EFI_SUCCESS The memory range is converted to tested. | |
@retval others Error happens. | |
**/ | |
EFI_STATUS | |
ConvertToTestedMemory ( | |
IN UINT64 BaseAddress, | |
IN UINT64 Length, | |
IN UINT64 Capabilities | |
) | |
{ | |
EFI_STATUS Status; | |
Status = gDS->RemoveMemorySpace ( | |
BaseAddress, | |
Length | |
); | |
if (!EFI_ERROR (Status)) { | |
Status = gDS->AddMemorySpace ( | |
((Capabilities & EFI_MEMORY_MORE_RELIABLE) == EFI_MEMORY_MORE_RELIABLE) ? | |
EfiGcdMemoryTypeMoreReliable : EfiGcdMemoryTypeSystemMemory, | |
BaseAddress, | |
Length, | |
Capabilities &~ | |
(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED | EFI_MEMORY_RUNTIME) | |
); | |
} | |
return Status; | |
} | |
/** | |
Add the extened memory to whole system memory map. | |
@param[in] Private Point to generic memory test driver's private data. | |
@retval EFI_SUCCESS Successful add all the extended memory to system memory map. | |
@retval Others Failed to add the tested extended memory. | |
**/ | |
EFI_STATUS | |
UpdateMemoryMap ( | |
IN GENERIC_MEMORY_TEST_PRIVATE *Private | |
) | |
{ | |
LIST_ENTRY *Link; | |
NONTESTED_MEMORY_RANGE *Range; | |
Link = Private->NonTestedMemRanList.ForwardLink; | |
while (Link != &Private->NonTestedMemRanList) { | |
Range = NONTESTED_MEMORY_RANGE_FROM_LINK (Link); | |
ConvertToTestedMemory ( | |
Range->StartAddress, | |
Range->Length, | |
Range->Capabilities &~ | |
(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED | EFI_MEMORY_RUNTIME) | |
); | |
Link = Link->ForwardLink; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Test a range of the memory directly . | |
@param[in] Private Point to generic memory test driver's private data. | |
@param[in] StartAddress Starting address of the memory range to be tested. | |
@param[in] Length Length in bytes of the memory range to be tested. | |
@param[in] Capabilities The bit mask of attributes that the memory range supports. | |
@retval EFI_SUCCESS Successful test the range of memory. | |
@retval Others Failed to test the range of memory. | |
**/ | |
EFI_STATUS | |
DirectRangeTest ( | |
IN GENERIC_MEMORY_TEST_PRIVATE *Private, | |
IN EFI_PHYSICAL_ADDRESS StartAddress, | |
IN UINT64 Length, | |
IN UINT64 Capabilities | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// Perform a dummy memory test, so directly write the pattern to all range | |
// | |
WriteMemory (Private, StartAddress, Length); | |
// | |
// Verify the memory range | |
// | |
Status = VerifyMemory (Private, StartAddress, Length); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Add the tested compatible memory to system memory using GCD service | |
// | |
ConvertToTestedMemory ( | |
StartAddress, | |
Length, | |
Capabilities &~ | |
(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED | EFI_MEMORY_RUNTIME) | |
); | |
return EFI_SUCCESS; | |
} | |
/** | |
Construct the system non-tested memory range through GCD service. | |
@param[in] Private Point to generic memory test driver's private data. | |
@retval EFI_SUCCESS Successful construct the non-tested memory range through GCD service. | |
@retval EFI_OUT_OF_RESOURCE Could not allocate needed resource from base memory. | |
@retval Others Failed to construct non-tested memory range through GCD service. | |
**/ | |
EFI_STATUS | |
ConstructNonTestedMemoryRange ( | |
IN GENERIC_MEMORY_TEST_PRIVATE *Private | |
) | |
{ | |
NONTESTED_MEMORY_RANGE *Range; | |
BOOLEAN NoFound; | |
UINTN NumberOfDescriptors; | |
EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; | |
UINTN Index; | |
// | |
// Non tested memory range may be span 4G here | |
// | |
NoFound = TRUE; | |
gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap); | |
for (Index = 0; Index < NumberOfDescriptors; Index++) { | |
if ((MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeReserved) && | |
((MemorySpaceMap[Index].Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) == | |
(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)) | |
) | |
{ | |
NoFound = FALSE; | |
// | |
// Light version do not need to process >4G memory range | |
// | |
gBS->AllocatePool ( | |
EfiBootServicesData, | |
sizeof (NONTESTED_MEMORY_RANGE), | |
(VOID **)&Range | |
); | |
Range->Signature = EFI_NONTESTED_MEMORY_RANGE_SIGNATURE; | |
Range->StartAddress = MemorySpaceMap[Index].BaseAddress; | |
Range->Length = MemorySpaceMap[Index].Length; | |
Range->Capabilities = MemorySpaceMap[Index].Capabilities; | |
mNonTestedSystemMemory += MemorySpaceMap[Index].Length; | |
InsertTailList (&Private->NonTestedMemRanList, &Range->Link); | |
} | |
} | |
if (NoFound) { | |
return EFI_NOT_FOUND; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Write the memory test pattern into a range of physical memory. | |
@param[in] Private Point to generic memory test driver's private data. | |
@param[in] Start The memory range's start address. | |
@param[in] Size The memory range's size. | |
@retval EFI_SUCCESS Successful write the test pattern into the non-tested memory. | |
@retval Others The test pattern may not really write into the physical memory. | |
**/ | |
EFI_STATUS | |
WriteMemory ( | |
IN GENERIC_MEMORY_TEST_PRIVATE *Private, | |
IN EFI_PHYSICAL_ADDRESS Start, | |
IN UINT64 Size | |
) | |
{ | |
EFI_PHYSICAL_ADDRESS Address; | |
Address = Start; | |
// | |
// Add 4G memory address check for IA32 platform | |
// NOTE: Without page table, there is no way to use memory above 4G. | |
// | |
if (Start + Size > MAX_ADDRESS) { | |
return EFI_SUCCESS; | |
} | |
while (Address < (Start + Size)) { | |
CopyMem ((VOID *)(UINTN)Address, Private->MonoPattern, Private->MonoTestSize); | |
Address += Private->CoverageSpan; | |
} | |
// | |
// bug bug: we may need GCD service to make the code cache and data uncache, | |
// if GCD do not support it or return fail, then just flush the whole cache. | |
// | |
if (Private->Cpu != NULL) { | |
Private->Cpu->FlushDataCache (Private->Cpu, Start, Size, EfiCpuFlushTypeWriteBackInvalidate); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Verify the range of physical memory which covered by memory test pattern. | |
This function will also do not return any informatin just cause system reset, | |
because the handle error encount fatal error and disable the bad DIMMs. | |
@param[in] Private Point to generic memory test driver's private data. | |
@param[in] Start The memory range's start address. | |
@param[in] Size The memory range's size. | |
@retval EFI_SUCCESS Successful verify the range of memory, no errors' location found. | |
@retval Others The range of memory have errors contained. | |
**/ | |
EFI_STATUS | |
VerifyMemory ( | |
IN GENERIC_MEMORY_TEST_PRIVATE *Private, | |
IN EFI_PHYSICAL_ADDRESS Start, | |
IN UINT64 Size | |
) | |
{ | |
EFI_PHYSICAL_ADDRESS Address; | |
INTN ErrorFound; | |
EFI_MEMORY_EXTENDED_ERROR_DATA *ExtendedErrorData; | |
Address = Start; | |
ExtendedErrorData = NULL; | |
// | |
// Add 4G memory address check for IA32 platform | |
// NOTE: Without page table, there is no way to use memory above 4G. | |
// | |
if (Start + Size > MAX_ADDRESS) { | |
return EFI_SUCCESS; | |
} | |
// | |
// Use the software memory test to check whether have detected miscompare | |
// error here. If there is miscompare error here then check if generic | |
// memory test driver can disable the bad DIMM. | |
// | |
while (Address < (Start + Size)) { | |
ErrorFound = CompareMemWithoutCheckArgument ( | |
(VOID *)(UINTN)(Address), | |
Private->MonoPattern, | |
Private->MonoTestSize | |
); | |
if (ErrorFound != 0) { | |
// | |
// Report uncorrectable errors | |
// | |
ExtendedErrorData = AllocateZeroPool (sizeof (EFI_MEMORY_EXTENDED_ERROR_DATA)); | |
if (ExtendedErrorData == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
ExtendedErrorData->DataHeader.HeaderSize = (UINT16)sizeof (EFI_STATUS_CODE_DATA); | |
ExtendedErrorData->DataHeader.Size = (UINT16)(sizeof (EFI_MEMORY_EXTENDED_ERROR_DATA) - sizeof (EFI_STATUS_CODE_DATA)); | |
ExtendedErrorData->Granularity = EFI_MEMORY_ERROR_DEVICE; | |
ExtendedErrorData->Operation = EFI_MEMORY_OPERATION_READ; | |
ExtendedErrorData->Syndrome = 0x0; | |
ExtendedErrorData->Address = Address; | |
ExtendedErrorData->Resolution = 0x40; | |
REPORT_STATUS_CODE_EX ( | |
EFI_ERROR_CODE, | |
EFI_COMPUTING_UNIT_MEMORY | EFI_CU_MEMORY_EC_UNCORRECTABLE, | |
0, | |
&gEfiGenericMemTestProtocolGuid, | |
NULL, | |
(UINT8 *)ExtendedErrorData + sizeof (EFI_STATUS_CODE_DATA), | |
ExtendedErrorData->DataHeader.Size | |
); | |
return EFI_DEVICE_ERROR; | |
} | |
Address += Private->CoverageSpan; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Initialize the generic memory test. | |
@param[in] This The protocol instance pointer. | |
@param[in] Level The coverage level of the memory test. | |
@param[out] RequireSoftECCInit Indicate if the memory need software ECC init. | |
@retval EFI_SUCCESS The generic memory test is initialized correctly. | |
@retval EFI_NO_MEDIA The system had no memory to be tested. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
InitializeMemoryTest ( | |
IN EFI_GENERIC_MEMORY_TEST_PROTOCOL *This, | |
IN EXTENDMEM_COVERAGE_LEVEL Level, | |
OUT BOOLEAN *RequireSoftECCInit | |
) | |
{ | |
EFI_STATUS Status; | |
GENERIC_MEMORY_TEST_PRIVATE *Private; | |
EFI_CPU_ARCH_PROTOCOL *Cpu; | |
Private = GENERIC_MEMORY_TEST_PRIVATE_FROM_THIS (This); | |
*RequireSoftECCInit = FALSE; | |
// | |
// This is initialize for default value, but some value may be reset base on | |
// platform memory test driver. | |
// | |
Private->CoverLevel = Level; | |
Private->BdsBlockSize = TEST_BLOCK_SIZE; | |
Private->MonoPattern = GenericMemoryTestMonoPattern; | |
Private->MonoTestSize = GENERIC_CACHELINE_SIZE; | |
// | |
// Initialize several internal link list | |
// | |
InitializeListHead (&Private->NonTestedMemRanList); | |
// | |
// Construct base memory range | |
// | |
ConstructBaseMemoryRange (Private); | |
// | |
// get the cpu arch protocol to support flash cache | |
// | |
Status = gBS->LocateProtocol ( | |
&gEfiCpuArchProtocolGuid, | |
NULL, | |
(VOID **)&Cpu | |
); | |
if (!EFI_ERROR (Status)) { | |
Private->Cpu = Cpu; | |
} | |
// | |
// Create the CoverageSpan of the memory test base on the coverage level | |
// | |
switch (Private->CoverLevel) { | |
case EXTENSIVE: | |
Private->CoverageSpan = GENERIC_CACHELINE_SIZE; | |
break; | |
case SPARSE: | |
Private->CoverageSpan = SPARSE_SPAN_SIZE; | |
break; | |
// | |
// Even the BDS do not need to test any memory, but in some case it | |
// still need to init ECC memory. | |
// | |
default: | |
Private->CoverageSpan = QUICK_SPAN_SIZE; | |
break; | |
} | |
// | |
// This is the first time we construct the non-tested memory range, if no | |
// extended memory found, we know the system have not any extended memory | |
// need to be test | |
// | |
Status = ConstructNonTestedMemoryRange (Private); | |
if (Status == EFI_NOT_FOUND) { | |
return EFI_NO_MEDIA; | |
} | |
// | |
// ready to perform the R/W/V memory test | |
// | |
mTestedSystemMemory = Private->BaseMemorySize; | |
mCurrentLink = Private->NonTestedMemRanList.ForwardLink; | |
mCurrentRange = NONTESTED_MEMORY_RANGE_FROM_LINK (mCurrentLink); | |
mCurrentAddress = mCurrentRange->StartAddress; | |
return EFI_SUCCESS; | |
} | |
/** | |
Perform the memory test. | |
@param[in] This The protocol instance pointer. | |
@param[out] TestedMemorySize Return the tested extended memory size. | |
@param[out] TotalMemorySize Return the whole system physical memory size. | |
The total memory size does not include memory in a slot with a disabled DIMM. | |
@param[out] ErrorOut TRUE if the memory error occurred. | |
@param[in] IfTestAbort Indicates that the user pressed "ESC" to skip the memory test. | |
@retval EFI_SUCCESS One block of memory passed the test. | |
@retval EFI_NOT_FOUND All memory blocks have already been tested. | |
@retval EFI_DEVICE_ERROR Memory device error occurred, and no agent can handle it. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
GenPerformMemoryTest ( | |
IN EFI_GENERIC_MEMORY_TEST_PROTOCOL *This, | |
OUT UINT64 *TestedMemorySize, | |
OUT UINT64 *TotalMemorySize, | |
OUT BOOLEAN *ErrorOut, | |
IN BOOLEAN TestAbort | |
) | |
{ | |
EFI_STATUS Status; | |
GENERIC_MEMORY_TEST_PRIVATE *Private; | |
EFI_MEMORY_RANGE_EXTENDED_DATA *RangeData; | |
UINT64 BlockBoundary; | |
Private = GENERIC_MEMORY_TEST_PRIVATE_FROM_THIS (This); | |
*ErrorOut = FALSE; | |
RangeData = NULL; | |
BlockBoundary = 0; | |
// | |
// In extensive mode the boundary of "mCurrentRange->Length" may will lost | |
// some range that is not Private->BdsBlockSize size boundary, so need | |
// the software mechanism to confirm all memory location be covered. | |
// | |
if (mCurrentAddress < (mCurrentRange->StartAddress + mCurrentRange->Length)) { | |
if ((mCurrentAddress + Private->BdsBlockSize) <= (mCurrentRange->StartAddress + mCurrentRange->Length)) { | |
BlockBoundary = Private->BdsBlockSize; | |
} else { | |
BlockBoundary = mCurrentRange->StartAddress + mCurrentRange->Length - mCurrentAddress; | |
} | |
// | |
// If TestAbort is true, means user cancel the memory test | |
// | |
if (!TestAbort && (Private->CoverLevel != IGNORE)) { | |
// | |
// Report status code of every memory range | |
// | |
RangeData = AllocateZeroPool (sizeof (EFI_MEMORY_RANGE_EXTENDED_DATA)); | |
if (RangeData == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
RangeData->DataHeader.HeaderSize = (UINT16)sizeof (EFI_STATUS_CODE_DATA); | |
RangeData->DataHeader.Size = (UINT16)(sizeof (EFI_MEMORY_RANGE_EXTENDED_DATA) - sizeof (EFI_STATUS_CODE_DATA)); | |
RangeData->Start = mCurrentAddress; | |
RangeData->Length = BlockBoundary; | |
REPORT_STATUS_CODE_EX ( | |
EFI_PROGRESS_CODE, | |
EFI_COMPUTING_UNIT_MEMORY | EFI_CU_MEMORY_PC_TEST, | |
0, | |
&gEfiGenericMemTestProtocolGuid, | |
NULL, | |
(UINT8 *)RangeData + sizeof (EFI_STATUS_CODE_DATA), | |
RangeData->DataHeader.Size | |
); | |
// | |
// The software memory test (R/W/V) perform here. It will detect the | |
// memory mis-compare error. | |
// | |
WriteMemory (Private, mCurrentAddress, BlockBoundary); | |
Status = VerifyMemory (Private, mCurrentAddress, BlockBoundary); | |
if (EFI_ERROR (Status)) { | |
// | |
// If perform here, means there is mis-compare error, and no agent can | |
// handle it, so we return to BDS EFI_DEVICE_ERROR. | |
// | |
*ErrorOut = TRUE; | |
return EFI_DEVICE_ERROR; | |
} | |
} | |
mTestedSystemMemory += BlockBoundary; | |
*TestedMemorySize = mTestedSystemMemory; | |
// | |
// If the memory test restart after the platform driver disable dimms, | |
// the NonTestSystemMemory may be changed, but the base memory size will | |
// not changed, so we can get the current total memory size. | |
// | |
*TotalMemorySize = Private->BaseMemorySize + mNonTestedSystemMemory; | |
// | |
// Update the current test address pointing to next BDS BLOCK | |
// | |
mCurrentAddress += Private->BdsBlockSize; | |
return EFI_SUCCESS; | |
} | |
// | |
// Change to next non tested memory range | |
// | |
mCurrentLink = mCurrentLink->ForwardLink; | |
if (mCurrentLink != &Private->NonTestedMemRanList) { | |
mCurrentRange = NONTESTED_MEMORY_RANGE_FROM_LINK (mCurrentLink); | |
mCurrentAddress = mCurrentRange->StartAddress; | |
return EFI_SUCCESS; | |
} else { | |
// | |
// Here means all the memory test have finished | |
// | |
*TestedMemorySize = mTestedSystemMemory; | |
*TotalMemorySize = Private->BaseMemorySize + mNonTestedSystemMemory; | |
return EFI_NOT_FOUND; | |
} | |
} | |
/** | |
Finish the memory test. | |
@param[in] This The protocol instance pointer. | |
@retval EFI_SUCCESS Success. All resources used in the memory test are freed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
GenMemoryTestFinished ( | |
IN EFI_GENERIC_MEMORY_TEST_PROTOCOL *This | |
) | |
{ | |
EFI_STATUS Status; | |
GENERIC_MEMORY_TEST_PRIVATE *Private; | |
Private = GENERIC_MEMORY_TEST_PRIVATE_FROM_THIS (This); | |
// | |
// Perform Data and Address line test only if not ignore memory test | |
// | |
if (Private->CoverLevel != IGNORE) { | |
Status = PerformAddressDataLineTest (Private); | |
ASSERT_EFI_ERROR (Status); | |
} | |
// | |
// Add the non tested memory range to system memory map through GCD service | |
// | |
UpdateMemoryMap (Private); | |
// | |
// we need to free all the memory allocate | |
// | |
DestroyLinkList (Private); | |
return EFI_SUCCESS; | |
} | |
/** | |
Provides the capability to test the compatible range used by some special drivers. | |
@param[in] This The protocol instance pointer. | |
@param[in] StartAddress The start address of the compatible memory range that | |
must be below 16M. | |
@param[in] Length The compatible memory range's length. | |
@retval EFI_SUCCESS The compatible memory range pass the memory test. | |
@retval EFI_INVALID_PARAMETER The compatible memory range are not below Low 16M. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
GenCompatibleRangeTest ( | |
IN EFI_GENERIC_MEMORY_TEST_PROTOCOL *This, | |
IN EFI_PHYSICAL_ADDRESS StartAddress, | |
IN UINT64 Length | |
) | |
{ | |
EFI_STATUS Status; | |
GENERIC_MEMORY_TEST_PRIVATE *Private; | |
EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor; | |
EFI_PHYSICAL_ADDRESS CurrentBase; | |
UINT64 CurrentLength; | |
Private = GENERIC_MEMORY_TEST_PRIVATE_FROM_THIS (This); | |
// | |
// Check if the parameter is below 16MB | |
// | |
if (StartAddress + Length > 0x1000000) { | |
return EFI_INVALID_PARAMETER; | |
} | |
CurrentBase = StartAddress; | |
do { | |
// | |
// Check the required memory range status; if the required memory range span | |
// the different GCD memory descriptor, it may be cause different action. | |
// | |
Status = gDS->GetMemorySpaceDescriptor ( | |
CurrentBase, | |
&Descriptor | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if ((Descriptor.GcdMemoryType == EfiGcdMemoryTypeReserved) && | |
((Descriptor.Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) == | |
(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)) | |
) | |
{ | |
CurrentLength = Descriptor.BaseAddress + Descriptor.Length - CurrentBase; | |
if (CurrentBase + CurrentLength > StartAddress + Length) { | |
CurrentLength = StartAddress + Length - CurrentBase; | |
} | |
Status = DirectRangeTest ( | |
Private, | |
CurrentBase, | |
CurrentLength, | |
Descriptor.Capabilities | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
CurrentBase = Descriptor.BaseAddress + Descriptor.Length; | |
} while (CurrentBase < StartAddress + Length); | |
// | |
// Here means the required range already be tested, so just return success. | |
// | |
return EFI_SUCCESS; | |
} | |
/** | |
Perform the address line walking ones test. | |
@param[in] Private Point to generic memory test driver's private data. | |
@retval EFI_SUCCESS Successful finished walking ones test. | |
@retval EFI_OUT_OF_RESOURCE Could not get resource in base memory. | |
@retval EFI_ACCESS_DENIED Code may can not run here because if walking one test | |
failed, system may be already halt. | |
**/ | |
EFI_STATUS | |
PerformAddressDataLineTest ( | |
IN GENERIC_MEMORY_TEST_PRIVATE *Private | |
) | |
{ | |
LIST_ENTRY *ExtendedLink; | |
NONTESTED_MEMORY_RANGE *ExtendedRange; | |
BOOLEAN InExtendedRange; | |
EFI_PHYSICAL_ADDRESS TestAddress; | |
// | |
// Light version no data line test, only perform the address line test | |
// | |
TestAddress = (EFI_PHYSICAL_ADDRESS)0x1; | |
while (TestAddress < MAX_ADDRESS && TestAddress > 0) { | |
// | |
// only test if the address falls in the enabled range | |
// | |
InExtendedRange = FALSE; | |
ExtendedLink = Private->NonTestedMemRanList.BackLink; | |
while (ExtendedLink != &Private->NonTestedMemRanList) { | |
ExtendedRange = NONTESTED_MEMORY_RANGE_FROM_LINK (ExtendedLink); | |
if ((TestAddress >= ExtendedRange->StartAddress) && | |
(TestAddress < (ExtendedRange->StartAddress + ExtendedRange->Length)) | |
) | |
{ | |
InExtendedRange = TRUE; | |
} | |
ExtendedLink = ExtendedLink->BackLink; | |
} | |
if (InExtendedRange) { | |
*(EFI_PHYSICAL_ADDRESS *)(UINTN)TestAddress = TestAddress; | |
Private->Cpu->FlushDataCache (Private->Cpu, TestAddress, 1, EfiCpuFlushTypeWriteBackInvalidate); | |
if (*(EFI_PHYSICAL_ADDRESS *)(UINTN)TestAddress != TestAddress) { | |
return EFI_ACCESS_DENIED; | |
} | |
} | |
TestAddress = LShiftU64 (TestAddress, 1); | |
} | |
return EFI_SUCCESS; | |
} | |
// | |
// Driver entry here | |
// | |
GENERIC_MEMORY_TEST_PRIVATE mGenericMemoryTestPrivate = { | |
EFI_GENERIC_MEMORY_TEST_PRIVATE_SIGNATURE, | |
NULL, | |
NULL, | |
{ | |
InitializeMemoryTest, | |
GenPerformMemoryTest, | |
GenMemoryTestFinished, | |
GenCompatibleRangeTest | |
}, | |
(EXTENDMEM_COVERAGE_LEVEL)0, | |
0, | |
0, | |
NULL, | |
0, | |
0, | |
{ | |
NULL, | |
NULL | |
} | |
}; | |
/** | |
The generic memory test driver's entry point. | |
It initializes private data to default value. | |
@param[in] ImageHandle The firmware allocated handle for the EFI image. | |
@param[in] SystemTable A pointer to the EFI System Table. | |
@retval EFI_SUCCESS The entry point is executed successfully. | |
@retval EFI_NOT_FOUND Can't find HandOff Hob in HobList. | |
@retval other Some error occurs when executing this entry point. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
GenericMemoryTestEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
VOID *HobList; | |
EFI_BOOT_MODE BootMode; | |
EFI_PEI_HOB_POINTERS Hob; | |
// | |
// Use the generic pattern to test compatible memory range | |
// | |
mGenericMemoryTestPrivate.MonoPattern = GenericMemoryTestMonoPattern; | |
mGenericMemoryTestPrivate.MonoTestSize = GENERIC_CACHELINE_SIZE; | |
// | |
// Get the platform boot mode | |
// | |
HobList = GetHobList (); | |
Hob.Raw = HobList; | |
if (Hob.Header->HobType != EFI_HOB_TYPE_HANDOFF) { | |
return EFI_NOT_FOUND; | |
} | |
BootMode = Hob.HandoffInformationTable->BootMode; | |
// | |
// Get the platform boot mode and create the default memory test coverage | |
// level and span size for compatible memory test using | |
// | |
switch (BootMode) { | |
case BOOT_WITH_FULL_CONFIGURATION: | |
case BOOT_WITH_DEFAULT_SETTINGS: | |
mGenericMemoryTestPrivate.CoverageSpan = SPARSE_SPAN_SIZE; | |
break; | |
case BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS: | |
mGenericMemoryTestPrivate.CoverageSpan = GENERIC_CACHELINE_SIZE; | |
break; | |
default: | |
mGenericMemoryTestPrivate.CoverageSpan = QUICK_SPAN_SIZE; | |
break; | |
} | |
// | |
// Install the protocol | |
// | |
Status = gBS->InstallProtocolInterface ( | |
&mGenericMemoryTestPrivate.Handle, | |
&gEfiGenericMemTestProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
&mGenericMemoryTestPrivate.GenericMemoryTest | |
); | |
return Status; | |
} |