Newer
Older
AMI-Aptio-BIOS-Reversed / LegacyRegion2 / LegacyRegion2.c
@Ajax Dong Ajax Dong 2 days ago 24 KB Init
/** @file
  LegacyRegion2 DXE driver - Reconstructed C source.

  Manages PAM (Programmable Attribute Map) registers to control
  read/write/execute permissions on the legacy VGA/BIOS memory region
  (0xC0000 - 0xFFFFF).  Installs the EFI_LEGACY_REGION2_PROTOCOL.

  Source file:  e:\hs\PurleyPlatPkg\Legacy\Dxe\LegacyRegion\LegacyRegion.c
  PDB:          LegacyRegion2.pdb
  Build:        VS2015 DEBUG, PurleyPlatPkg

  Copyright (C) 2026 Contributors. All rights reserved.
**/

#include "LegacyRegion2.h"

//
// ---------------------------------------------------------------------------
// Global data
// ---------------------------------------------------------------------------
//

//
// PAM region descriptor table.
// 13 entries of 9 bytes each = 117 bytes.
// Located at .data offset 0x1050 (unk_1050).
//
LEGACY_REGION_DESCRIPTOR  mRegionDescriptorTable[LEGACY_REGION_DESCRIPTOR_COUNT] = {
  { 0xF0000, 0x10000, 0 },   // 0xF0000 - 0xFFFFF (BIOS segment F, 64 KB)
  { 0xC0000, 0x4000,  1 },   // 0xC0000 - 0xC3FFF (VGA BIOS segment 0, 16 KB)
  { 0xC4000, 0x4000,  2 },   // 0xC4000 - 0xC7FFF
  { 0xC8000, 0x4000,  3 },   // 0xC8000 - 0xCBFFF
  { 0xCC000, 0x4000,  4 },   // 0xCC000 - 0xCFFFF
  { 0xD0000, 0x4000,  5 },   // 0xD0000 - 0xD3FFF
  { 0xD4000, 0x4000,  6 },   // 0xD4000 - 0xD7FFF
  { 0xD8000, 0x4000,  7 },   // 0xD8000 - 0xDBFFF
  { 0xDC000, 0x4000,  8 },   // 0xDC000 - 0xDFFFF
  { 0xE0000, 0x4000,  9 },   // 0xE0000 - 0xE3FFF
  { 0xE4000, 0x4000, 10 },   // 0xE4000 - 0xE7FFF
  { 0xE8000, 0x4000, 11 },   // 0xE8000 - 0xEBFFF
  { 0xEC000, 0x4000, 12 },   // 0xEC000 - 0xEFFFF
};

//
// Global variable definitions.
// These are initialised in ModuleEntryPoint via library constructor code
// linked from MdePkg UefiBootServicesTableLib / UefiRuntimeServicesTableLib.
//
EFI_HANDLE                 gImageHandle       = NULL;  // 0x10F0
EFI_SYSTEM_TABLE          *gST                = NULL;  // 0x10E0
EFI_BOOT_SERVICES         *gBS                = NULL;  // 0x10E8
EFI_RUNTIME_SERVICES      *gRT                = NULL;  // 0x10F8

//
// Located protocols.
//
EFI_LEGACY_REGION_PROTOCOL   *gLegacyRegion   = NULL;  // 0x1170 (qword_1170)
EFI_LEGACY_REGION2_PROTOCOL  *gLegacyRegion2  = NULL;  // 0x1168 (qword_1168)

//
// PAM capabilities bitmask read from gLegacyRegion2->PamCapabilities.
// Lower 4 bits indicate which PAM attribute slots are active.
//
UINT32                       gPamCapabilities = 0;     // 0x1160 (dword_1160)

//
// Cached HOB list pointer, initially NULL.  Populated by GetHobList().
//
VOID                        *gHobList         = NULL;  // 0x1108 (qword_1108)

//
// Signature / sentinel value.  'INIT' in little-endian = 0x4E495449.
//
UINT32                       gLegacyRegion2Signature = 0x4E495449; // 0x1120 (n1313293650)

