Newer
Older
AMI-Aptio-BIOS-Reversed / PcatSingleSegmentPciCfg2Pei / PcatSingleSegmentPciCfg2Pei.c
@Ajax Dong Ajax Dong 2 days ago 28 KB Init
/** @file
  PcatSingleSegmentPciCfg2Pei.c - Full decompilation

  Full reverse engineering of the PcatSingleSegmentPciCfg2Pei UEFI PEIM.
  This PEIM installs the EFI_PEI_PCI_CFG2_PPI on a single-segment PCI
  system using PCI Express (ECAM) memory-mapped configuration space access.

  Source: MdeModulePkg/Universal/PcatSingleSegmentPciCfg2Pei/PciCfg2.c
  Build:  VS2015 DEBUG IA32
  PDB:    PcatSingleSegmentPciCfg2Pei.pdb
  GUID:   {057A449A-1FDC-4C06-BFC9-F53F6A99BB92}

  Image layout:
    HEADER  0xffdab714 - 0xffdab974  (0x260)
    .text   0xffdab974 - 0xffdac614  (0xca0)
    .rdata  0xffdac614 - 0xffdac9f4  (0x3e0)
    .data   0xffdac9f4 - 0xffdaca54  (0x60)
    .reloc  0xffdaca54 - 0xffdacad4  (0x80)
**/

#include <Uefi.h>
#include <PiPei.h>
#include <Ppi/PciCfg2.h>
#include <Library/DebugLib.h>
#include <Library/BaseLib.h>
#include <Library/IoLib.h>
#include <Library/PciExpressLib.h>
#include <Library/PeiServicesTablePointerLib.h>

//
// .data section global data
//
// GUID for PCD database PPI lookup
STATIC CONST EFI_GUID mPcdDatabaseGuid = {
  0x36232936, 0x0E76, 0x31C8, { 0xA1, 0x3A, 0x3A, 0xF2, 0xFC, 0x1C, 0x39, 0x32 }
};

// EFI_PEI_PCI_CFG2_PPI GUID: {057A449A-1FDC-4C06-BFC9-F53F6A99BB92}
STATIC CONST EFI_GUID mPcatSingleSegmentPciCfg2PpiGuid = {
  0x057A449A, 0x1FDC, 0x4C06, { 0xBF, 0xC9, 0xF5, 0x3F, 0x6A, 0x99, 0xBB, 0x92 }
};

//
// PRIVATE_DATA - EFI_PEI_PCI_CFG2_PPI instance
//
STATIC EFI_PEI_PCI_CFG2_PPI mPrivateData;

//
// PPI Descriptor
//
STATIC EFI_PEI_PPI_DESCRIPTOR mPpiDescriptor = {
  EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
  &mPcatSingleSegmentPciCfg2PpiGuid,
  &mPrivateData
};

// ============================================================================
// Forward declarations
// ============================================================================

EFI_STATUS
EFIAPI
PciCfg2Read (
  IN  EFI_PEI_SERVICES  **PeiServices,
  IN  VOID              *This,
  IN  UINTN             Width,
  IN  UINT64            Address,
  IN  VOID              *Buffer
  );

EFI_STATUS
EFIAPI
PciCfg2Write (
  IN  EFI_PEI_SERVICES  **PeiServices,
  IN  VOID              *This,
  IN  UINTN             Width,
  IN  UINT64            Address,
  IN  VOID              *Buffer
  );

EFI_STATUS
EFIAPI
PciCfg2Modify (
  IN  EFI_PEI_SERVICES  **PeiServices,
  IN  VOID              *This,
  IN  UINTN             Width,
  IN  UINT64            Address,
  IN  VOID              *SetBits,
  IN  VOID              *ClearBits
  );

// ============================================================================
// Module entry point
// Address: 0xffdaba34 | Size: 0x5b
// ============================================================================

EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS        Status;
  EFI_PEI_SERVICES  **PeiServices;

  //
  // Store PRIVATE_DATA address in SystemTable reserved slot.
  // This effectively associates the private data with ImageHandle.
  // Equivalent to: ImageHandle->SystemTable->Something = &mPrivateData
  //
  *(UINTN *)((UINTN)SystemTable + 100) = (UINTN)&mPrivateData;

  //
  // Get PEI Services Table pointer
  //
  PeiServices = GetPeiServicesTablePtr();

  //
  // Install EFI_PEI_PCI_CFG2_PPI
  //
  Status = (*PeiServices)->InstallPpi (PeiServices, &mPpiDescriptor);

  //
  // ASSERT on failure
  //
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
  }

  return Status;
}

