Newer
Older
AMI-Aptio-BIOS-Reversed / MdeModulePkg / Universal / CapsuleRuntimeDxe / CapsuleRuntimeDxe.c
@Ajax Dong Ajax Dong 2 days ago 24 KB Full restructure
/** @file
  CapsuleRuntimeDxe - UEFI Capsule Runtime Driver

  This DXE driver produces the UEFI Runtime Capsule services:
  - UpdateCapsule
  - QueryCapsuleCapabilities

  It also registers VirtualAddressChange and ExitBootServices
  notification events to transition runtime services properly.

  Source: HR650X BIOS, CapsuleRuntimeDxe.efi (Index 0104)
  Module GUID: (from AutoGen.c)
  Build: DEBUG_VS2015 X64
  File: CapsuleRuntimeDxe.efi.i64
  MD5: 24e107efaaec61d3574c53c6a4e84b56
  SHA256: 8a14945ce1115188ee9c525ecd703c467bae7d6a5a96788cf619959c3ae43b85
**/

#include "CapsuleRuntimeDxe.h"

//
// Global data - boot-time only references
//
EFI_HANDLE         gImageHandle = NULL;
EFI_SYSTEM_TABLE  *gST          = NULL;
EFI_BOOT_SERVICES *gBS          = NULL;
EFI_RUNTIME_SERVICES *gRT       = NULL;

//
// Saved copies for runtime transition
// (BootServices_0 used by VirtualAddressChange to NULL out gBS)
//
EFI_BOOT_SERVICES    *gBS_Runtime       = NULL;
EFI_RUNTIME_SERVICES *gRT_Runtime       = NULL;

//
// Capsule configuration globals
//
UINT64               gCapsuleMaxSize      = 104857600;  // 100 MB
UINT64               gCapsuleMaxSizeNonCapsule = 34603008;  // 33 MB
UINT8                gCapsuleInRuntime    = 0;

//
// Runtime services function table (obtained via gBS->LocateProtocol)
// and the protocol GUID.
//
STATIC VOID         *mCapsuleRuntimeProtocol = NULL;
EFI_EVENT            mVirtualAddressChangeEvent = NULL;
STATIC VOID         *mHobList               = NULL;

// EFI_GUID {0x...} for the capsule runtime protocol (placeholder)
// unk_3020 and unk_3030 are GUID structures used for GUID comparison
// unk_3070 is a GUID table used by capsule type detection

//
// Function prototypes (forward declarations for local functions)
//
EFI_STATUS
EFIAPI
CapsuleRuntimeDxeDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  );

EFI_STATUS
EFIAPI
CapsuleUpdateCapsule (
  IN EFI_CAPSULE_HEADER **CapsuleHeaderArray,
  IN UINT64              CapsuleCount,
  IN EFI_PHYSICAL_ADDRESS ScatterGatherList OPTIONAL
  );

EFI_STATUS
EFIAPI
CapsuleQueryCapsuleCapabilities (
  IN  EFI_CAPSULE_HEADER **CapsuleHeaderArray,
  IN  UINT64              CapsuleCount,
  OUT UINT64             *MaxCapsuleSize,
  OUT EFI_STATUS         *ResetType
  );

BOOLEAN
EFIAPI
IsCapspaceGuidEqual (
  IN EFI_GUID *Guid1,
  IN EFI_GUID *Guid2
  );

UINT64
ReadUnaligned64 (
  IN UINT64 *Buffer
  );

EFI_STATUS
EFIAPI
CheckCapsuleType (
  IN EFI_CAPSULE_HEADER *CapsuleHeader
  );

STATIC
EFI_STATUS
GetCapsuleRuntimeProtocol (
  VOID
  );

//
// -- 16 functions total --
//

