mirror of
				https://github.com/acidanthera/audk.git
				synced 2025-10-27 01:03:45 +01:00 
			
		
		
		
	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
		
			
				
	
	
		
			907 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			907 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
|   Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
 | |
| 
 | |
|   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;
 | |
| }
 |