// ============================================================================
// PCI Express address to MMIO offset conversion
// Address: 0xffdaba8f | Size: 0x52
// ============================================================================
//
// The EFI_PEI_PCI_CFG2_PPI Address format packs:
//   Offset 0: Register (bits 7:0) - config register offset
//   Offset 1: Function (bits 2:0)
//   Offset 2: Device (bits 4:0)
//   Offset 3: Bus (bits 7:0)
//   Offset 4-7: ExtendedRegister (32-bit, if non-zero overrides Register)
//
// ECAM MMIO offset = (Bus * 32 + Device) * 8 + Function) * 4096 + Register
//   Bus << 20 | Device << 15 | Function << 12 | Register
//

UINTN
EFIAPI
PciExpressAddressToOffset (
  IN UINT8  *PciAddress
  )
{
  UINTN   Device;
  UINTN   Bus;
  UINTN   Function;
  UINTN   Register;
  UINT32  ExtendedRegister;

  Device            = PciAddress[2] & 0x1F;
  ExtendedRegister  = *(UINT32 *)(PciAddress + 4);

  if (ExtendedRegister != 0) {
    //
    // Extended config space access (>= 256 bytes offset, for ECAM)
    //
    return (ExtendedRegister & 0xFFF)
           | (((PciAddress[1] & 7) | (8 * ((32 * PciAddress[3]) | Device))) << 12);
  } else {
    //
    // Standard config space access (< 256 bytes offset)
    //
    Function  = PciAddress[1] & 7;
    Bus       = PciAddress[3];
    Register = PciAddress[0];

    return Register | (((Function | (Device << 3) | (Bus << 8))) << 12);
  }
}

// ============================================================================
// PCI CFG2 Read
// Address: 0xffdabae1 | Size: 0x1b1
// ============================================================================
//
// Reads from PCI configuration space at the given Address.
// Supports UINT8 (0), UINT16 (1), UINT32 (2), and UINT64 (3) widths.
// Handles misaligned accesses by breaking into byte/word reads as needed.
//

EFI_STATUS
EFIAPI
PciCfg2Read (
  IN  EFI_PEI_SERVICES  **PeiServices,
  IN  VOID              *This,
  IN  UINTN             Width,
  IN  UINT64            Address,
  IN  VOID              *Buffer
  )
{
  UINTN   Offset;

  Offset = PciExpressAddressToOffset ((UINT8 *)&Address);

  switch (Width) {

  case PCI_CFG2_WIDTH_UINT8:
    *(UINT8 *)Buffer = PciExpressRead8 (Offset);
    break;

  case PCI_CFG2_WIDTH_UINT16:
    if (Offset & 1) {
      //
      // Misaligned: read two bytes individually
      //
      ((UINT8 *)Buffer)[0] = PciExpressRead8 (Offset);
      ((UINT8 *)Buffer)[1] = PciExpressRead8 (Offset + 1);
    } else {
      UnalignedWrite16 (Buffer, PciExpressRead16 (Offset));
    }
    break;

  case PCI_CFG2_WIDTH_UINT32:
    if (Offset & 3) {
      if (Offset & 1) {
        //
        // Word-level misalignment: read 4 bytes individually
        //
        ((UINT8 *)Buffer)[0] = PciExpressRead8 (Offset);
        ((UINT8 *)Buffer)[1] = PciExpressRead8 (Offset + 1);
        ((UINT8 *)Buffer)[2] = PciExpressRead8 (Offset + 2);
        ((UINT8 *)Buffer)[3] = PciExpressRead8 (Offset + 3);
      } else {
        //
        // Halfword-aligned but not word-aligned: two 16-bit reads
        //
        UnalignedWrite16 (Buffer, PciExpressRead16 (Offset));
        UnalignedWrite16 ((UINT8 *)Buffer + 2, PciExpressRead16 (Offset + 2));
      }
    } else {
      UnalignedWrite32 (Buffer, PciExpressRead32 (Offset));
    }
    break;

  case PCI_CFG2_WIDTH_UINT64:
    if (Offset & 3) {
      if (Offset & 1) {
        //
        // Byte-level misalignment: read 8 bytes individually
        //
        ((UINT8 *)Buffer)[0] = PciExpressRead8 (Offset);
        ((UINT8 *)Buffer)[1] = PciExpressRead8 (Offset + 1);
        ((UINT8 *)Buffer)[2] = PciExpressRead8 (Offset + 2);
        ((UINT8 *)Buffer)[3] = PciExpressRead8 (Offset + 3);
        ((UINT8 *)Buffer)[4] = PciExpressRead8 (Offset + 4);
        ((UINT8 *)Buffer)[5] = PciExpressRead8 (Offset + 5);
        ((UINT8 *)Buffer)[6] = PciExpressRead8 (Offset + 6);
        ((UINT8 *)Buffer)[7] = PciExpressRead8 (Offset + 7);
      } else {
        //
        // Halfword-aligned: four 16-bit reads
        //
        UnalignedWrite16 (Buffer, PciExpressRead16 (Offset));
        UnalignedWrite16 ((UINT16 *)Buffer + 1, PciExpressRead16 (Offset + 2));
        UnalignedWrite16 ((UINT16 *)Buffer + 2, PciExpressRead16 (Offset + 4));
        UnalignedWrite16 ((UINT16 *)Buffer + 3, PciExpressRead16 (Offset + 6));
      }
    } else {
      //
      // Word-aligned: two 32-bit reads
      //
      UnalignedWrite32 (Buffer, PciExpressRead32 (Offset));
      UnalignedWrite32 ((UINT8 *)Buffer + 4, PciExpressRead32 (Offset + 4));
    }
    break;

  default:
    return EFI_INVALID_PARAMETER;
  }

  return EFI_SUCCESS;
}

