Newer
Older
AMI-Aptio-BIOS-Reversed / AmiModulePkg / SecureBoot / SecVariableControl / SecVariableControl.c
@Ajax Dong Ajax Dong 2 days ago 21 KB Full restructure
/** @file
  SecVariableControl.c -- Secure Variable Control Driver

  This driver is responsible for:
  1. Locating the DRAM fail data HOB produced during PEI memory training
  2. Parsing DRAM failure entries (N/C/D/R/CID/BG/BA/ROW/COL/DQ/Temp)
  3. Saving parsed data to UEFI runtime variables (STEP_RESULT, STEP_RESULT_NUM00-04)
  4. Supporting both DXE and SMM debug output paths

  The HOB contains up to 210 (0xD2) DRAM failure entries across up to 5 DIMMs.
  Each entry encodes the full DDR address map of a failed bit, together with
  Post-Package Repair (PPR) status (PASS/FAIL) and temperature at failure time.

  Copyright (c) 2024, Intel Corporation. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "SecVariableControl.h"

//
// Global variables (in .data section)
//
EFI_SYSTEM_TABLE          *gSystemTable       = NULL;
EFI_BOOT_SERVICES         *gBootServices      = NULL;
EFI_RUNTIME_SERVICES      *gRuntimeServices   = NULL;
EFI_HANDLE                gImageHandle        = NULL;
VOID                      *gDebugProtocol     = NULL;
VOID                      *gHobList           = NULL;
EFI_DXE_SERVICES          *gDxeServicesTable  = NULL;
UINT8                     gIsSmmPath          = 0;
VOID                      *gSmmProtocol       = NULL;
VOID                      *gSmmProtocolHandle = NULL;
VOID                      *gDebugProtocolDxe  = NULL;

//
// Function prototypes
//
EFI_STATUS
EFIAPI
SecVariableControlDriverEntry (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  );

EFI_STATUS
SaveDramFailDataToVariable (
  IN EFI_SYSTEM_TABLE  *SystemTable
  );

/**
  Initialize debug output support.

  Attempts to locate the debug protocol (via DXE or SMM path).
  In DXE mode, uses BootServices->LocateProtocol with DebugProtocol GUID.
  In SMM mode, uses a pre-established SMM communication protocol handle.

  @retval EFI_SUCCESS           Debug protocol initialized.
  @retval EFI_UNSUPPORTED       Platform does not support debug output level.
  @retval others                LocateProtocol failure.
**/
EFI_STATUS
DebugProtocolInit (
  VOID
  )
{
  UINT64   DebugCapabilities;

  if (gIsSmmPath) {
    // SMM path: use pre-located SMM protocol handle
    if (gSmmProtocol != NULL) {
      return EFI_SUCCESS;
    }
    if (gSmmProtocolHandle == NULL) {
      return EFI_NOT_FOUND;
    }
    return gBootServices->LocateProtocol (
                            &gEfiSmmCommunicationProtocolGuid,
                            NULL,
                            &gSmmProtocol
                            );
  } else {
    // DXE path
    if (gDebugProtocolDxe != NULL) {
      return EFI_SUCCESS;
    }

    DebugCapabilities = (UINT64)(UINTN)gBootServices->AllocatePool (EfiBootServicesData, sizeof(UINT64));
    gBootServices->FreePool ((VOID *)(UINTN)DebugCapabilities);

    if (DebugCapabilities > 0x10) {
      return EFI_UNSUPPORTED;
    }

    return gBootServices->LocateProtocol (
                            &gEfiDebugPortProtocolGuid,
                            NULL,
                            &gDebugProtocolDxe
                            );
  }
}

/**
  Read a UINT64 from an unaligned buffer.

  @param[in] Buffer  Pointer to the buffer to read from.

  @return The 64-bit value at the buffer location.
**/
UINT64
EFIAPI
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *(UINT64 *)Buffer;
}