/**
  _ModuleEntryPoint - DXE driver entry point.

  Initializes the UEFI Boot Services Table Library, Runtime Services Table
  Library, and Runtime Library; installs the capsule runtime protocol and
  registers the CapsuleUpdateCapsule / QueryCapsuleCapabilities runtime
  services; registers VirtualAddressChange and ExitBootServices events.

  @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 other                 Some error occurs when executing this entry point.
**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  )
{
  EFI_STATUS  Status;

  //
  // Initialize UEFI Boot/Runtime Services Library state
  // and register the capsule runtime protocol.
  //
  CapsuleRuntimeDxeDriverEntryPoint (ImageHandle, SystemTable);

  //
  // Install the capsule runtime protocol.
  // NOTE: In the real source this is installed via UefiRuntimeProtocolLib
  // or gBS->InstallMultipleProtocolInterfaces. Here we approximate the
  // call that sets up gRT->UpdateCapsule / gRT->QueryCapsuleCapabilities.
  //
  // The decompiled code writes:
  //   RuntimeServices[112 / 8] = CapsuleUpdateCapsule;       // offset 14
  //   RuntimeServices[120 / 8] = CapsuleQueryCapsuleCapabilities; // offset 15
  //
  gRT->UpdateCapsule            = CapsuleUpdateCapsule;
  gRT->QueryCapsuleCapabilities  = CapsuleQueryCapsuleCapabilities;
  gCapsuleMaxSize                = 104857600;
  gCapsuleMaxSizeNonCapsule      = 34603008;

  //
  // Locate and install the capsule runtime protocol
  // (equivalent to InstallMultipleProtocolInterfaces)
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &gImageHandle,
                  &gEfiCapsuleRuntimeProtocolGuid,
                  NULL,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    //
    // Assert on failure (debug builds only)
    //
    DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT_EFI_ERROR (FALSE);
    CpuDeadLoop ();
  }

  return Status;
}

/**
  CapsuleRuntimeDxeDriverEntryPoint (sub_1194)

  Initializes global state: saves ImageHandle, SystemTable, BootServices,
  RuntimeServices; creates VirtualAddressChange and ExitBootServices events;
  initializes the HOB list pointer and registers runtime capsule events.

  This corresponds to the UefiBootServicesTableLib, UefiRuntimeServicesTableLib,
  and UefiRuntimeLib initialization sequence.

  @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 driver data was initialized successfully.
  @retval other        An error occurred during initialization.
**/
EFI_STATUS
EFIAPI
CapsuleRuntimeDxeDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  )
{
  EFI_STATUS  Status;

  //
  // UefiBootServicesTableLib constructor
  //
  gImageHandle = ImageHandle;
  ASSERT (gImageHandle != NULL);

  gST = SystemTable;
  ASSERT (gST != NULL);

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

  //
  // UefiRuntimeServicesTableLib constructor
  //
  gRT = SystemTable->RuntimeServices;
  ASSERT (gRT != NULL);

  //
  // Save copies for VirtualAddressChange event
  //
  gBS_Runtime = gBS;
  gRT_Runtime = gRT;

  //
  // Create VirtualAddressChange event (TPL_NOTIFY, EFI_EVENT_SIGNAL_VIRTUAL_ADDRESS_CHANGE)
  //
  Status = gBS->CreateEvent (
                  EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
                  TPL_NOTIFY,
                  VirtualAddressChangeEvent,
                  NULL,
                  &mVirtualAddressChangeEvent
                  );
  ASSERT_EFI_ERROR (Status);

  //
  // Create ExitBootServices event (TPL_CALLBACK, EFI_EVENT_SIGNAL_EXIT_BOOT_SERVICES)
  //
  Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_CALLBACK,
                  ExitBootServicesEvent,
                  NULL,
                  &gEfiEventExitBootServicesGuid,
                  &mVirtualAddressChangeEvent
                  );
  ASSERT_EFI_ERROR (Status);

  //
  // UefiRuntimeLib constructor: initialize HOB list and register runtime capsule event
  //
  if (gRT == NULL) {
    ASSERT (gRT != NULL);
  }
  if (gBS == NULL) {
    ASSERT (gBS != NULL);
  }

  //
  // Create event for runtime capsule update notification
  //
  Status = gBS->CreateEvent (
                  EVT_NOTIFY_WAIT,
                  TPL_CALLBACK,
                  RuntimeCapsuleEvent,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT_EFI_ERROR (FALSE);
  }

  //
  // Create runtime event for capsule variable (EfiRuntimeServicesData)
  //
  Status = gBS->CreateEvent (
                  EVT_NOTIFY_WAIT,
                  TPL_CALLBACK,
                  CapsuleRuntimeVariableEvent,
                  NULL,
                  NULL,
                  &mCapsuleRuntimeProtocol
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT_EFI_ERROR (FALSE);
    DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT_EFI_ERROR (FALSE);
  }

  return Status;
}