// ============================================================================
// PCI CFG2 Write
// Address: 0xffdabc92 | Size: 0x1a5
// ============================================================================
//
// Writes to PCI configuration space at the given Address.
// Supports UINT8 (0), UINT16 (1), UINT32 (2), and UINT64 (3) widths.
//

EFI_STATUS
EFIAPI
PciCfg2Write (
  IN  EFI_PEI_SERVICES  **PeiServices,
  IN  VOID              *This,
  IN  UINTN             Width,
  IN  UINT64            Address,
  IN  VOID              *Buffer
  )
{
  UINTN   Offset;

  Offset = PciExpressAddressToOffset ((UINT8 *)&Address);

  switch (Width) {

  case PCI_CFG2_WIDTH_UINT8:
    PciExpressWrite8 (Offset, *(UINT8 *)Buffer);
    break;

  case PCI_CFG2_WIDTH_UINT16:
    if (Offset & 1) {
      //
      // Misaligned: write as two bytes
      //
      PciExpressWrite8 (Offset, ((UINT8 *)Buffer)[0]);
      PciExpressWrite8 (Offset + 1, ((UINT8 *)Buffer)[1]);
    } else {
      PciExpressWrite16 (Offset, UnalignedRead16 (Buffer));
    }
    break;

  case PCI_CFG2_WIDTH_UINT32:
    if (Offset & 3) {
      if (Offset & 1) {
        //
        // Byte-level misalignment: write 4 bytes individually
        //
        PciExpressWrite8 (Offset, ((UINT8 *)Buffer)[0]);
        PciExpressWrite8 (Offset + 1, ((UINT8 *)Buffer)[1]);
        PciExpressWrite8 (Offset + 2, ((UINT8 *)Buffer)[2]);
        PciExpressWrite8 (Offset + 3, ((UINT8 *)Buffer)[3]);
      } else {
        //
        // Halfword-aligned: two 16-bit writes
        //
        PciExpressWrite16 (Offset, UnalignedRead16 (Buffer));
        PciExpressWrite16 (Offset + 2, UnalignedRead16 ((UINT8 *)Buffer + 2));
      }
    } else {
      PciExpressWrite32 (Offset, UnalignedRead32 (Buffer));
    }
    break;

  case PCI_CFG2_WIDTH_UINT64:
    if (Offset & 3) {
      if (Offset & 1) {
        //
        // Byte-level misalignment: write 8 bytes individually
        //
        PciExpressWrite8 (Offset, ((UINT8 *)Buffer)[0]);
        PciExpressWrite8 (Offset + 1, ((UINT8 *)Buffer)[1]);
        PciExpressWrite8 (Offset + 2, ((UINT8 *)Buffer)[2]);
        PciExpressWrite8 (Offset + 3, ((UINT8 *)Buffer)[3]);
        PciExpressWrite8 (Offset + 4, ((UINT8 *)Buffer)[4]);
        PciExpressWrite8 (Offset + 5, ((UINT8 *)Buffer)[5]);
        PciExpressWrite8 (Offset + 6, ((UINT8 *)Buffer)[6]);
        PciExpressWrite8 (Offset + 7, ((UINT8 *)Buffer)[7]);
      } else {
        //
        // Halfword-aligned: four 16-bit writes
        //
        PciExpressWrite16 (Offset, UnalignedRead16 (Buffer));
        PciExpressWrite16 (Offset + 2, UnalignedRead16 ((UINT8 *)Buffer + 2));
        PciExpressWrite16 (Offset + 4, UnalignedRead16 ((UINT8 *)Buffer + 4));
        PciExpressWrite16 (Offset + 6, UnalignedRead16 ((UINT8 *)Buffer + 6));
      }
    } else {
      //
      // Word-aligned: two 32-bit writes
      //
      PciExpressWrite32 (Offset, UnalignedRead32 (Buffer));
      PciExpressWrite32 (Offset + 4, UnalignedRead32 ((UINT8 *)Buffer + 4));
    }
    break;

  default:
    return EFI_INVALID_PARAMETER;
  }

  return EFI_SUCCESS;
}