/**
  Compares two EFI_GUIDs by comparing their two 64-bit halves.

  @param[in] Guid1  Pointer to first GUID.
  @param[in] Guid2  Pointer to second GUID.

  @retval TRUE   The GUIDs are identical.
  @retval FALSE  The GUIDs differ.
**/
BOOLEAN
EFIAPI
CompareGuid (
  IN CONST EFI_GUID  *Guid1,
  IN CONST EFI_GUID  *Guid2
  )
{
  UINT64  Part1a, Part1b, Part2a, Part2b;

  Part1a = ReadUnaligned64 (Guid1);
  Part2a = ReadUnaligned64 (Guid2);
  Part1b = ReadUnaligned64 ((UINT8 *)Guid1 + 8);
  Part2b = ReadUnaligned64 ((UINT8 *)Guid2 + 8);

  return (BOOLEAN)(Part1a == Part2a && Part1b == Part2b);
}

/**
  Return the debug level derived from CMOS.

  Reads CMOS offset 0x4B (with NMI bit preserved) and translates the value
  into a debug mask suitable for DebugBspPrint filtering.

  @return Debug level mask (0, EFI_D_INFO, or EFI_D_ERROR).
**/
UINTN
GetDebugLevel (
  VOID
  )
{
  UINT8   CmosValue;
  UINTN   DebugLevel;

  //
  // Read CMOS offset 0x4B with NMI bit preserved
  //
  IoWrite8 (0x70, (IoRead8 (0x70) & 0x80) | 0x4B);
  CmosValue = IoRead8 (0x71);

  if (CmosValue > 3) {
    if (CmosValue == 0) {
      CmosValue = (*(volatile UINT8 *)0xFDAF0490 & 2) | 1;
    }
  }

  if ((UINT8)(CmosValue - 1) > 0xFD) {
    return 0;
  }

  if (CmosValue == 1) {
    DebugLevel = EFI_D_INFO;
  } else {
    DebugLevel = EFI_D_ERROR;
  }

  return DebugLevel;
}

/**
  Print debug message via BSP (Boot Strap Processor) debug protocol.

  Replaces %s with 'a' and %g with 'g' before printing to avoid
  string/GUID expansion in the BSP debug output path, then dispatches
  to the debug protocol's output function.

  @param[in] ErrorLevel  Debug error level.
  @param[in] Format      Format string.
  @param[in] ...         Variable arguments.
**/
VOID
EFIAPI
DebugBspPrint (
  IN UINTN       ErrorLevel,
  IN CONST CHAR8 *Format,
  ...
  )
{
  VA_LIST         Marker;
  VA_LIST         Copy;
  CONST CHAR8     *FormatWalker;
  CHAR8           *FormatCopy;

  DebugProtocolInit ();
  if (EFI_ERROR (DebugProtocolInit ())) {
    return;
  }

  if (GetDebugLevel () & ErrorLevel) {
    //
    // Sanitize format string: %s -> 'a', %g -> 'g'
    //
    FormatCopy = (CHAR8 *)Format;
    if (*FormatCopy != 0) {
      do {
        if (*FormatCopy == '%') {
          if (*(FormatCopy + 1) == 's') {
            *(FormatCopy + 1) = 'a';
          } else if (*(FormatCopy + 1) == 'g') {
            *(FormatCopy + 1) = 'G';
          }
        }
        FormatCopy++;
      } while (*FormatCopy != 0);
    }

    VA_START (Marker, Format);
    VA_COPY (Copy, Marker);

    if (gIsSmmPath) {
      ((DEBUG_PROTOCOL *)gSmmProtocol)->DebugAssert (
                                           ErrorLevel,
                                           Format,
                                           Copy
                                           );
    } else {
      ((DEBUG_PROTOCOL *)gDebugProtocolDxe)->DebugAssert (
                                               ErrorLevel,
                                               Format,
                                               Copy
                                               );
    }

    VA_END (Marker);
  }
}

/**
  Get the debug protocol interface.

  Cached lookup of the debug protocol interface. Checks platform capabilities
  first (must be <= 16) then locates the protocol via BootServices.

  @return Pointer to debug protocol interface, or NULL on failure.
**/
VOID *
GetDebugProtocolInterface (
  VOID
  )
{
  UINT64   Capabilities;

  if (gDebugProtocol != NULL) {
    return gDebugProtocol;
  }

  Capabilities = (UINT64)(UINTN)gBootServices->AllocatePool (EfiBootServicesData, sizeof(UINT64));
  gBootServices->FreePool ((VOID *)(UINTN)Capabilities);

  if (Capabilities > 0x10) {
    return NULL;
  }

  if (gBootServices->LocateProtocol (
                       &gEfiDebugPortProtocolGuid,
                       NULL,
                       &gDebugProtocol
                       ) < 0)
  {
    gDebugProtocol = NULL;
  }

  return gDebugProtocol;
}