//
// Saved ImageHandle for protocol installation.
//
EFI_HANDLE                   gImageHandleSaved = NULL; // 0x1158 (ImageHandle_0)

//
// Protocol handle for the installed LegacyRegion2 protocol.
//
EFI_HANDLE                   gLegacyRegion2ProtocolHandle = NULL; // 0x1128 (qword_1128)

//
// EFI_LEGACY_REGION2_PROTOCOL function table (installed as protocol interface).
//
// Individual function pointers populated in WheaSupportEntry().
//
EFI_LEGACY_REGION2_PROTOCOL  gLegacyRegion2Protocol;   // 0x1130-0x1158

// Function pointer slots used for initialising above protocol:
// psub_898  at 0x1130  -> LegacyRegion2Decode
// psub_8BC  at 0x1138  -> LegacyRegion2Program
// psub_8BC_0 at 0x1140 -> LegacyRegion2ProgramLock
// psub_930  at 0x1148  -> LegacyRegion2GetMaxSize
// psub_588  at 0x1150  -> LegacyRegion2GetMaxSize (fallback/unused)
// ImageHandle_0 at 0x1158 -> gImageHandleSaved

//
// ---------------------------------------------------------------------------
// Library helper functions (linked from MdePkg BaseLib / DebugLib)
// ---------------------------------------------------------------------------
//

/**
  ASSERT and debug-log macro helper (library wrapper).

  Writes the status string via the debug library's assertion handler,
  which performs CMOS-based platform type detection and routes to the
  appropriate serial/console output.

  @param  Status          Error status.
  @param  FormatString    Format string.
  @param  ...             Variable arguments.
**/
VOID
DebugAssertPrint (
  IN  UINTN       Status,
  IN  CONST CHAR8 *FormatString,
  ...
  )
{
  //
  // This function is a thin wrapper around the platform's debug print
  // (DebugLib).  It checks the CMOS register 0x4B to determine the
  // platform type, reads I/O port 0x70/0x71 to get the status bits,
  // then calls into the registered debug handler obtained via the HOB.
  //
  __debugbreak ();  // Placeholder -- implementation is in DebugLib
}

/**
  ASSERT macro helper.

  Invokes the assertion call-out with the file/line/condition text.

  @param  FileName    Source file name.
  @param  LineNumber  Line number.
  @param  Condition   Condition string.
**/
VOID
DebugAssert (
  IN  CONST CHAR8  *FileName,
  IN  UINTN         LineNumber,
  IN  CONST CHAR8  *Condition
  )
{
  //
  // Calls the registered assertion handler (via HOB callback).
  // Placeholder -- implementation is in DebugLib.
  //
  __debugbreak ();
}

/**
  Read an unaligned 64-bit value from memory.

  @param  Buffer  Pointer to the 8-byte value.

  @return The 64-bit value read.
**/
UINT64
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  if (Buffer == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c", 192,
      "Buffer != ((void *) 0)"
      );
  }
  return *(const UINT64 *)Buffer;
}

//
// ---------------------------------------------------------------------------
// HOB list lookup (replacement for DxeHobLib)
// ---------------------------------------------------------------------------
//

/**
  Check if a Configuration Table entry GUID matches the HOB list GUID.

  Compares the full 16 bytes of the entry's VendorGuid against the
  HOB list GUID at 0x1040 ({7739F24C-93D7-11D4-9A3A-0090273FC14D}),
  using two 8-byte unaligned reads.

  @param  ConfigEntry  Pointer to the EFI_CONFIGURATION_TABLE entry.

  @retval TRUE   The entry's GUID matches the HOB list GUID.
  @retval FALSE  No match.
**/
BOOLEAN
EFIAPI
IsHobListConfigEntry (
  IN VOID  *ConfigEntry
  )
{
  EFI_CONFIGURATION_TABLE  *Entry = (EFI_CONFIGURATION_TABLE *)ConfigEntry;
  EFI_GUID                  HobListGuid = HOB_LIST_GUID;

  //
  // Compare first 8 bytes and last 8 bytes of the GUID separately
  // via unaligned reads (ReadUnaligned64 from BaseLib).
  //
  return (ReadUnaligned64 (&Entry->VendorGuid) == ReadUnaligned64 (&HobListGuid)) &&
         (ReadUnaligned64 (&Entry->VendorGuid.Data4) == ReadUnaligned64 (&HobListGuid.Data4));
}