// ---------------------------------------------------------------------------
// CapsuleUpdateCapsule (sub_14FC)
// ---------------------------------------------------------------------------

/**
  The UpdateCapsule runtime service.

  @param[in]     CapsuleHeaderArray  Virtual pointer to an array of virtual
                                     pointers to the capsules being passed
                                     into update capsule.
  @param[in]     CapsuleCount        Number of pointers in the array.
  @param[in]     ScatterGatherList   Physical pointer to a set of
                                     EFI_CAPSULE_BLOCK_DESCRIPTOR that
                                     describes the physical location of a
                                     set of capsules.

  @retval EFI_SUCCESS                Valid capsule was passed.
  @retval EFI_INVALID_PARAMETER      CapsuleCount is 0.
  @retval EFI_UNSUPPORTED            Capsule type is not supported on
                                     this platform.
  @retval EFI_WRITE_PROTECTED        The capsule is for runtime update
                                     but the capsule runtime protocol is
                                     not available.
**/
EFI_STATUS
EFIAPI
CapsuleUpdateCapsule (
  IN EFI_CAPSULE_HEADER **CapsuleHeaderArray,
  IN UINT64              CapsuleCount,
  IN EFI_PHYSICAL_ADDRESS ScatterGatherList OPTIONAL
  )
{
  UINT64       Index;
  EFI_STATUS   Status;
  UINT64       CheckIndex;
  UINT64       ReturnValue;

  if (CapsuleCount == 0) {
    return EFI_INVALID_PARAMETER;
  }

  Index = 0;
  for (Index = 0; Index < CapsuleCount; Index++) {
    EFI_CAPSULE_HEADER *ThisCapsule = CapsuleHeaderArray[Index];

    //
    // Check capsule flags:
    //   CapsuleFlag bit 17 (0x20000) = POPULATE_SYSTEM_TABLE
    //   CapsuleFlag bit 18 (0x40000) = INITIATE_RESET
    //   Combined bit 17+18 (0x30000) mask
    //   Combined bit 18+19 (0x50000) mask
    //
    // If both bits 17 and 18 are clear (0x20000 check) or
    // bit 18 is set and bit 17 is clear (0x40000 check), this
    // capsule needs runtime protocol validation.
    //
    if (((ThisCapsule->Flags & 0x30000) == 0x20000) ||
        ((ThisCapsule->Flags & 0x50000) == 0x40000) ||
        (IsCapspaceGuidEqual (ThisCapsule->CapsuleGuid, &gEfiCapsuleGuid) &&
         (ThisCapsule->Flags & 0x20000) != 0)) {
      //
      // Populate system table capsule - break out and process below
      //
      break;
    }

    if ((ThisCapsule->Flags & 0x20000) == 0) {
      //
      // Not a populate-system-table capsule; capsule type check required
      //
      Status = CheckCapsuleType (ThisCapsule);
      if (EFI_ERROR (Status)) {
        return Status;
      }
    }
  }

  if (Index >= CapsuleCount) {
    //
    // All capsules processed, check if any need reset populate
    //
    if (CapsuleCount == 0) {
      return EFI_SUCCESS;
    }

    CheckIndex = 0;
    while ((CapsuleHeaderArray[CheckIndex]->Flags & 0x10000) != 0) {
      CheckIndex++;
      if (CheckIndex >= CapsuleCount) {
        //
        // All capsules have bit 16 set - need a reset capsule
        //
        ReturnValue = (UINT64)EFI_UNSUPPORTED;
        if (ScatterGatherList != NULL) {
          ReturnValue |= 1;
        }
        return ReturnValue;
      }
    }

    //
    // Found a capsule without bit 16 set
    //
    ReturnValue = (UINT64)EFI_INVALID_PARAMETER;
    if (gCapsuleInRuntime) {
      ReturnValue |= 6;
    }
    return ReturnValue;
  }

  //
  // If we broke out above (populate system table capsule found),
  // fall through to success path
  //
  return EFI_SUCCESS;
}

