- stardard -> standard - doule -> double - defalut -> default - Pacakge -> Package - globa -> global - responsiblity -> responsibility - outputed -> outputted - specifiecd -> specified - Resuts -> Results - the a -> a - suported -> supported - assocated -> associated - TURE -> TRUE - successfull -> successfully - excute -> execute - reseting -> resetting - Retrive -> Retrieve - funciton -> function - paramter -> parameter - dependecy -> dependency - boundry -> boundary - permenantly -> permanently Cc: Feng Tian <feng.tian@intel.com> Cc: Star Zeng <star.zeng@intel.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Gary Lin <glin@suse.com> Reviewed-by: Feng Tian <feng.tian@intel.com> Reviewed-by: Star Zeng <star.zeng@intel.com>
		
			
				
	
	
		
			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 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
 | 
						|
  //
 | 
						|
  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;
 | 
						|
}
 |