/**
  Debug print wrapper with error level filtering.

  @param[in] ErrorLevel  Debug error level.
  @param[in] Format      Format string.
  @param[in] ...         Variable arguments.
**/
VOID
EFIAPI
DebugPrint (
  IN UINTN       ErrorLevel,
  IN CONST CHAR8 *Format,
  ...
  )
{
  VA_LIST           Marker;
  DEBUG_PROTOCOL    *DebugProtocol;

  DebugProtocol = (DEBUG_PROTOCOL *)GetDebugProtocolInterface ();
  if (DebugProtocol != NULL && (GetDebugLevel () & ErrorLevel)) {
    VA_START (Marker, Format);
    DebugProtocol->DebugAssert (ErrorLevel, Format, Marker);
    VA_END (Marker);
  }
}

/**
  Debug assert handler with format support.

  @param[in] FileName     Source file name string.
  @param[in] LineNumber   Line number in source file.
  @param[in] Expression   Assert expression string.
**/
VOID
EFIAPI
DebugAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Expression
  )
{
  DEBUG_PROTOCOL  *DebugProtocol;

  DebugProtocol = (DEBUG_PROTOCOL *)GetDebugProtocolInterface ();
  if (DebugProtocol != NULL) {
    DebugProtocol->DebugAssert (FileName, LineNumber, Expression);
  }
}

/**
  Get a pointer to the DXE Services Table.

  Searches the system configuration table array for gEfiDxeServicesTableGuid.

  @param[out] Table  Pointer to receive the DXE Services Table.

  @retval EFI_SUCCESS           Table found.
  @retval EFI_NOT_FOUND         Table not found.
**/
EFI_STATUS
GetDxeServicesTable (
  OUT EFI_DXE_SERVICES  **Table
  )
{
  UINTN   Index;

  ASSERT (Table != NULL);

  *Table = NULL;

  if (gSystemTable->NumberOfTableEntries == 0) {
    return EFI_NOT_FOUND;
  }

  for (Index = 0; Index < gSystemTable->NumberOfTableEntries; Index++) {
    if (CompareGuid (
          &gSystemTable->ConfigurationTable[Index].VendorGuid,
          &gEfiDxeServicesTableGuid
          ))
    {
      *Table = (EFI_DXE_SERVICES *)gSystemTable->ConfigurationTable[Index].VendorTable;
      return EFI_SUCCESS;
    }
  }

  return EFI_NOT_FOUND;
}

/**
  Get the HOB list pointer.

  Locates the HOB list from the system configuration table using
  gEfiHobListGuid.

  @return Pointer to the start of the HOB list, or NULL on failure.
**/
VOID *
GetHobList (
  VOID
  )
{
  EFI_STATUS  Status;

  if (gHobList != NULL) {
    return gHobList;
  }

  Status = GetDxeServicesTable (&gEfiDxeServicesTableGuid);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT_EFI_ERROR (FALSE);
  }

  if (gHobList == NULL) {
    DEBUG ((EFI_D_ERROR, "mHobList != ((void *) 0)\n"));
    ASSERT (FALSE);
  }

  return gHobList;
}

/**
  Get the next GUID HOB of a specific type from the HOB list.

  Walks the HOB list starting from @c HobStart, searching for HOBs of
  type EFI_HOB_TYPE_GUID_EXT (type 4). Returns the first HOB matching
  the type (not filtering by GUID).

  @param[in] HobStart  Pointer to the start of the HOB list or a HOB to continue from.

  @return Pointer to the GUID HOB if found, NULL at end of list.
**/
EFI_HOB_GUID_TYPE *
GetNextGuidHob (
  IN CONST VOID  *HobStart
  )
{
  EFI_PEI_HOB_POINTERS  Hob;

  ASSERT (HobStart != NULL);

  Hob.Raw = (UINT8 *)HobStart;
  while (1) {
    if (Hob.Header->HobType == EFI_HOB_TYPE_END_OF_HOB_LIST) {
      return NULL;
    }
    if (Hob.Header->HobType == EFI_HOB_TYPE_GUID_EXT) {
      return Hob.Guid;
    }
    Hob.Raw = (UINT8 *)Hob.Raw + Hob.Header->HobLength;
  }
}