// ---------------------------------------------------------------------------
// CapsuleQueryCapsuleCapabilities (sub_1608)
// ---------------------------------------------------------------------------

/**
  The QueryCapsuleCapabilities runtime service.

  @param[in]  CapsuleHeaderArray  Virtual pointer to an array of virtual
                                  pointers to the capsules being passed
                                  into update capsule.
  @param[in]  CapsuleCount        Number of pointers in the array.
  @param[out] MaxCapsuleSize      Returns the maximum size of the capsule
                                  the platform supports.
  @param[out] ResetType           Returns the type of reset needed for the
                                  capsule update. EFI_STATUS return of 0
                                  means no reset needed. Non-zero means
                                  reset required of the given type.

  @retval EFI_SUCCESS           Valid capsule was passed.
  @retval EFI_INVALID_PARAMETER MaxCapsuleSize is NULL, or ResetType is NULL,
                                or CapsuleCount is 0.
  @retval EFI_UNSUPPORTED       Capsule type is not supported on this platform.
**/
EFI_STATUS
EFIAPI
CapsuleQueryCapsuleCapabilities (
  IN  EFI_CAPSULE_HEADER **CapsuleHeaderArray,
  IN  UINT64              CapsuleCount,
  OUT UINT64             *MaxCapsuleSize,
  OUT EFI_STATUS         *ResetType
  )
{
  UINT64       Index;
  EFI_STATUS   Status;
  UINT64       CheckIndex;

  if (!CapsuleCount || !MaxCapsuleSize || !ResetType) {
    return EFI_INVALID_PARAMETER;
  }

  for (Index = 0; Index < CapsuleCount; Index++) {
    EFI_CAPSULE_HEADER *ThisCapsule = CapsuleHeaderArray[Index];

    //
    // Same capsule flag checks as UpdateCapsule
    //
    if (((ThisCapsule->Flags & 0x30000) == 0x20000) ||
        ((ThisCapsule->Flags & 0x50000) == 0x40000) ||
        (IsCapspaceGuidEqual (ThisCapsule->CapsuleGuid, &gEfiCapsuleGuid) &&
         (ThisCapsule->Flags & 0x20000) != 0)) {
      break;
    }

    if ((ThisCapsule->Flags & 0x20000) == 0) {
      Status = CheckCapsuleType (ThisCapsule);
      if (EFI_ERROR (Status)) {
        return Status;
      }
    }
  }

  if (Index >= CapsuleCount) {
    CheckIndex = 0;
    if (CapsuleCount > 0) {
      while ((CapsuleHeaderArray[CheckIndex]->Flags & 0x10000) == 0) {
        CheckIndex++;
        if (CheckIndex >= CapsuleCount) {
          goto SetOutput;
        }
      }
      return EFI_UNSUPPORTED;
    }
  }

SetOutput:
  *ResetType      = 0;
  *MaxCapsuleSize = gCapsuleMaxSizeNonCapsule;
  return EFI_SUCCESS;
}

// ---------------------------------------------------------------------------
// IsCapspaceGuidEqual (sub_1710)
// ---------------------------------------------------------------------------

