Newer
Older
AMI-Aptio-BIOS-Reversed / OemReadyToBootDxe / OemReadyToBootDxe.c
@Ajax Dong Ajax Dong 2 days ago 9 KB Init
/** @file
  OemReadyToBootDxe - OEM ReadyToBoot Event Handler

  This module registers a ReadyToBoot event callback that scans IIO NVMe
  ports on both CPU sockets and clears the power fault status.

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

  Binary: HR650X BIOS / 0102_OemReadyToBootDxe_fcf4696c02b0
**/

#include "OemReadyToBootDxe.h"

//
// Global protocol pointers
//
EFI_BOOT_SERVICES   *gBS = NULL;
EFI_RUNTIME_SERVICES *gRT = NULL;
EFI_SYSTEM_TABLE    *gST = NULL;
EFI_HANDLE           gImageHandle = NULL;

UINT64  gPciExpressBaseAddress = 0;
VOID   *gHobList = NULL;
VOID   *gIioUdsProtocol = NULL;
VOID   *gPcdProtocol = NULL;
VOID   *gCpuCsrAccess = NULL;
UINT8   gDebugPortValue = 0;

//
// GUID definitions (referenced in .rdata)
//
extern EFI_GUID  gEfiIioUdsProtocolGuid;
extern EFI_GUID  gEfiCpuCsrAccessGuid;
extern EFI_GUID  gEfiPcdProtocolGuid;
extern EFI_GUID  gEfiHobListGuid;

/**
  Read an unaligned 64-bit value from a buffer.

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

  @return The 64-bit value read from the buffer.
**/
UINT64
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  if (Buffer == NULL) {
    DebugAssert (
      __FILE__, 192,
      "Buffer != ((void *) 0)"
      );
  }
  return *(UINT64 *)Buffer;
}

/**
  Check whether two GUIDs match by comparing their first and second
  64-bit halves.

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

  @retval TRUE   The GUIDs match.
  @retval FALSE  The GUIDs do not match.
**/
BOOLEAN
IsHobGuidMatching (
  IN EFI_GUID  *Guid1,
  IN EFI_GUID  *Guid2
  )
{
  UINT64  Data1;
  UINT64  Data2;

  if ((Guid1 == NULL) || (Guid2 == NULL)) {
    return FALSE;
  }

  Data1 = ReadUnaligned64 (Guid1);
  Data2 = ReadUnaligned64 (Guid2);

  return (BOOLEAN)(Data1 == Data2);
}

/**
  Retrieve the HOB list pointer.

  Locates the HOB list from the system table configuration table and
  caches it in gHobList.

  @return Pointer to the HOB list, or NULL if not found.
**/
EFI_HOB_HANDOFF_INFO_TABLE *
GetHobList (
  VOID
  )
{
  EFI_STATUS                 Status;
  UINTN                      Index;
  EFI_CONFIGURATION_TABLE    *ConfigTable;
  EFI_GUID                   mHobListGuid = { 0x7739F24C, 0x1E93, 0x4D66, { 0xB4, 0x90, 0x9B, 0xDB, 0xEB, 0xE1, 0xEE, 0x55 } };

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

  gHobList = NULL;

  if (gST->NumberOfTableEntries != 0) {
    ConfigTable = (EFI_CONFIGURATION_TABLE *)(gST->ConfigurationTable);
    for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
      if (IsHobGuidMatching (&mHobListGuid, &ConfigTable->VendorGuid)) {
        gHobList = (VOID *)(UINTN)ConfigTable->VendorTable;
        break;
      }
      ConfigTable++;
    }
  }

  if (gHobList == NULL) {
    DebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND);
    DebugAssert (__FILE__, 54, "!EFI_ERROR (Status)");
  }

  if (gHobList == NULL) {
    DebugAssert (__FILE__, 55, "mHobList != ((void *) 0)");
  }

  return gHobList;
}