// ============================================================================
// PCI CFG2 Modify (Read-Modify-Write)
// Address: 0xffdabe37 | Size: 0x325
// ============================================================================
//
// Performs a read-modify-write on PCI configuration space.
//   new_value = (old_value & ~ClearBits) | SetBits
// Supports UINT8 (0), UINT16 (1), UINT32 (2), and UINT64 (3) widths.
//

EFI_STATUS
EFIAPI
PciCfg2Modify (
  IN  EFI_PEI_SERVICES  **PeiServices,
  IN  VOID              *This,
  IN  UINTN             Width,
  IN  UINT64            Address,
  IN  VOID              *SetBits,
  IN  VOID              *ClearBits
  )
{
  UINTN   Offset;

  Offset = PciExpressAddressToOffset ((UINT8 *)&Address);

  switch (Width) {

  case PCI_CFG2_WIDTH_UINT8:
    PciExpressOr8 (Offset, ~*(UINT8 *)ClearBits, *(UINT8 *)SetBits);
    break;

  case PCI_CFG2_WIDTH_UINT16:
    if (Offset & 1) {
      //
      // Misaligned: two byte-level ORs
      //
      PciExpressOr8 (Offset, ~((UINT8 *)ClearBits)[0], ((UINT8 *)SetBits)[0]);
      PciExpressOr8 (Offset + 1, ~((UINT8 *)ClearBits)[1], ((UINT8 *)SetBits)[1]);
    } else {
      PciExpressOr16 (Offset, ~UnalignedRead16 (ClearBits), UnalignedRead16 (SetBits));
    }
    break;

  case PCI_CFG2_WIDTH_UINT32:
    if (Offset & 3) {
      if (Offset & 1) {
        //
        // Byte-level misalignment: four byte ORs
        //
        PciExpressOr8 (Offset, ~((UINT8 *)ClearBits)[0], ((UINT8 *)SetBits)[0]);
        PciExpressOr8 (Offset + 1, ~((UINT8 *)ClearBits)[1], ((UINT8 *)SetBits)[1]);
        PciExpressOr8 (Offset + 2, ~((UINT8 *)ClearBits)[2], ((UINT8 *)SetBits)[2]);
        PciExpressOr8 (Offset + 3, ~((UINT8 *)ClearBits)[3], ((UINT8 *)SetBits)[3]);
      } else {
        //
        // Halfword-aligned: two 16-bit ORs
        //
        PciExpressOr16 (Offset, ~UnalignedRead16 (ClearBits), UnalignedRead16 (SetBits));
        PciExpressOr16 (Offset + 2, ~UnalignedRead16 ((UINT8 *)ClearBits + 2),
                        UnalignedRead16 ((UINT8 *)SetBits + 2));
      }
    } else {
      PciExpressOr32 (Offset, ~UnalignedRead32 (ClearBits), UnalignedRead32 (SetBits));
    }
    break;

  case PCI_CFG2_WIDTH_UINT64:
    if (Offset & 3) {
      if (Offset & 1) {
        //
        // Byte-level misalignment: eight byte ORs
        //
        PciExpressOr8 (Offset, ~((UINT8 *)ClearBits)[0], ((UINT8 *)SetBits)[0]);
        PciExpressOr8 (Offset + 1, ~((UINT8 *)ClearBits)[1], ((UINT8 *)SetBits)[1]);
        PciExpressOr8 (Offset + 2, ~((UINT8 *)ClearBits)[2], ((UINT8 *)SetBits)[2]);
        PciExpressOr8 (Offset + 3, ~((UINT8 *)ClearBits)[3], ((UINT8 *)SetBits)[3]);
        PciExpressOr8 (Offset + 4, ~((UINT8 *)ClearBits)[4], ((UINT8 *)SetBits)[4]);
        PciExpressOr8 (Offset + 5, ~((UINT8 *)ClearBits)[5], ((UINT8 *)SetBits)[5]);
        PciExpressOr8 (Offset + 6, ~((UINT8 *)ClearBits)[6], ((UINT8 *)SetBits)[6]);
        PciExpressOr8 (Offset + 7, ~((UINT8 *)ClearBits)[7], ((UINT8 *)SetBits)[7]);
      } else {
        //
        // Halfword-aligned: four 16-bit ORs
        //
        PciExpressOr16 (Offset, ~UnalignedRead16 (ClearBits), UnalignedRead16 (SetBits));
        PciExpressOr16 (Offset + 2, ~UnalignedRead16 ((UINT8 *)ClearBits + 2),
                        UnalignedRead16 ((UINT8 *)SetBits + 2));
        PciExpressOr16 (Offset + 4, ~UnalignedRead16 ((UINT8 *)ClearBits + 4),
                        UnalignedRead16 ((UINT8 *)SetBits + 4));
        PciExpressOr16 (Offset + 6, ~UnalignedRead16 ((UINT8 *)ClearBits + 6),
                        UnalignedRead16 ((UINT8 *)SetBits + 6));
      }
    } else {
      //
      // Word-aligned: two 32-bit ORs
      //
      PciExpressOr32 (Offset, ~UnalignedRead32 (ClearBits), UnalignedRead32 (SetBits));
      PciExpressOr32 (Offset + 4, ~UnalignedRead32 ((UINT8 *)ClearBits + 4),
                      UnalignedRead32 ((UINT8 *)SetBits + 4));
    }
    break;

  default:
    return EFI_INVALID_PARAMETER;
  }

  return EFI_SUCCESS;
}