/**
  Get the HOB list pointer from the System Table Configuration Table.

  Scans gST->ConfigurationTable for an entry whose VendorGuid matches
  the HOB List GUID ({7739F24C-93D7-11D4-9A3A-0090273FC14D}) and
  returns the associated VendorTable pointer (which is the HOB list).

  @return  Pointer to the HOB list, or NULL if not found.
**/
VOID *
GetHobList (
  VOID
  )
{
  UINTN  Index;

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

  gHobList = NULL;

  //
  // Iterate over the Configuration Table.
  // gST->ConfigurationTable is an array of EFI_CONFIGURATION_TABLE entries,
  // each 24 bytes (16-byte GUID + 8-byte pointer).
  //
  if (gST->NumberOfTableEntries > 0) {
    for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
      if (IsHobListConfigEntry (
            (VOID *)&gST->ConfigurationTable[Index]
            )) {
        gHobList = gST->ConfigurationTable[Index].VendorTable;
        break;
      }
    }
  }

  //
  // If not found, ASSERT via the debug library.
  //
  if (gHobList == NULL) {
    DebugAssertPrint (
      0x80000000LL,
      "\nASSERT_EFI_ERROR (Status = %r)\n",
      0x800000000000000EuLL
      );
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c", 54,
      "!EFI_ERROR (Status)"
      );
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c", 55,
      "mHobList != ((void *) 0)"
      );
  }

  return gHobList;
}

/**
  Return a cached version of the DXE HOB list pointer.

  @return  The cached HOB list pointer.
**/
VOID *
GetHobListCache (
  VOID
  )
{
  return GetHobList ();
}

//
// ---------------------------------------------------------------------------
// PAM (Programmable Attribute Map) register helpers
// ---------------------------------------------------------------------------
//

/**
  Read a PAM attribute register for a given region index.

  Accesses the chipset PAM I/O registers via the LegacyRegion protocol's
  ReadPamRegister function (offset +8 in the protocol vtable).

  @param  RegionIndex  Index of the region (0-3, up to 4 per PAM register).

  @return The 32-bit PAM register value.
**/
UINT32
ReadPamAttribute (
  IN  UINT8  RegionIndex
  )
{
  return gLegacyRegion->ReadPamRegister (RegionIndex, 0, PAM_READ_PORT);
}

/**
  Write a PAM attribute register for a given region index.

  @param  RegionIndex  Index of the region.
  @param  Value        The 32-bit PAM register value to write.
**/
VOID
WritePamAttribute (
  IN  UINT8   RegionIndex,
  IN  UINT32  Value
  )
{
  gLegacyRegion->WritePamRegister (RegionIndex, 0, PAM_WRITE_PORT, Value);
}

//
// ---------------------------------------------------------------------------
// EFI_LEGACY_REGION2_PROTOCOL implementation
// ---------------------------------------------------------------------------
//

/**
  Decode a region of legacy memory.

  Validates that the requested range [StartAddress, StartAddress+Length)
  is entirely within 0xC0000-0xFFFFF.

  @param  This            Protocol instance.
  @param  StartAddress    Start of region.
  @param  Length          Length of region.

  @retval EFI_SUCCESS             Range is valid.
  @retval EFI_INVALID_PARAMETER   Range extends outside legacy region.
**/
EFI_STATUS
EFIAPI
LegacyRegion2Decode (
  IN  EFI_LEGACY_REGION2_PROTOCOL  *This,
  IN  UINT32                       StartAddress,
  IN  UINT32                       Length
  )
{
  if ((StartAddress < LEGACY_REGION_BASE) ||
      (StartAddress + Length - 1 > LEGACY_REGION_TOP)) {
    return EFI_INVALID_PARAMETER;
  }

  return EFI_SUCCESS;
}