/**
  Compares two EFI_GUIDs for equality using 64-bit unaligned reads.

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

  @retval TRUE   The GUIDs are equal.
  @retval FALSE  The GUIDs are not equal.
**/
BOOLEAN
EFIAPI
IsCapspaceGuidEqual (
  IN EFI_GUID *Guid1,
  IN EFI_GUID *Guid2
  )
{
  UINT64  Guid1Part1;
  UINT64  Guid1Part2;

  Guid1Part1 = ReadUnaligned64 ((UINT64 *)Guid1);
  Guid2->Data1 = (UINT32)ReadUnaligned64 ((UINT64 *)Guid2);
  Guid1Part2 = ReadUnaligned64 ((UINT64 *)Guid1 + 1);
  Guid2->Data2 = (UINT16)ReadUnaligned64 ((UINT64 *)Guid2 + 1);

  return (BOOLEAN)(Guid1Part1 == Guid2->Data1 && Guid1Part2 == Guid2->Data2);
}

// ---------------------------------------------------------------------------
// ReadUnaligned64 (sub_1778)
// ---------------------------------------------------------------------------

/**
  Reads a 64-bit value from an unaligned address.

  @param[in] Buffer  Pointer to the unaligned 64-bit value.

  @return The 64-bit value read from Buffer.
**/
UINT64
ReadUnaligned64 (
  IN UINT64 *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *Buffer;
}

// ---------------------------------------------------------------------------
// GetCapsuleRuntimeProtocol (sub_17A8)
// ---------------------------------------------------------------------------

/**
  Retrieves the capsule runtime protocol interface.

  Uses gBS->LocateProtocol to find the protocol. Caches the result.

  @return Pointer to the capsule runtime protocol interface, or NULL
          if not found or if we are not in boot services.
**/
STATIC
EFI_STATUS
GetCapsuleRuntimeProtocol (
  VOID
  )
{
  EFI_STATUS  Status;

  if (mCapsuleRuntimeProtocol != NULL) {
    return EFI_SUCCESS;
  }

  if (gBS_Runtime != NULL) {
    UINTN  HobSize;

    //
    // Check if HOB list has room (<= 16 bytes header)
    //
    HobSize = gBS->GetHobListSize ();
    if (HobSize <= sizeof (EFI_HOB_GENERIC_HEADER)) {
      Status = gBS->LocateProtocol (
                      &gEfiCapsuleRuntimeProtocolGuid,
                      NULL,
                      &mCapsuleRuntimeProtocol
                      );
      if (EFI_ERROR (Status)) {
        mCapsuleRuntimeProtocol = NULL;
      }
      return mCapsuleRuntimeProtocol;
    }
  }

  return NULL;
}

// ---------------------------------------------------------------------------
// DebugAssert (sub_1830)
// ---------------------------------------------------------------------------

/**
  Internal debug ASSERT primitive with port 0x70/0x71 CMOS debug level
  filtering.

  Reads the current debug level from CMOS (port 0x70 index 0x4B), clamps
  to 0-3, then checks if the given error severity passes the debug mask.
  If so, calls into the registered debug print function.

  @param[in] ErrorLevel  Error severity (EFI_D_* bitmask).
  @param[in] Format      Format string.
  @param[in] ...         Variable arguments.
**/
VOID
DebugAssertInternal (
  IN UINT64       ErrorLevel,
  IN CONST CHAR8  *Format,
  ...
  )
{
  VA_LIST                 VaList;
  DEBUG_PRINT_FUNCTION    DebugPrint;
  UINT8                   DebugLevel;
  UINT64                  ErrorMask;

  DebugPrint = (DEBUG_PRINT_FUNCTION)GetCapsuleRuntimeProtocol ();
  ErrorMask  = 0;

  if (DebugPrint != NULL) {
    //
    // Read debug level from CMOS
    //
    DebugLevel = IoRead8 (0x70);
    IoWrite8 (0x70, (DebugLevel & 0x80) | 0x4B);
    DebugLevel = IoRead8 (0x71);
    if (DebugLevel > 3) {
      DebugLevel = 3;
    }

    //
    // Map debug level to severity mask
    //
    switch (DebugLevel) {
      case 1:
        ErrorMask = EFI_D_ERROR;
        break;
      case 2:
        ErrorMask = EFI_D_INFO;
        break;
      case 3:
      default:
        ErrorMask = EFI_D_VERBOSE;
        break;
    }

    if ((ErrorMask & ErrorLevel) != 0) {
      VA_START (VaList, Format);
      DebugPrint (ErrorLevel, Format, VaList);
      VA_END (VaList);
    }
  }
}