// ============================================================================
// Memory utility functions (library wrappers)
// ============================================================================

VOID *
EFIAPI
SetMem (
  OUT VOID   *Buffer,
  IN  UINTN  Size,
  IN  UINT8  Value
  )
{
  return memset (Buffer, Value, Size);
}

VOID *
EFIAPI
SetMem32 (
  OUT VOID   *Buffer,
  IN  UINTN  Count,
  IN  UINT32 ValueLo,
  IN  UINT32 ValueHi
  )
{
  UINT32  *Buf32 = (UINT32 *)Buffer;

  do {
    Buf32[0] = ValueLo;
    Buf32[1] = ValueHi;
    Buf32 += 2;
  } while (--Count);

  return Buffer;
}

VOID *
EFIAPI
ZeroMem (
  OUT VOID   *Buffer,
  IN  UINTN  Size
  )
{
  return memset (Buffer, 0, Size);
}

VOID *
EFIAPI
CopyMem (
  OUT VOID       *Destination,
  IN  CONST VOID *Source,
  IN  UINTN      Size
  )
{
  //
  // If source < destination and ranges overlap (backward copy case):
  //   reverse copy from end
  //
  if ((UINTN)Source < (UINTN)Destination &&
      (UINTN)Source + Size - 1 >= (UINTN)Destination) {

    //
    // Copy backwards from the end, one byte at a time
    //
    return memmove (Destination, Source, Size);
  }

  //
  // Forward copy: start with aligned 32-bit chunks, then byte remainder
  //
  Size = (Size & 3);  // remainder after 32-bit copies
  // ... (the rest falls through to byte-by-byte qmemcpy of remainder)
  // In the actual binary, the remainder copy uses qmemcpy for remainder of size % 4.

  return memcpy (Destination, Source, ((UINTN)Size + 3) & ~3);
}

// ============================================================================
// PEI Services Table pointer retrieval (via IDTR)
// Address: 0xffdac3a8 | Size: 0x32
// ============================================================================