/**
  LegacyRegion2Program - Program legacy region for read/write decode.

  Calls LegacyRegion2Decode for validation, then programs the PAM registers.

  @param  This            Protocol instance.
  @param  StartAddress    Start of region.
  @param  Length          Length of region.
  @param  LegacyDecode    TRUE = enable decode (read/write).
  @param  RealReadSignal  TRUE = assert read signal.

  @retval EFI_SUCCESS             Region programmed.
  @retval EFI_INVALID_PARAMETER   Region outside 0xC0000-0xFFFFF.
**/
EFI_STATUS
EFIAPI
LegacyRegion2Program (
  IN  EFI_LEGACY_REGION2_PROTOCOL  *This,
  IN  UINT32                       StartAddress,
  IN  UINT32                       Length,
  IN  BOOLEAN                      LegacyDecode,
  IN  BOOLEAN                      RealReadSignal
  )
{
  EFI_STATUS  Status;

  Status = LegacyRegion2Decode (This, StartAddress, Length);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Check platform write-disable policy via LegacyRegion2 protocol.
  // If the platform has the write-lock active (Capabilities bit 0),
  // force write-enable (RealReadSignal = 1).
  //
  // The original assembly checks:
  //   *(gLegacyRegion2->... + 0x06F4) byte != 0  -> write locked
  //   *(gLegacyRegion2->... + 0x06F1) byte >= 3 -> SMM version >= 3
  //
  if (RealReadSignal) {
    if (*(UINT8 *)((UINTN)gLegacyRegion2 + 0x06F4) != 0 ||
        *(UINT8 *)((UINTN)gLegacyRegion2 + 0x06F1) >= 3) {
      RealReadSignal = 1;
    }
  }

  return ProgramPamRegisters (
           StartAddress,
           Length,
           LegacyDecode ? 1 : 0,
           RealReadSignal ? 1 : 0,
           NULL
           );
}

/**
  LegacyRegion2ProgramLock - Program with full locking semantics.

  Forces LegacyDecode=2 (override) and uses RealReadSignal policy from
  the LegacyRegion2 protocol's internal state.

  @param  This            Protocol instance.
  @param  StartAddress    Start of region.
  @param  Length          Length of region.
  @param  LegacyDecode    TRUE = enable decode.
  @param  RealReadSignal  TRUE = assert read signal.

  @retval EFI_SUCCESS             Region programmed.
  @retval EFI_INVALID_PARAMETER   Region outside 0xC0000-0xFFFFF.
**/
EFI_STATUS
EFIAPI
LegacyRegion2ProgramLock (
  IN  EFI_LEGACY_REGION2_PROTOCOL  *This,
  IN  UINT32                       StartAddress,
  IN  UINT32                       Length,
  IN  BOOLEAN                      LegacyDecode,
  IN  BOOLEAN                      RealReadSignal
  )
{
  EFI_STATUS  Status;

  Status = LegacyRegion2Decode (This, StartAddress, Length);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Check platform write-disable via LegacyRegion2 protocol internal state.
  //
  if (RealReadSignal) {
    if (*(UINT8 *)((UINTN)gLegacyRegion2 + 0x06F4) != 0 ||
        *(UINT8 *)((UINTN)gLegacyRegion2 + 0x06F1) >= 3) {
      RealReadSignal = 1;
    }
  }

  //
  // LegacyDecode=2 signals "override" in the PAM programming core.
  //
  return ProgramPamRegisters (
           StartAddress,
           Length,
           2,
           RealReadSignal ? 1 : 0,
           NULL
           );
}

/**
  LegacyRegion2GetMaxSize - Return maximum decode size.

  @param  This       Protocol instance.
  @param  MaxSize    Receives the maximum size.

  @retval EFI_SUCCESS   MaxSize was set.
**/
EFI_STATUS
EFIAPI
LegacyRegion2GetMaxSize (
  IN  EFI_LEGACY_REGION2_PROTOCOL  *This,
  OUT UINT32                       *MaxSize
  )
{
  *MaxSize = 0;
  return EFI_SUCCESS;
}

//
// ---------------------------------------------------------------------------
// PAM register programming core
// ---------------------------------------------------------------------------
//