/**
  Locate the PCD protocol.

  Locates gEfiPcdProtocolGuid and caches it in gPcdProtocol.

  @return Pointer to the PCD protocol interface, or NULL.
**/
VOID *
GetPcdProtocol (
  VOID
  )
{
  EFI_STATUS  Status;

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

  Status = gBS->LocateProtocol (
                  &gEfiPcdProtocolGuid,
                  NULL,
                  &gPcdProtocol
                  );
  if (EFI_ERROR (Status)) {
    DebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
    DebugAssert (__FILE__, 78, "!EFI_ERROR (Status)");
  }

  if (gPcdProtocol == NULL) {
    DebugAssert (__FILE__, 79, "mPcd != ((void *) 0)");
  }

  return gPcdProtocol;
}

/**
  Translate a PCI Express register address to a memory-mapped address.

  Validates that the address fits within the PCIe MMIO window
  (upper 4 bits of the 32-bit address must be clear), then adds the
  cached PCIe base address.

  @param[in]  Address  PCI Express register address (bus/device/function/offset).

  @return The full memory-mapped PCIe address.
**/
UINT64
PciExpressRead (
  IN UINT64  Address
  )
{
  if ((Address & 0xFFFFFFFFF0000000ULL) != 0) {
    DebugAssert (
      __FILE__, 118,
      "((Address) & ~0xfffffff) == 0"
      );
  }

  return gPciExpressBaseAddress + Address;
}

/**
  Scan IIO NVMe ports on a given socket and clear the power fault status.

  For each of the 4 NVMe ports on the specified socket, reads the
  port status register and, if the power fault bit (bit 1) is set,
  writes it back to clear the fault.

  @param[in]  Socket  The IIO socket number (byte index into CPUBUSNO).
**/
VOID
IioScanNvmePort (
  IN UINT8  Socket
  )
{
  UINT8   Port;
  UINT32  NvmePortValue;
  UINT32  BaseBus;

  Port = 0;
  BaseBus = Socket;

  do {
    //
    // Calculate the PCIe address: offset 0xAA in the NVMe port's
    // PCI configuration space.
    //
    NvmePortValue = *(volatile UINT32 *)PciExpressRead (
                                          ((UINT32)(Socket << 5) | (Port & 0x1F)) << 15 | 0xAA
                                          );
    DebugPrint (64, "IioScanNvmePort:%x,%x, %x\n", BaseBus, Port, NvmePortValue);

    if ((NvmePortValue & 0x2) != 0) {
      //
      // Clear the power fault status (bit 1 = write-1-to-clear)
      //
      *(volatile UINT32 *)PciExpressRead (
                            ((UINT32)(Socket << 5) | (Port & 0x1F)) << 15 | 0xAA
                            ) = NvmePortValue | 0x2;
    }

    Port++;
  } while (Port < 4);
}