EFI_PEI_SERVICES **
EFIAPI
GetPeiServicesTablePtr (
  VOID
  )
{
  IA32_IDTR   Idtr;
  EFI_PEI_SERVICES  **PeiServices;

  //
  // Read IDTR to find the base of the IDT
  //
  ReadIdtr (&Idtr);

  //
  // The PEI Services Table pointer is stored at offset -4 from the IDT base.
  // This is a standard PEI Services Table Pointer Library mechanism for IA32.
  //
  PeiServices = *(EFI_PEI_SERVICES ***)(Idtr.Base - 4);

  if (PeiServices == NULL) {
    DEBUG ((EFI_D_ERROR, "PeiServices is NULL\n"));
  }

  return PeiServices;
}

// ============================================================================
// Read IDTR register (SIDT instruction wrapper)
// Address: 0xffdac3da | Size: 0x23
// ============================================================================

VOID
EFIAPI
ReadIdtr (
  OUT IA32_IDTR  *IdtrBuffer
  )
{
  ASSERT (IdtrBuffer != NULL);
  __sidt (IdtrBuffer);
}

// ============================================================================
// PCD Database access
// Address: 0xffdac15c | Size: 0x31
// ============================================================================
//
// Retrieves the PCD database pointer by locating the PCD PPI via
// PEI Services' LocatePpi.
//

VOID *
EFIAPI
GetPcdDatabasePtr (
  VOID
  )
{
  EFI_PEI_SERVICES  **PeiServices;
  VOID              *PcdDatabase;
  EFI_STATUS        Status;

  PeiServices = GetPeiServicesTablePtr ();

  Status = (*PeiServices)->LocatePpi (
                             PeiServices,
                             &mPcdDatabaseGuid,
                             0,
                             NULL,
                             &PcdDatabase
                             );
  if (!EFI_ERROR (Status)) {
    return PcdDatabase;
  }

  return NULL;
}

// ============================================================================
// Get PCD database pointer via PEI PCD PPI Locate
// Address: 0xffdac5b5 | Size: 0x58
// ============================================================================
//
// This function locates the PCD PPI (not PCD database directly) and
// returns the PPI interface. It is used to get PcdPpi, which is then
// used to call PcdGetX functions.
//

VOID *
EFIAPI
PeiPcdLocate (
  VOID
  )
{
  EFI_PEI_SERVICES  **PeiServices;
  VOID              *PcdPpi;
  EFI_STATUS        Status;

  PeiServices = GetPeiServicesTablePtr ();

  //
  // LocatePpi to get the PCD PPI interface
  //
  Status = (*PeiServices)->LocatePpi (
                             PeiServices,
                             &mPcdDatabaseGuid,  // Actually uses gPeiPcdPpiGuid
                             0,
                             NULL,
                             &PcdPpi
                             );
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
  }

  return PcdPpi;
}

// ============================================================================
// PCI Express base address retrieval (from PCD)
// Address: 0xffdac3fd | Size: 0xc
// ============================================================================
//
// Gets the PciExpressBaseAddress from PCD. Uses the PCD PPI to call
// PcdGet64/PcdGet32(token=5) which returns the MMIO base address of
// the PCI Express configuration space (ECAM).
//

UINTN
EFIAPI
GetPciExpressBaseAddr (
  VOID
  )
{
  VOID    *PcdPpi;
  UINTN   (**PcdGetPtr)(UINTN);

  PcdPpi      = PeiPcdLocate ();
  PcdGetPtr   = (UINTN (**)(UINTN))PcdPpi;

  //
  // Call PcdGetPtr(token_value=5) to retrieve PcdPciExpressBaseAddress.
  // The token value 5 corresponds to fixed-at-build PCD
  // PcdPciExpressBaseAddress in typical EDK2 builds.
  //
  return PcdGetPtr[4] (5);
}

// ============================================================================
// PCI Express MMIO config space access - Read8
// Address: 0xffdac409 | Size: 0x30
// ============================================================================

UINT8
EFIAPI
PciExpressRead8 (
  IN UINTN  Address
  )
{
  //
  // Validate address: must have bits [31:28] clear ( < 256MB offset )
  //
  if ((Address & 0xF0000000) != 0) {
    ASSERT (Address <= 0x0FFFFFFF);
  }

  return *(volatile UINT8 *)(GetPciExpressBaseAddr () + Address);
}

// ============================================================================
// PCI Express MMIO config space access - Write8
// Address: 0xffdac439 | Size: 0x39
// ============================================================================

UINT8
EFIAPI
PciExpressWrite8 (
  IN UINTN  Address,
  IN UINT8  Value
  )
{
  if ((Address & 0xF0000000) != 0) {
    ASSERT (Address <= 0x0FFFFFFF);
  }

  *(volatile UINT8 *)(GetPciExpressBaseAddr () + Address) = Value;
  return Value;
}