// ---------------------------------------------------------------------------
// DebugPrint (sub_18B0)
// ---------------------------------------------------------------------------

/**
  Debug print wrapper using the capsule runtime protocol.

  @param[in] FileName   Source file name string.
  @param[in] LineNumber  Line number in the source file.
  @param[in] Format     Format string.
  @param[in] ...        Variable arguments.

  @return Status from the protocol print function, or EFI_UNSUPPORTED
          if the protocol is not available.
**/
UINT64
DebugPrint (
  IN UINT64       FileName,
  IN UINT64       LineNumber,
  IN UINT64       Format,
  ...
  )
{
  DEBUG_PRINT_FUNCTION  DebugPrint;

  DebugPrint = (DEBUG_PRINT_FUNCTION)GetCapsuleRuntimeProtocol ();
  if (DebugPrint != NULL) {
    return DebugPrint (FileName, LineNumber, Format);
  }

  return EFI_UNSUPPORTED;
}

// ---------------------------------------------------------------------------
// VirtualAddressChangeEvent (sub_18F0)
// ---------------------------------------------------------------------------

/**
  Virtual Address Change event notification handler.

  Sets the boot services pointer to NULL so that runtime code does not
  accidentally call boot services after SetVirtualAddressMap.

  @param[in] Event    Event whose notification function is being invoked.
  @param[in] Context  Pointer to the notification function's context.
**/
VOID
EFIAPI
VirtualAddressChangeEvent (
  IN EFI_EVENT  Event,
  IN VOID      *Context
  )
{
  gBS_Runtime = NULL;
}

// ---------------------------------------------------------------------------
// ExitBootServicesEvent (sub_18FC)
// ---------------------------------------------------------------------------

/**
  Exit Boot Services event notification handler.

  Frees the capsule runtime protocol reference so it cannot be used
  after boot services are exited.

  @param[in] Event    Event whose notification function is being invoked.
  @param[in] Context  Pointer to the notification function's context.
**/
VOID
EFIAPI
ExitBootServicesEvent (
  IN EFI_EVENT  Event,
  IN VOID      *Context
  )
{
  if (mCapsuleRuntimeProtocol != NULL) {
    gRT_Runtime->FreePages (0, (UINTN)&mCapsuleRuntimeProtocol);
  }
}

// ---------------------------------------------------------------------------
// GetHobList (sub_1924)
// ---------------------------------------------------------------------------

/**
  Returns the pointer to the HOB list.

  Locates the HOB list from the System Table configuration table using
  the HOB list GUID. Caches the result.

  @return Pointer to the HOB list.
**/
VOID *
GetHobList (
  VOID
  )
{
  EFI_STATUS           Status;
  UINTN                Index;
  EFI_CONFIGURATION_TABLE *ConfigTable;

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

  mHobList = NULL;
  if (gST->NumberOfTableEntries > 0) {
    ConfigTable = gST->ConfigurationTable;
    for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
      if (IsCapspaceGuidEqual (
            (EFI_GUID *)&gEfiHobListGuid,
            (EFI_GUID *)((UINT8 *)ConfigTable + Index * sizeof (EFI_CONFIGURATION_TABLE))
            )) {
        mHobList = (VOID *)ConfigTable[Index].VendorTable;
        break;
      }
    }
  }

  if (mHobList == NULL) {
    Status = EFI_NOT_FOUND;
    DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT_EFI_ERROR (FALSE);
  }

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

  return mHobList;
}

// ---------------------------------------------------------------------------
// CheckCapsuleType (sub_1A04)
// ---------------------------------------------------------------------------