/**
  Clear NVMe power fault status on all sockets.

  Locates the IIO UDS and CPU CSR access protocols, then reads the
  CPUBUSNO for each socket to determine the bus number.  Calls
  IioScanNvmePort for each socket that has NVMe ports.

  For Socket 0: always present.
  For Socket 1: only present if bit 1 of byte at (IioUds + 2067) is set.
**/
VOID
ClearNvmePowerFaultStatus (
  VOID
  )
{
  EFI_STATUS  Status;
  UINT32      CpuBusNo;
  UINT32      Socket1CpuBusNo;
  VOID        *CpuCsrAccess;
  VOID        *IioUds;

  DebugPrint (64, "ClearNvmePowerFaultStatus\n");

  //
  // Locate IIO UDS protocol
  //
  Status = gBS->LocateProtocol (
                  &gEfiIioUdsProtocolGuid,
                  NULL,
                  &IioUds
                  );
  if (EFI_ERROR (Status)) {
    DebugPrint (0x80000000, "Locate gEfiIioUdsProtocolGuid FAILED\n");
    return;
  }

  //
  // Locate CPU CSR access protocol
  //
  Status = gBS->LocateProtocol (
                  &gEfiCpuCsrAccessGuid,
                  NULL,
                  &CpuCsrAccess
                  );
  if (EFI_ERROR (Status)) {
    DebugPrint (0x80000000, "Locate EfiCpuCsrAccessGuid FAILED %r\n", Status);
    return;
  }

  //
  // Read CPUBUSNO for Socket 0 (SocketId=0, StackId=0, Register=0x13023A0C)
  //
  CpuBusNo = ((UINT32 (*)(UINT8, UINT8, UINT32))(CpuCsrAccess)) (0, 0, 318914764);
  DebugPrint (64, "Socket: 0, CPUBUSNO: %08x\n", CpuBusNo);

  IioScanNvmePort ((UINT8)(CpuBusNo >> 8));
  IioScanNvmePort ((UINT8)(CpuBusNo >> 16));
  IioScanNvmePort ((UINT8)(CpuBusNo >> 24));

  //
  // Check if Socket 1 is present (bit 1 in byte 2067 of IIO UDS)
  //
  if (((*(UINT8 *)(*(UINT64 *)IioUds + 2067)) & 0x2) != 0) {
    Socket1CpuBusNo = ((UINT32 (*)(UINT8, UINT8, UINT32))(CpuCsrAccess)) (1, 0, 318914764);
    DebugPrint (64, "Socket: 1, CPUBUSNO: %08x\n", Socket1CpuBusNo);

    IioScanNvmePort ((UINT8)(Socket1CpuBusNo >> 8));
    IioScanNvmePort ((UINT8)(Socket1CpuBusNo >> 16));
    IioScanNvmePort ((UINT8)(Socket1CpuBusNo >> 24));
  }
}

/**
  The ReadyToBoot callback.

  Called when the ReadyToBoot event is signaled, which occurs just before
  the BDS phase hands off to the OS boot loader.

  This function calls ClearNvmePowerFaultStatus to ensure all NVMe
  power fault indicators are cleared before OS boot.

  @param[in]  Event     The ReadyToBoot event.
  @param[in]  Context   Not used (must be NULL).

  @return EFI_STATUS code (always EFI_SUCCESS in practice).
**/
EFI_STATUS
EFIAPI
OemReadyToBootDxeEntry (
  VOID
  )
{
  EFI_STATUS  Status;

  DebugPrint (64, "OemReadyToBootDxeEntry...Start\n");

  Status = EFI_INVALID_PARAMETER;

  //
  // Register the NVMe power fault clearing routine with the
  // ReadyToBoot event group.
  //
  Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_CALLBACK,
                  ClearNvmePowerFaultStatus,
                  NULL,
                  &gEfiEventReadyToBootGuid,
                  &gReadyToBootEvent
                  );

  if (EFI_ERROR (Status)) {
    DebugPrint (0x80000000, "OemReadyToBootDxeEntry:%r...End\n", Status);
  } else {
    DebugPrint (64, "OemReadyToBootDxeEntry:%r...End\n", Status);
  }

  return Status;
}

/**
  User driver entry point.

  Standard UEFI DXE driver entry point.  Initializes global variables,
  caches protocols, gets the PciExpressBaseAddress from PCD, and calls
  OemReadyToBootDxeEntry().

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

  @retval EFI_SUCCESS         The entry point executed successfully.
  @retval Others              An error occurred.
**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  VOID        *PcdProtocol;

  //
  // Initialize UEFI global variables (UefiBootServicesTableLib)
  //
  gImageHandle = ImageHandle;
  gST          = SystemTable;
  gBS          = SystemTable->BootServices;
  gRT          = SystemTable->RuntimeServices;

  //
  // Cache the HOB list
  //
  GetHobList ();

  //
  // Get the PCD protocol to read PciExpressBaseAddress
  //
  PcdProtocol = GetPcdProtocol ();
  gPciExpressBaseAddress = ((UINT64 (*)(UINTN))(*(UINT64 **)PcdProtocol)[4])(5);

  //
  // Register the ReadyToBoot callback
  //
  Status = OemReadyToBootDxeEntry ();
  return Status;
}