summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MdeModulePkg/MdeModulePkg.dsc1
-rw-r--r--MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/GenericMemoryTestDxe.inf59
-rw-r--r--MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.c906
-rw-r--r--MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.h342
4 files changed, 1308 insertions, 0 deletions
diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc
index a273c27263..8ebab61560 100644
--- a/MdeModulePkg/MdeModulePkg.dsc
+++ b/MdeModulePkg/MdeModulePkg.dsc
@@ -245,6 +245,7 @@
MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
MdeModulePkg/Universal/DriverSampleDxe/DriverSampleDxe.inf
MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
+ MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/GenericMemoryTestDxe.inf
MdeModulePkg/Universal/MemoryTest/NullMemoryTestDxe/NullMemoryTestDxe.inf
MdeModulePkg/Universal/Metronome/Metronome.inf
MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
diff --git a/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/GenericMemoryTestDxe.inf b/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/GenericMemoryTestDxe.inf
new file mode 100644
index 0000000000..5158a8c779
--- /dev/null
+++ b/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/GenericMemoryTestDxe.inf
@@ -0,0 +1,59 @@
+## @file
+# Component description file for Generic memory test.
+#
+# This driver first constructs the non-tested memory range,
+# then performs the R/W/V memory test.
+#
+# Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<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.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = GenericMemoryTestDxe
+ FILE_GUID = 9C1080EE-D02E-487f-9432-F3BF086EC180
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+
+ ENTRY_POINT = GenericMemoryTestEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ LightMemoryTest.h
+ LightMemoryTest.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ BaseMemoryLib
+ BaseLib
+ ReportStatusCodeLib
+ DxeServicesTableLib
+ HobLib
+ UefiDriverEntryPoint
+ DebugLib
+
+[Protocols]
+ gEfiCpuArchProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiGenericMemTestProtocolGuid # PROTOCOL ALWAYS_PRODUCED
+
+[Depex]
+ gEfiCpuArchProtocolGuid
+
diff --git a/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.c b/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.c
new file mode 100644
index 0000000000..6161274a94
--- /dev/null
+++ b/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.c
@@ -0,0 +1,906 @@
+/** @file
+
+ Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<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;
+}
diff --git a/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.h b/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.h
new file mode 100644
index 0000000000..391c8a4c81
--- /dev/null
+++ b/MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/LightMemoryTest.h
@@ -0,0 +1,342 @@
+/** @file
+ The generic memory test driver definition
+
+ Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<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.
+
+**/
+
+#ifndef _GENERIC_MEMORY_TEST_H_
+#define _GENERIC_MEMORY_TEST_H_
+
+#include <Guid/StatusCodeDataTypeId.h>
+#include <Protocol/GenericMemoryTest.h>
+#include <Protocol/Cpu.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/HobLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+//
+// Some global define
+//
+#define GENERIC_CACHELINE_SIZE 0x40
+
+//
+// attributes for reserved memory before it is promoted to system memory
+//
+#define EFI_MEMORY_PRESENT 0x0100000000000000ULL
+#define EFI_MEMORY_INITIALIZED 0x0200000000000000ULL
+#define EFI_MEMORY_TESTED 0x0400000000000000ULL
+
+//
+// The SPARSE_SPAN_SIZE size can not small then the MonoTestSize
+//
+#define TEST_BLOCK_SIZE 0x2000000
+#define QUICK_SPAN_SIZE (TEST_BLOCK_SIZE >> 2)
+#define SPARSE_SPAN_SIZE (TEST_BLOCK_SIZE >> 4)
+
+//
+// This structure records every nontested memory range parsed through GCD
+// service.
+//
+#define EFI_NONTESTED_MEMORY_RANGE_SIGNATURE SIGNATURE_32 ('N', 'T', 'M', 'E')
+
+typedef struct {
+ UINTN Signature;
+ LIST_ENTRY Link;
+ EFI_PHYSICAL_ADDRESS StartAddress;
+ UINT64 Length;
+ UINT64 Capabilities;
+ BOOLEAN Above4G;
+ BOOLEAN AlreadyMapped;
+} NONTESTED_MEMORY_RANGE;
+
+#define NONTESTED_MEMORY_RANGE_FROM_LINK(link) \
+ CR ( \
+ link, \
+ NONTESTED_MEMORY_RANGE, \
+ Link, \
+ EFI_NONTESTED_MEMORY_RANGE_SIGNATURE \
+ )
+
+//
+// This is the memory test driver's structure definition
+//
+#define EFI_GENERIC_MEMORY_TEST_PRIVATE_SIGNATURE SIGNATURE_32 ('G', 'E', 'M', 'T')
+
+typedef struct {
+
+ UINTN Signature;
+ EFI_HANDLE Handle;
+
+ //
+ // Cpu arch protocol's pointer
+ //
+ EFI_CPU_ARCH_PROTOCOL *Cpu;
+
+ //
+ // generic memory test driver's protocol
+ //
+ EFI_GENERIC_MEMORY_TEST_PROTOCOL GenericMemoryTest;
+
+ //
+ // memory test covered spans
+ //
+ EXTENDMEM_COVERAGE_LEVEL CoverLevel;
+ UINTN CoverageSpan;
+ UINT64 BdsBlockSize;
+
+ //
+ // the memory test pattern and size every time R/W/V memory
+ //
+ VOID *MonoPattern;
+ UINTN MonoTestSize;
+
+ //
+ // base memory's size which tested in PEI phase
+ //
+ UINT64 BaseMemorySize;
+
+ //
+ // memory range list
+ //
+ LIST_ENTRY NonTestedMemRanList;
+
+} GENERIC_MEMORY_TEST_PRIVATE;
+
+#define GENERIC_MEMORY_TEST_PRIVATE_FROM_THIS(a) \
+ CR ( \
+ a, \
+ GENERIC_MEMORY_TEST_PRIVATE, \
+ GenericMemoryTest, \
+ EFI_GENERIC_MEMORY_TEST_PRIVATE_SIGNATURE \
+ )
+
+//
+// Function Prototypes
+//
+
+/**
+ Construct the system base memory range through GCD service.
+
+ @param[in] Private Point to generic memory test driver's private data.
+
+ @retval EFI_SUCCESS Successful construct the base memory range through GCD service.
+ @retval EFI_OUT_OF_RESOURCE Could not allocate needed resource from base memory.
+ @retval Others Failed to construct base memory range through GCD service.
+
+**/
+EFI_STATUS
+ConstructBaseMemoryRange (
+ IN GENERIC_MEMORY_TEST_PRIVATE *Private
+ );
+
+/**
+ Construct the system non-tested memory range through GCD service.
+
+ @param[in] Private Point to generic memory test driver's private data.
+
+ @retval EFI_SUCCESS Successful construct the non-tested memory range through GCD service.
+ @retval EFI_OUT_OF_RESOURCE Could not allocate needed resource from base memory.
+ @retval Others Failed to construct non-tested memory range through GCD service.
+
+**/
+EFI_STATUS
+ConstructNonTestedMemoryRange (
+ IN GENERIC_MEMORY_TEST_PRIVATE *Private
+ );
+
+/**
+ Perform the address line walking ones test.
+
+ @param[in] Private Point to generic memory test driver's private data.
+
+ @retval EFI_SUCCESS Successful finished walking ones test.
+ @retval EFI_OUT_OF_RESOURCE Could not get resource in base memory.
+ @retval EFI_ACCESS_DENIED Code may can not run here because if walking one test
+ failed, system may be already halt.
+
+**/
+EFI_STATUS
+PerformAddressDataLineTest (
+ IN GENERIC_MEMORY_TEST_PRIVATE *Private
+ );
+
+/**
+ Destroy the link list base on the correspond link list type.
+
+ @param[in] Private Point to generic memory test driver's private data.
+
+**/
+VOID
+DestroyLinkList (
+ IN GENERIC_MEMORY_TEST_PRIVATE *Private
+ );
+
+/**
+ Add the extened memory to whole system memory map.
+
+ @param[in] Private Point to generic memory test driver's private data.
+
+ @retval EFI_SUCCESS Successful add all the extended memory to system memory map.
+ @retval Others Failed to add the tested extended memory.
+
+**/
+EFI_STATUS
+UpdateMemoryMap (
+ IN GENERIC_MEMORY_TEST_PRIVATE *Private
+ );
+
+/**
+ Write the memory test pattern into a range of physical memory.
+
+ @param[in] Private Point to generic memory test driver's private data.
+ @param[in] Start The memory range's start address.
+ @param[in] Size The memory range's size.
+
+ @retval EFI_SUCCESS Successful write the test pattern into the non-tested memory.
+ @retval Others The test pattern may not really write into the physical memory.
+
+**/
+EFI_STATUS
+WriteMemory (
+ IN GENERIC_MEMORY_TEST_PRIVATE *Private,
+ IN EFI_PHYSICAL_ADDRESS Start,
+ IN UINT64 Size
+ );
+
+/**
+ Verify the range of physical memory which covered by memory test pattern.
+
+ This function will also do not return any informatin just cause system reset,
+ because the handle error encount fatal error and disable the bad DIMMs.
+
+ @param[in] Private Point to generic memory test driver's private data.
+ @param[in] Start The memory range's start address.
+ @param[in] Size The memory range's size.
+
+ @retval EFI_SUCCESS Successful verify the range of memory, no errors' location found.
+ @retval Others The range of memory have errors contained.
+
+**/
+EFI_STATUS
+VerifyMemory (
+ IN GENERIC_MEMORY_TEST_PRIVATE *Private,
+ IN EFI_PHYSICAL_ADDRESS Start,
+ IN UINT64 Size
+ );
+
+/**
+ Test a range of the memory directly .
+
+ @param[in] Private Point to generic memory test driver's private data.
+ @param[in] StartAddress Starting address of the memory range to be tested.
+ @param[in] Length Length in bytes of the memory range to be tested.
+ @param[in] Capabilities The bit mask of attributes that the memory range supports.
+
+ @retval EFI_SUCCESS Successful test the range of memory.
+ @retval Others Failed to test the range of memory.
+
+**/
+EFI_STATUS
+DirectRangeTest (
+ IN GENERIC_MEMORY_TEST_PRIVATE *Private,
+ IN EFI_PHYSICAL_ADDRESS StartAddress,
+ IN UINT64 Length,
+ IN UINT64 Capabilities
+ );
+
+/**
+ Initialize the generic memory test.
+
+ @param[in] This The protocol instance pointer.
+ @param[in] Level The coverage level of the memory test.
+ @param[out] RequireSoftECCInit Indicate if the memory need software ECC init.
+
+ @retval EFI_SUCCESS The generic memory test is initialized correctly.
+ @retval EFI_NO_MEDIA The system had no memory to be tested.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeMemoryTest (
+ IN EFI_GENERIC_MEMORY_TEST_PROTOCOL *This,
+ IN EXTENDMEM_COVERAGE_LEVEL Level,
+ OUT BOOLEAN *RequireSoftECCInit
+ );
+
+/**
+ Perform the memory test.
+
+ @param[in] This The protocol instance pointer.
+ @param[out] TestedMemorySize Return the tested extended memory size.
+ @param[out] TotalMemorySize Return the whole system physical memory size.
+ The total memory size does not include memory in a slot with a disabled DIMM.
+ @param[out] ErrorOut TRUE if the memory error occured.
+ @param[in] IfTestAbort Indicates that the user pressed "ESC" to skip the memory test.
+
+ @retval EFI_SUCCESS One block of memory passed the test.
+ @retval EFI_NOT_FOUND All memory blocks have already been tested.
+ @retval EFI_DEVICE_ERROR Memory device error occured, and no agent can handle it.
+
+**/
+EFI_STATUS
+EFIAPI
+GenPerformMemoryTest (
+ IN EFI_GENERIC_MEMORY_TEST_PROTOCOL *This,
+ OUT UINT64 *TestedMemorySize,
+ OUT UINT64 *TotalMemorySize,
+ OUT BOOLEAN *ErrorOut,
+ IN BOOLEAN TestAbort
+ );
+
+/**
+ Finish the memory test.
+
+ @param[in] This The protocol instance pointer.
+
+ @retval EFI_SUCCESS Success. All resources used in the memory test are freed.
+
+**/
+EFI_STATUS
+EFIAPI
+GenMemoryTestFinished (
+ IN EFI_GENERIC_MEMORY_TEST_PROTOCOL *This
+ );
+
+/**
+ Provides the capability to test the compatible range used by some special drivers.
+
+ @param[in] This The protocol instance pointer.
+ @param[in] StartAddress The start address of the compatible memory range that
+ must be below 16M.
+ @param[in] Length The compatible memory range's length.
+
+ @retval EFI_SUCCESS The compatible memory range pass the memory test.
+ @retval EFI_INVALID_PARAMETER The compatible memory range are not below Low 16M.
+
+**/
+EFI_STATUS
+EFIAPI
+GenCompatibleRangeTest (
+ IN EFI_GENERIC_MEMORY_TEST_PROTOCOL *This,
+ IN EFI_PHYSICAL_ADDRESS StartAddress,
+ IN UINT64 Length
+ );
+
+#endif