/**
  Program PAM registers for the given range.

  Iterates over all 13 entries in mRegionDescriptorTable and for every
  entry that overlaps the requested [StartAddress, StartAddress+Length)
  range:
  - Determines the region's PAM attribute bit position (based on Type).
  - Reads the current 32-bit PAM register value via LegacyRegion protocol.
  - Modifies the 2-bit attribute field (read-enable, write-enable).
  - Writes the updated register value back.

  @param  StartAddress    Start of the range.
  @param  Length          Length of the range.
  @param  LegacyDecode    0=read-only, 1=enable, 2=override (force enable).
  @param  RealReadSignal  0=force read-only (on overlap), 1=assert writes.
  @param  MaxSize         Optional: receives the largest region size encountered.

  @retval EFI_SUCCESS     Always returned.
**/
EFI_STATUS
ProgramPamRegisters (
  IN  UINT32    StartAddress,
  IN  UINT32    Length,
  IN  UINT32    LegacyDecode,
  IN  UINT32    RealReadSignal,
  OUT UINT32   *MaxSize OPTIONAL
  )
{
  UINT32   MaxRegionSize;
  UINTN    Index;
  UINT8    RegionIndex;
  UINT32   RequestEnd;

  MaxRegionSize = 0;
  RequestEnd    = StartAddress + Length;

  //
  // Iterate over all 13 region descriptors.
  //
  for (Index = 0; Index < LEGACY_REGION_DESCRIPTOR_COUNT; Index++) {
    LEGACY_REGION_DESCRIPTOR  *Region = &mRegionDescriptorTable[Index];
    UINT32  RegionBase   = Region->Base;
    UINT32  RegionLength = Region->Length;
    UINT32  RegionType   = Region->Type;
    UINT32  RegionEnd    = RegionBase + RegionLength;
    UINT32  ReadValue;
    UINT32  WriteValue;
    UINT32  Attr;
    BOOLEAN Overlap;

    //
    // Check for overlap between request and this region.
    //
    Overlap = (StartAddress < RegionEnd && RequestEnd > RegionBase);

    if (!Overlap) {
      continue;
    }

    //
    // Track the largest region size for the optional MaxSize output.
    //
    if (RegionLength > MaxRegionSize) {
      MaxRegionSize = RegionLength;
    }

    //
    // Find the first active PAM attribute slot from the capabilities mask.
    // gPamCapabilities lower 4 bits indicate which of the 4 slots per
    // PAM register are active for this platform.
    //
    for (RegionIndex = 0; RegionIndex < 4; RegionIndex++) {
      if ((gPamCapabilities >> RegionIndex) & 1) {
        break;
      }
    }

    //
    // Read current PAM register values.
    // Two reads: the "read port" register and the "write port" register.
    // For types 0-6, attributes are in ReadValue; for types 7-12, in WriteValue.
    //
    ReadValue  = ReadPamAttribute (RegionIndex);
    WriteValue = ReadPamAttribute (RegionIndex);

    //
    // Extract the 2-bit attribute field based on region type.
    // PAM registers pack 4 x 2-bit attributes into a 32-bit value.
    // Type determines which 2-bit slot:
    //   Types 0-6:  8 bits per slot (bits 4-5 for type 0, 8-9 for type 1, ...)
    //   Types 7-12: same layout but in WriteValue (bits 0-1 for type 7, ...)
    //
    if (RegionType <= 6) {
      //
      // Attributes are in the low PAM register (ReadValue).
      //
      switch (RegionType) {
        case 0: Attr = (ReadValue >> 4)  & 3; break;
        case 1: Attr = (ReadValue >> 8)  & 3; break;
        case 2: Attr = (ReadValue >> 12) & 3; break;
        case 3: Attr = (ReadValue >> 16) & 3; break;
        case 4: Attr = (ReadValue >> 20) & 3; break;
        case 5: Attr = (ReadValue >> 24) & 3; break;
        case 6: Attr = (ReadValue >> 28) & 3; break;
        default: Attr = 0; break;
      }
    } else {
      //
      // Attributes are in the high PAM register (WriteValue).
      //
      switch (RegionType) {
        case 7:  Attr = WriteValue       & 3; break;
        case 8:  Attr = (WriteValue >> 4)  & 3; break;
        case 9:  Attr = (WriteValue >> 8)  & 3; break;
        case 10: Attr = (WriteValue >> 12) & 3; break;
        case 11: Attr = (WriteValue >> 16) & 3; break;
        case 12: Attr = (WriteValue >> 20) & 3; break;
        default: Attr = 0; break;
      }
    }

    //
    // Modify the attribute bits.
    // If LegacyDecode == 1: set bit 0 (read enable).
    // If RealReadSignal == 1: set bit 1 (write enable).
    // If RealReadSignal == 0 and overlap: clear bit 1.
    //
    if (LegacyDecode == 1) {
      Attr |= 1;
    }

    if (RealReadSignal == 1) {
      Attr |= 2;
    } else if (Overlap) {
      Attr &= ~2;
    }

    //
    // Write the modified PAM register values back.
    // For types 0-6: modify ReadValue.
    // For types 7-12: modify WriteValue, then XOR attrs between
    // the two registers (the LegacyRegion2 protocol expects the
    // attribute bits to be toggled across both registers).
    //
    if (RegionType <= 6) {
      //
      // Apply to the low PAM register (ReadValue).
      //
      switch (RegionType) {
        case 0: ReadValue = (ReadValue & ~0x30)         | (Attr << 4);  break;
        case 1: ReadValue = (ReadValue & ~0x300)        | (Attr << 8);  break;
        case 2: ReadValue = (ReadValue & ~0x3000)       | (Attr << 12); break;
        case 3: ReadValue = (ReadValue & ~0x30000)      | (Attr << 16); break;
        case 4: ReadValue = (ReadValue & ~0x300000)     | (Attr << 20); break;
        case 5: ReadValue = (ReadValue & ~0x3000000)    | (Attr << 24); break;
        case 6: ReadValue = (ReadValue & ~0x30000000)   | (Attr << 28); break;
      }

      //
      // Perform two writes: the PAM controller expects separate
      // writes to the read-port and write-port registers.
      //
      WritePamAttribute (RegionIndex, ReadValue);
      WritePamAttribute (RegionIndex, WriteValue);

    } else {
      //
      // Apply to the high PAM register (WriteValue).
      //
      switch (RegionType) {
        case 7:  WriteValue = (WriteValue & ~3)          | (Attr & 3);       break;
        case 8:  WriteValue = (WriteValue & ~0x30)       | ((Attr & 3) << 4);  break;
        case 9:  WriteValue = (WriteValue & ~0x300)      | ((Attr & 3) << 8);  break;
        case 10: WriteValue = (WriteValue & ~0x3000)     | ((Attr & 3) << 12); break;
        case 11: WriteValue = (WriteValue & ~0x30000)    | ((Attr & 3) << 16); break;
        case 12: WriteValue = (WriteValue & ~0x300000)   | ((Attr & 3) << 20); break;
      }

      //
      // XOR the attribute bits into ReadValue to synchronise both PAM registers.
      //
      ReadValue ^= (WriteValue & 0x300) ? 2 : 0;

      WritePamAttribute (RegionIndex, ReadValue);
      WritePamAttribute (RegionIndex, WriteValue);
    }
  }

  if (MaxSize != NULL) {
    *MaxSize = MaxRegionSize;
  }

  return EFI_SUCCESS;
}