// ============================================================================
// PCI Express MMIO config space access - Read16
// Address: 0xffdac472 | Size: 0x38
// ============================================================================

UINT16
EFIAPI
PciExpressRead16 (
  IN UINTN  Address
  )
{
  if ((Address & 0xF0000000) != 0) {
    ASSERT (Address <= 0x0FFFFFFF);
  }

  return AlignedRead16 ((UINT16 *)(GetPciExpressBaseAddr () + Address));
}

// ============================================================================
// PCI Express MMIO config space access - Write16
// Address: 0xffdac4aa | Size: 0x3e
// ============================================================================

UINT16
EFIAPI
PciExpressWrite16 (
  IN UINTN   Address,
  IN UINT16  Value
  )
{
  if ((Address & 0xF0000000) != 0) {
    ASSERT (Address <= 0x0FFFFFFF);
  }

  return AlignedWrite16 ((UINT16 *)(GetPciExpressBaseAddr () + Address), Value);
}

// ============================================================================
// PCI Express MMIO config space access - Read32
// Address: 0xffdac4e8 | Size: 0x33
// ============================================================================

UINT32
EFIAPI
PciExpressRead32 (
  IN UINTN  Address
  )
{
  if ((Address & 0xF0000000) != 0) {
    ASSERT (Address <= 0x0FFFFFFF);
  }

  return *(volatile UINT32 *)(GetPciExpressBaseAddr () + Address);
}

// ============================================================================
// PCI Express MMIO config space access - Write32
// Address: 0xffdac51b | Size: 0x39
// ============================================================================

UINT32
EFIAPI
PciExpressWrite32 (
  IN UINTN   Address,
  IN UINT32  Value
  )
{
  if ((Address & 0xF0000000) != 0) {
    ASSERT (Address <= 0x0FFFFFFF);
  }

  *(volatile UINT32 *)(GetPciExpressBaseAddr () + Address) = Value;
  return Value;
}

// ============================================================================
// PCI Express Atomic OR - 8-bit
// Address: 0xffdac283 | Size: 0x42
// ============================================================================

UINT8
EFIAPI
PciExpressOr8 (
  IN UINTN  Address,
  IN UINT8  OrData,
  IN UINT8  AndData
  )
{
  UINT8   Value;

  if ((Address & 0xF0000000) != 0) {
    ASSERT (Address <= 0x0FFFFFFF);
  }

  Value = *(volatile UINT8 *)(GetPciExpressBaseAddr () + Address);
  Value = (Value & AndData) | OrData;
  *(volatile UINT8 *)(GetPciExpressBaseAddr () + Address) = Value;

  return Value;
}

// ============================================================================
// PCI Express Atomic OR - 16-bit
// Address: 0xffdac2c5 | Size: 0x52
// ============================================================================

UINT16
EFIAPI
PciExpressOr16 (
  IN UINTN   Address,
  IN UINT16  OrData,
  IN UINT16  AndData
  )
{
  UINT16  *AddrPtr;

  if ((Address & 0xF0000000) != 0) {
    ASSERT (Address <= 0x0FFFFFFF);
  }

  AddrPtr = (UINT16 *)(GetPciExpressBaseAddr () + Address);

  return AlignedWrite16 (
           AddrPtr,
           (AlignedRead16 (AddrPtr) & AndData) | OrData
           );
}

// ============================================================================
// PCI Express Atomic OR - 32-bit
// Address: 0xffdac317 | Size: 0x42
// ============================================================================

UINT32
EFIAPI
PciExpressOr32 (
  IN UINTN   Address,
  IN UINT32  OrData,
  IN UINT32  AndData
  )
{
  UINT32  Value;

  if ((Address & 0xF0000000) != 0) {
    ASSERT (Address <= 0x0FFFFFFF);
  }

  Value = *(volatile UINT32 *)(GetPciExpressBaseAddr () + Address);
  Value = (Value & AndData) | OrData;
  *(volatile UINT32 *)(GetPciExpressBaseAddr () + Address) = Value;

  return Value;
}

// ============================================================================
// Unaligned memory access helpers
// ============================================================================

UINT16
EFIAPI
UnalignedRead16 (
  IN VOID  *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *(UINT16 *)Buffer;
}