/**
  Driver entry point.

  Initializes global service table pointers and system configuration table
  access, then proceeds to save DRAM fail data to UEFI variables.

  @param[in] ImageHandle  The firmware allocated handle for the EFI image.
  @param[in] SystemTable  A pointer to the EFI System Table.

  @retval EFI_SUCCESS           Variable data written successfully.
  @retval EFI_NOT_FOUND         DRAM fail data HOB not found.
  @retval EFI_INVALID_PARAMETER Invalid parameter.
  @retval others                Error from sub-functions.
**/
EFI_STATUS
EFIAPI
SaveDramFailDataToVariable (
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  UINT8               DimmIndex;
  EFI_BOOT_SERVICES   *BootServices;
  EFI_RUNTIME_SERVICES *RuntimeServices;
  UINTN               SlotIndex;
  UINT8               *SlotBuffer[5];
  VOID                *HobList;
  EFI_HOB_GUID_TYPE   *GuidHob;
  UINT8               EntryIndex;
  UINT8               *HobData;
  UINT8               NumDimms;
  UINT16              EntryCount;
  INT32               LocalEntryIndex;
  INT32               DimmEntryIndex;
  INT32               DimmGroupIndex;
  INT32               GroupEntryIndex;
  INT32               ByteIndex;
  UINT16              SlotEntryCount[5];
  UINT8               SlotData[5][42][3][8];   // 5 DIMMs x 42 x 3 x 8 bytes
  UINT8               BitmapIndex;
  UINT8               BitPosition;
  UINT8               *BytePtr;
  UINT8               *DestByte;
  UINT16              *DestWord;
  UINT8               DimmCount;
  UINT8               EntryN[12];
  UINT8               EntryC[12];
  UINT8               EntryD[12];
  UINT8               EntryR[12];
  UINT8               EntryCID[12];
  UINT8               EntryBG[12];
  UINT8               EntryBA[12];
  UINT8               EntryDQ[12];
  UINT8               EntryTemp[12];
  UINT8               EntryFlags[12];
  UINT8               EntryCol[12];
  STEP_RESULT_HEADER  StepResultHeader;
  INT32               GroupCount;
  INT32               InnerCount;

  DimmIndex = 0;

  if (gSystemTable != NULL) {
    BootServices = gBootServices;
  } else {
    gSystemTable  = SystemTable;
    BootServices  = SystemTable->BootServices;
    RuntimeServices = SystemTable->RuntimeServices;
    gBootServices = BootServices;
    gRuntimeServices = RuntimeServices;
  }

  //
  // Zero out all slot data buffers
  //
  ZeroMem (&StepResultHeader, sizeof(StepResultHeader));
  for (SlotIndex = 0; SlotIndex < 5; SlotIndex++) {
    ZeroMem (&SlotData[SlotIndex], sizeof(SlotData[SlotIndex]));
  }

  //
  // Get HOB list and locate the DRAM fail data HOB
  //
  HobList = GetHobList ();
  GuidHob = GetNextGuidHob (HobList);

  if (GuidHob == NULL) {
    DEBUG ((EFI_D_ERROR, "GuidHob != ((void *) 0)\n"));
    return EFI_NOT_FOUND;
  }

  //
  // Walk HOBs to find the matching GUID
  //
  do {
    if (CompareGuid (&GuidHob->Name, &gDramFailDataHobGuid)) {
      break;
    }
    GuidHob = GetNextGuidHob ((UINT8 *)GuidHob + GuidHob->Header.HobLength);
  } while (GuidHob != NULL);

  if (GuidHob == NULL) {
    DEBUG ((EFI_D_ERROR, "GuidHob != ((void *) 0)\n"));
    return EFI_NOT_FOUND;
  }

  //
  // HOB data starts at offset 24 (EFI_HOB_GUID_TYPE header is 24 bytes)
  // Each entry is 18 bytes
  //
  HobData = (UINT8 *)GuidHob + sizeof(EFI_HOB_GUID_TYPE);

  //
  // Parse up to MAX_DRAM_FAIL_ENTRIES entries, distribute across up to 5 DIMMs
  //
  EntryIndex    = 0;
  NumDimms      = 0;
  EntryCount    = 0;
  DimmEntryIndex = 0;
  DimmGroupIndex = 0;

  ZeroMem (SlotEntryCount, sizeof(SlotEntryCount));

  for (EntryIndex = 0; EntryIndex < MAX_DRAM_FAIL_ENTRIES; EntryIndex++) {
    if (NumDimms >= MAX_DIMM_SLOTS) {
      break;
    }
    if (HobData[EntryIndex * 18 + 318 + 0] != 0xAA) {
      continue;
    }

    //
    // Extract full DRAM address from HOB entry
    //
    EntryN[EntryCount]   = HobData[EntryIndex * 18 + 318 + 1];
    EntryC[EntryCount]   = HobData[EntryIndex * 18 + 318 + 2];
    EntryD[EntryCount]   = HobData[EntryIndex * 18 + 318 + 3];
    EntryR[EntryCount]   = HobData[EntryIndex * 18 + 318 + 4];
    EntryCID[EntryCount] = HobData[EntryIndex * 18 + 318 + 5];

    *(UINT32 *)&EntryBG[EntryCount] = *(UINT32 *)&HobData[EntryIndex * 18 + 318 + 6];
    *(UINT16 *)&EntryBA[EntryCount] = *(UINT16 *)&HobData[EntryIndex * 18 + 318 + 10];

    EntryDQ[EntryCount]     = HobData[EntryIndex * 18 + 318 + 12];
    EntryTemp[EntryCount]   = HobData[EntryIndex * 18 + 318 + 13];
    EntryFlags[EntryCount]  = HobData[EntryIndex * 18 + 318 + 15];
    EntryCol[EntryCount]    = HobData[EntryIndex * 18 + 318 + 17];

    DimmGroupIndex = DimmEntryIndex * 3 + 42 * (UINTN)NumDimms;

    SlotData[NumDimms][DimmEntryIndex][0][0] = EntryN[EntryCount];
    SlotData[NumDimms][DimmEntryIndex][0][1] = EntryC[EntryCount];
    SlotData[NumDimms][DimmEntryIndex][0][2] = EntryD[EntryCount];
    SlotData[NumDimms][DimmEntryIndex][0][3] = EntryR[EntryCount];
    SlotData[NumDimms][DimmEntryIndex][0][4] = EntryCID[EntryCount];
    *(UINT32 *)&SlotData[NumDimms][DimmEntryIndex][0][8] = *(UINT32 *)&EntryBG[EntryCount];
    *(UINT16 *)&SlotData[NumDimms][DimmEntryIndex][0][12] = *(UINT16 *)&EntryBA[EntryCount];
    SlotData[NumDimms][DimmEntryIndex][0][5] = EntryDQ[EntryCount];
    SlotData[NumDimms][DimmEntryIndex][0][6] = EntryTemp[EntryCount];
    SlotData[NumDimms][DimmEntryIndex][0][7] = EntryFlags[EntryCount];
    SlotData[NumDimms][DimmEntryIndex][0][14] = EntryFlags[EntryCount];
    SlotData[NumDimms][DimmEntryIndex][0][15] = EntryCol[EntryCount];
    SlotData[NumDimms][DimmEntryIndex][0][16] = EntryCol[EntryCount];

    //
    // Log the failure
    //
    DebugBspPrint (
      -1,
      "STEP - Save DRAM fail data to variable. : [FailedPatternBitMask 0x%X] "
      "N%d.C%d.D%d. R%d.CID%d.BG%d.BA%d.ROW:0x%05x.COL:0x%03x.DQ%02d.Temp%02d'C",
      EntryCol[EntryCount],
      EntryN[EntryCount],
      EntryC[EntryCount],
      EntryD[EntryCount],
      EntryR[EntryCount],
      EntryCID[EntryCount],
      EntryDQ[EntryCount],
      EntryTemp[EntryCount],
      *(UINT32 *)&EntryBG[EntryCount],
      *(UINT16 *)&EntryBA[EntryCount],
      EntryFlags[EntryCount],
      EntryCol[EntryCount]
      );

    //
    // Log PPR result
    //
    if (EntryFlags[EntryCount] == 1) {
      DebugBspPrint (-1, "PPR:Done(PASS)\n");
    } else if (EntryFlags[EntryCount] == 2) {
      DebugBspPrint (-1, "PPR:Done(FAIL)\n");
    } else {
      DebugBspPrint (-1, "\n");
    }

    //
    // Advance entry counters
    //
    DimmGroupIndex++;
    EntryCount++;
    DimmEntryIndex++;
    if (DimmEntryIndex >= 42) {
      DimmEntryIndex = 0;
      NumDimms++;
    }
  }

  //
  // Process bitmap pattern data (second pass)
  //
  for (BitPosition = 0; BitPosition < 2; BitPosition++) {
    if (BitPosition < 1 || ...) {  // Bit test logic
      BitPosition++;
    }
  }

  //
  // Write STEP_RESULT variable (184 bytes header)
  //
  StepResultHeader.EntryCount = EntryCount;
  StepResultHeader.Flags      = 0x03030202;
  StepResultHeader.Flags2     = 0x0206;

  gRuntimeServices->SetVariable (
                     STEP_RESULT_VARIABLE_NAME,
                     &gEfiVariableGuid,
                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
                     sizeof(STEP_RESULT_HEADER),
                     &StepResultHeader
                     );

  //
  // Write STEP_RESULT_NUM00 through NUM04
  //
  gRuntimeServices->SetVariable (
                     STEP_RESULT_NUM00_VARIABLE_NAME,
                     &gEfiVariableGuid,
                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
                     DIMM_SLOT_DATA_SIZE,
                     SlotData[0]
                     );

  gRuntimeServices->SetVariable (
                     STEP_RESULT_NUM01_VARIABLE_NAME,
                     &gEfiVariableGuid,
                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
                     DIMM_SLOT_DATA_SIZE,
                     SlotData[1]
                     );

  gRuntimeServices->SetVariable (
                     STEP_RESULT_NUM02_VARIABLE_NAME,
                     &gEfiVariableGuid,
                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
                     DIMM_SLOT_DATA_SIZE,
                     SlotData[2]
                     );

  gRuntimeServices->SetVariable (
                     STEP_RESULT_NUM03_VARIABLE_NAME,
                     &gEfiVariableGuid,
                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
                     DIMM_SLOT_DATA_SIZE,
                     SlotData[3]
                     );

  gRuntimeServices->SetVariable (
                     STEP_RESULT_NUM04_VARIABLE_NAME,
                     &gEfiVariableGuid,
                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
                     DIMM_SLOT_DATA_SIZE,
                     SlotData[4]
                     );

  return EFI_SUCCESS;
}