//
// ---------------------------------------------------------------------------
// Entry Point
// ---------------------------------------------------------------------------
//

/**
  Entry point for the LegacyRegion2 DXE driver.

  Saves ImageHandle and SystemTable to globals (for library consumption),
  initialises the HOB cache, and calls WheaSupportEntry.

  @param  ImageHandle     The firmware-allocated handle for this image.
  @param  SystemTable     The EFI system table.

  @retval EFI_SUCCESS     The driver initialised successfully.
  @retval Others          An error occurred.
**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE          ImageHandle,
  IN EFI_SYSTEM_TABLE   *SystemTable
  )
{
  //
  // Save globals for library use (UefiBootServicesTableLib /
  // UefiRuntimeServicesTableLib constructors).
  //
  gImageHandle = ImageHandle;
  gST          = SystemTable;
  gBS          = SystemTable->BootServices;
  gRT          = SystemTable->RuntimeServices;

  //
  // Initialise the HOB list cache.
  //
  GetHobList ();

  //
  // Call the main driver initialisation.
  //
  return WheaSupportEntry (ImageHandle);
}

//
// ---------------------------------------------------------------------------
// Driver Initialisation
// ---------------------------------------------------------------------------
//

/**
  Main driver initialisation routine (WheaSupportEntry style).

  Locates the EFI_LEGACY_REGION_PROTOCOL and EFI_LEGACY_REGION2_PROTOCOL,
  reads PAM capabilities, populates the LegacyRegion2 protocol function
  table, and installs it on a new protocol handle.

  @param  ImageHandle     The image handle.

  @retval EFI_SUCCESS     Protocol installed successfully.
  @retval Others          An error occurred locating protocols or installing.
**/
EFI_STATUS
WheaSupportEntry (
  IN EFI_HANDLE          ImageHandle
  )
{
  EFI_STATUS  Status;

  //
  // Save ImageHandle for the protocol installation.
  //
  gImageHandleSaved = ImageHandle;

  //
  // Locate the EFI_LEGACY_REGION_PROTOCOL (provided by CSM / platform driver).
  //
  Status = gBS->LocateProtocol (
                  &gEfiLegacyRegionProtocolGuid,
                  NULL,
                  (VOID **)&gLegacyRegion
                  );
  if (EFI_ERROR (Status)) {
    DebugAssertPrint (
      0x80000000LL,
      "\nASSERT_EFI_ERROR (Status = %r)\n",
      Status
      );
    DebugAssert (
      "e:\\hs\\PurleyPlatPkg\\Legacy\\Dxe\\LegacyRegion\\LegacyRegion.c",
      573,
      "!EFI_ERROR (Status)"
      );
  }

  //
  // Locate the EFI_LEGACY_REGION2_PROTOCOL (provided by SMM / other).
  //
  Status = gBS->LocateProtocol (
                  &gEfiLegacyRegion2ProtocolGuid,
                  NULL,
                  (VOID **)&gLegacyRegion2
                  );
  if (EFI_ERROR (Status)) {
    DebugAssertPrint (
      0x80000000LL,
      "\nASSERT_EFI_ERROR (Status = %r)\n",
      Status
      );
    DebugAssert (
      "e:\\hs\\PurleyPlatPkg\\Legacy\\Dxe\\LegacyRegion\\LegacyRegion.c",
      575,
      "!EFI_ERROR (Status)"
      );
  }

  //
  // Read the PAM capabilities from the LegacyRegion2 protocol.
  //
  gPamCapabilities = gLegacyRegion2->PamCapabilities;
  gLegacyRegion2Signature = 0x4E495449;  // 'INIT'

  //
  // Build the LegacyRegion2 protocol function table.
  //
  gLegacyRegion2Protocol.Decode            = LegacyRegion2Decode;
  gLegacyRegion2Protocol.Program           = LegacyRegion2Program;
  gLegacyRegion2Protocol.ProgramLock       = LegacyRegion2ProgramLock;
  gLegacyRegion2Protocol.GetMaxSize        = LegacyRegion2GetMaxSize;
  gLegacyRegion2Protocol.RegionCapabilities = gPamCapabilities;

  //
  // Install the protocol on a new handle.
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &gLegacyRegion2ProtocolHandle,
                  &gEfiLegacyRegion2ProtocolGuid,
                  &gLegacyRegion2Protocol,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    DebugAssertPrint (
      0x80000000LL,
      "\nASSERT_EFI_ERROR (Status = %r)\n",
      Status
      );
    DebugAssert (
      "e:\\hs\\PurleyPlatPkg\\Legacy\\Dxe\\LegacyRegion\\LegacyRegion.c",
      597,
      "!EFI_ERROR (Status)"
      );
  }

  return EFI_SUCCESS;
}