From 7c636bd022d0ca308d1270d83bf3e782f88c88b6 Mon Sep 17 00:00:00 2001 From: jljusten Date: Fri, 1 Jul 2011 00:40:26 +0000 Subject: [PATCH] MdeModulePkg: Add GenericMemoryTestDxe driver Signed-off-by: jljusten Reviewed-by: mdkinney git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11937 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/MdeModulePkg.dsc | 1 + .../GenericMemoryTestDxe.inf | 59 ++ .../GenericMemoryTestDxe/LightMemoryTest.c | 906 ++++++++++++++++++ .../GenericMemoryTestDxe/LightMemoryTest.h | 342 +++++++ 4 files changed, 1308 insertions(+) create mode 100644 MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/GenericMemoryTestDxe.inf create mode 100644 MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.c create mode 100644 MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.h diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index a273c27263..8ebab61560 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -245,6 +245,7 @@ MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf MdeModulePkg/Universal/DriverSampleDxe/DriverSampleDxe.inf MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf + MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/GenericMemoryTestDxe.inf MdeModulePkg/Universal/MemoryTest/NullMemoryTestDxe/NullMemoryTestDxe.inf MdeModulePkg/Universal/Metronome/Metronome.inf MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf diff --git a/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/GenericMemoryTestDxe.inf b/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/GenericMemoryTestDxe.inf new file mode 100644 index 0000000000..5158a8c779 --- /dev/null +++ b/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/GenericMemoryTestDxe.inf @@ -0,0 +1,59 @@ +## @file +# Component description file for Generic memory test. +# +# This driver first constructs the non-tested memory range, +# then performs the R/W/V memory test. +# +# Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials are +# licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = GenericMemoryTestDxe + FILE_GUID = 9C1080EE-D02E-487f-9432-F3BF086EC180 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = GenericMemoryTestEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + LightMemoryTest.h + LightMemoryTest.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + BaseLib + ReportStatusCodeLib + DxeServicesTableLib + HobLib + UefiDriverEntryPoint + DebugLib + +[Protocols] + gEfiCpuArchProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiGenericMemTestProtocolGuid # PROTOCOL ALWAYS_PRODUCED + +[Depex] + gEfiCpuArchProtocolGuid + diff --git a/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.c b/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.c new file mode 100644 index 0000000000..6161274a94 --- /dev/null +++ b/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.c @@ -0,0 +1,906 @@ +/** @file + + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions + of the BSD License which accompanies this distribution. The + full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#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) { + 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;; + } +} + +/** + 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); + + gDS->RemoveMemorySpace ( + Range->StartAddress, + Range->Length + ); + + gDS->AddMemorySpace ( + EfiGcdMemoryTypeSystemMemory, + 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 + // + gDS->RemoveMemorySpace ( + StartAddress, + Length + ); + + gDS->AddMemorySpace ( + EfiGcdMemoryTypeSystemMemory, + 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 occured. + @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 occured, 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 boundry, 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 + // + 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; +} diff --git a/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.h b/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.h new file mode 100644 index 0000000000..391c8a4c81 --- /dev/null +++ b/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.h @@ -0,0 +1,342 @@ +/** @file + The generic memory test driver definition + + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions + of the BSD License which accompanies this distribution. The + full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _GENERIC_MEMORY_TEST_H_ +#define _GENERIC_MEMORY_TEST_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Some global define +// +#define GENERIC_CACHELINE_SIZE 0x40 + +// +// attributes for reserved memory before it is promoted to system memory +// +#define EFI_MEMORY_PRESENT 0x0100000000000000ULL +#define EFI_MEMORY_INITIALIZED 0x0200000000000000ULL +#define EFI_MEMORY_TESTED 0x0400000000000000ULL + +// +// The SPARSE_SPAN_SIZE size can not small then the MonoTestSize +// +#define TEST_BLOCK_SIZE 0x2000000 +#define QUICK_SPAN_SIZE (TEST_BLOCK_SIZE >> 2) +#define SPARSE_SPAN_SIZE (TEST_BLOCK_SIZE >> 4) + +// +// This structure records every nontested memory range parsed through GCD +// service. +// +#define EFI_NONTESTED_MEMORY_RANGE_SIGNATURE SIGNATURE_32 ('N', 'T', 'M', 'E') + +typedef struct { + UINTN Signature; + LIST_ENTRY Link; + EFI_PHYSICAL_ADDRESS StartAddress; + UINT64 Length; + UINT64 Capabilities; + BOOLEAN Above4G; + BOOLEAN AlreadyMapped; +} NONTESTED_MEMORY_RANGE; + +#define NONTESTED_MEMORY_RANGE_FROM_LINK(link) \ + CR ( \ + link, \ + NONTESTED_MEMORY_RANGE, \ + Link, \ + EFI_NONTESTED_MEMORY_RANGE_SIGNATURE \ + ) + +// +// This is the memory test driver's structure definition +// +#define EFI_GENERIC_MEMORY_TEST_PRIVATE_SIGNATURE SIGNATURE_32 ('G', 'E', 'M', 'T') + +typedef struct { + + UINTN Signature; + EFI_HANDLE Handle; + + // + // Cpu arch protocol's pointer + // + EFI_CPU_ARCH_PROTOCOL *Cpu; + + // + // generic memory test driver's protocol + // + EFI_GENERIC_MEMORY_TEST_PROTOCOL GenericMemoryTest; + + // + // memory test covered spans + // + EXTENDMEM_COVERAGE_LEVEL CoverLevel; + UINTN CoverageSpan; + UINT64 BdsBlockSize; + + // + // the memory test pattern and size every time R/W/V memory + // + VOID *MonoPattern; + UINTN MonoTestSize; + + // + // base memory's size which tested in PEI phase + // + UINT64 BaseMemorySize; + + // + // memory range list + // + LIST_ENTRY NonTestedMemRanList; + +} GENERIC_MEMORY_TEST_PRIVATE; + +#define GENERIC_MEMORY_TEST_PRIVATE_FROM_THIS(a) \ + CR ( \ + a, \ + GENERIC_MEMORY_TEST_PRIVATE, \ + GenericMemoryTest, \ + EFI_GENERIC_MEMORY_TEST_PRIVATE_SIGNATURE \ + ) + +// +// Function Prototypes +// + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 occured. + @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 occured, 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +#endif -- 2.39.2