| /** @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; | |
| } |