/**
  Determines whether a capsule is of a recognized type by comparing its
  GUID against a known table.

  @param[in] CapsuleHeader  Pointer to the capsule header to check.

  @retval EFI_SUCCESS            The capsule type is recognized.
  @retval EFI_UNSUPPORTED        The capsule type is not recognized.
  @retval EFI_INVALID_PARAMETER  CapsuleHeader is NULL.
**/
EFI_STATUS
EFIAPI
CheckCapsuleType (
  IN EFI_CAPSULE_HEADER *CapsuleHeader
  )
{
  UINT32  TableIndex;
  UINT64  GuidIndex;

  //
  // First check against 2 known capsule GUIDs in the local table
  //
  for (TableIndex = 0; TableIndex < 2; TableIndex++) {
    if (IsCapspaceGuidEqual (CapsuleHeader->CapsuleGuid, &mCapsuleGuidTable[TableIndex])) {
      return EFI_SUCCESS;
    }
  }

  //
  // Then check against the extern capsule GUID list
  //
  if (&gExternCapsuleGuidList == NULL) {
    return EFI_UNSUPPORTED;
  }

  GuidIndex = 0;
  while (gExternCapsuleGuidList[GuidIndex] != NULL) {
    if (IsCapspaceGuidEqual (gExternCapsuleGuidList[GuidIndex], CapsuleHeader->CapsuleGuid)) {
      return EFI_SUCCESS;
    }
    GuidIndex++;
  }

  return EFI_UNSUPPORTED;
}

// ---------------------------------------------------------------------------
// RuntimeCapsuleEvent (sub_1A94)
// ---------------------------------------------------------------------------

/**
  Runtime capsule event notification.

  Used as a notification function for a wait event that is created
  during driver entry.

  @param[in] Event    Event whose notification function is being invoked.
  @param[in] Context  Pointer to the notification function's context.
**/
VOID
EFIAPI
RuntimeCapsuleEvent (
  IN EFI_EVENT  Event,
  IN VOID      *Context
  )
{
  gRT_Runtime->FreePages (0, (UINTN)&mCapsuleRuntimeProtocol);
}

// ---------------------------------------------------------------------------
// CapsuleRuntimeVariableEvent (sub_1A8C)
// ---------------------------------------------------------------------------

/**
  Capsule runtime variable event notification handler.

  Marks that we are now in runtime phase (ExitBootServices has been signaled).

  @param[in] Event    Event whose notification function is being invoked.
  @param[in] Context  Pointer to the notification function's context.
**/
VOID
EFIAPI
CapsuleRuntimeVariableEvent (
  IN EFI_EVENT  Event,
  IN VOID      *Context
  )
{
  gCapsuleInRuntime = 1;
}

// ---------------------------------------------------------------------------
// AsmCpuid (sub_1020)
// ---------------------------------------------------------------------------

/**
  Executes the CPUID instruction.

  @param[in]  Index    The CPUID leaf (EAX input).
  @param[out] Eax      CPUID return value for EAX.
  @param[out] Ebx      CPUID return value for EBX.
  @param[out] Ecx      CPUID return value for ECX.
  @param[out] Edx      CPUID return value for EDX.

  @return The CPUID Index (EAX).
**/
UINT64
AsmCpuid (
  IN  UINT32  Index,
  OUT UINT32 *Eax  OPTIONAL,
  OUT UINT32 *Ebx  OPTIONAL,
  OUT UINT32 *Ecx  OPTIONAL,
  OUT UINT32 *Edx  OPTIONAL
  )
{
  UINT64  RetValue;

  RetValue = Index;

  __asm {
    mov     eax, Index
    cpuid
    mov     RetValue, rax
  }

  if (Ecx != NULL) {
    *Ecx = _RCX;
  }
  if (Eax != NULL) {
    *Eax = _RAX;
  }
  if (Ebx != NULL) {
    *Ebx = _RBX;
  }
  if (Edx != NULL) {
    *Edx = _RDX;
  }

  return RetValue;
}