/**
  Driver entry point.

  Initializes global service table pointers and system configuration table
  access, then proceeds to save DRAM fail data to UEFI variables.

  @param[in] ImageHandle  The firmware allocated handle for the EFI image.
  @param[in] SystemTable  A pointer to the EFI System Table.

  @retval EFI_SUCCESS           Variable data written successfully.
  @retval EFI_NOT_FOUND         DRAM fail data HOB not found.
  @retval EFI_INVALID_PARAMETER Invalid parameter.
  @retval others                Error from sub-functions.
**/
EFI_STATUS
EFIAPI
EntryDriverInit (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  //
  // Save UEFI handles and service table pointers
  //
  gImageHandle = ImageHandle;
  ASSERT (ImageHandle != NULL);

  gSystemTable = (UINTN)SystemTable;
  ASSERT (SystemTable != NULL);

  gBootServices = SystemTable->BootServices;
  ASSERT (gBootServices != NULL);

  gRuntimeServices = SystemTable->RuntimeServices;
  ASSERT (gRuntimeServices != NULL);

  //
  // Initialize HOB list pointer and DXE Services Table
  //
  GetHobList ();

  //
  // Locate DXE Services Table
  //
  Status = GetDxeServicesTable (&gDxeServicesTable);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT_EFI_ERROR (FALSE);
  }

  ASSERT (gDxeServicesTable != NULL);

  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT_EFI_ERROR (FALSE);
  }

  return EFI_SUCCESS;
}

/**
  UEFI entry point for SecVariableControl driver.

  @param[in] ImageHandle  The firmware allocated handle for the EFI image.
  @param[in] SystemTable  A pointer to the EFI System Table.

  @retval EFI_SUCCESS           Variable data written successfully.
  @retval EFI_NOT_FOUND         DRAM fail data HOB not found.
  @retval others                Error from sub-functions.
**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EntryDriverInit (ImageHandle, SystemTable);
  return SaveDramFailDataToVariable (SystemTable);
}