UINT16
EFIAPI
UnalignedWrite16 (
  OUT VOID   *Buffer,
  IN  UINT16 Value
  )
{
  ASSERT (Buffer != NULL);
  *(UINT16 *)Buffer = Value;
  return Value;
}

UINT32
EFIAPI
UnalignedRead32 (
  IN VOID  *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *(UINT32 *)Buffer;
}

UINT32
EFIAPI
UnalignedWrite32 (
  OUT VOID   *Buffer,
  IN  UINT32 Value
  )
{
  ASSERT (Buffer != NULL);
  *(UINT32 *)Buffer = Value;
  return Value;
}

// ============================================================================
// Aligned memory access helpers
// ============================================================================

UINT16
EFIAPI
AlignedRead16 (
  IN UINT16  *Address
  )
{
  ASSERT (((UINTN)Address & 1) == 0);
  return *Address;
}

UINT16
EFIAPI
AlignedWrite16 (
  OUT UINT16  *Address,
  IN  UINT16  Value
  )
{
  ASSERT (((UINTN)Address & 1) == 0);
  *Address = Value;
  return Value;
}

// ============================================================================
// Platform error status (CMOS-based)
// Address: 0xffdac359 | Size: 0x4f
// ============================================================================
//
// Reads the platform error status from CMOS register 0x4A.
// This is used by the ASSERT infrastructure to decide whether to
// break or halt on assertion failure.
//
// Returns:
//   0               - No error
//   0xFFFFFFFF      - Error status cleared
//   -2147483644     - EFI_DEVICE_ERROR (recoverable)
//   -2147483578     - EFI_DEVICE_ERROR (non-recoverable)
//

UINTN
EFIAPI
GetPlatformErrorStatus (
  VOID
  )
{
  UINT8   Status;

  //
  // Read current CMOS index register (0x70), preserving bit 7 (NMI disable)
  //
  Status = IoRead8 (0x70);
  IoWrite8 (0x70, (Status & 0x80) | 0x4A);
  Status = IoRead8 (0x71);

  if (Status > 3) {
    if (Status == 0) {
      //
      // Check a hardware register (at fixed address 0xFDAF0490) for
      // additional error info
      //
      Status = (*(volatile UINT8 *)0xFDAF0490 & 2) | 1;
    }
  }

  //
  // Only Status values 1 and 2 are used:
  // 1 -> EFI_DEVICE_ERROR (recoverable)
  // 2 -> more severe error
  //
  switch (Status) {
  case 0:
    return 0;

  case 1:
    return (UINTN)EFI_DEVICE_ERROR;    // -2147483644

  default:
    if (Status != 0xFF) {
      return (UINTN)(0x80000000 | 0x00010046);  // -2147483578
    }
    return 0;  // Status was 0xFF (cleared)
  }
}

// ============================================================================
// Debug/assert infrastructure
// ============================================================================
//
// These functions use the PCD database to call into the DebugLib
// implementation installed as a PPI. The PCD database pointer at
// offset +4 contains the assertion/debug message handler function.
//
// Note: This is the old-style EDK2 DEBUG/ASSERT infrastructure
// before the standardized DebugLib was widely used.

VOID
EFIAPI
ReportAssert (
  IN UINTN        Flags,
  IN CONST CHAR8  *Format,
  ...
  )
{
  VOID    *PcdDatabase;
  UINTN   ErrorStatus;
  VA_LIST Marker;

  VA_START (Marker, Format);

  PcdDatabase = GetPcdDatabasePtr ();
  if (PcdDatabase != NULL) {
    ErrorStatus = GetPlatformErrorStatus ();
    if ((ErrorStatus & Flags) != 0) {
      //
      // Call the ASSERT handler from PCD database (at offset +4)
      //
      ((VOID (*)(UINTN, CONST CHAR8 *, VA_LIST))(
        *(UINTN *)((UINTN)PcdDatabase + 4)
        ))(Flags, Format, Marker);
    }
  }

  VA_END (Marker);
}

VOID
EFIAPI
ReportDebugMsg (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Message
  )
{
  VOID    *PcdDatabase;

  PcdDatabase = GetPcdDatabasePtr ();
  if (PcdDatabase != NULL) {
    //
    // Call the DEBUG handler from PCD database (at offset +4)
    //
    ((VOID (*)(CONST CHAR8 *, UINTN, CONST CHAR8 *))(
      *(UINTN *)((UINTN)PcdDatabase + 4)
      ))(FileName, LineNumber, Message);
  }
}