Newer
Older
AMI-Aptio-BIOS-Reversed / MdeModulePkg / Core / Pei / PeiMain / CPUInfo / CPUInfo.c
@Ajax Dong Ajax Dong 2 days ago 16 KB Restructure the repo
/** @file
  PeiCore - CPU Information PEI Module

  This PEIM provides CPU information services during the PEI phase.
  It displays POST progress, detects CPU presence via CMOS, and
  provides CPUID-based topology information.

  Source: e:\hs\MdeModulePkg\Core\Pei\PeiMain\PeiMain.c
          e:\hs\MdeModulePkg\Core\Pei\Dispatcher\Dispatcher.c
          e:\hs\MdeModulePkg\Core\Pei\FwVol\FwVol.c
          e:\hs\MdeModulePkg\Core\Pei\Ppi\Ppi.c
          e:\hs\MdeModulePkg\Core\Pei\Image\Image.c
          e:\hs\MdeModulePkg\Core\Pei\Memory\MemoryServices.c
**/

#include <PiPei.h>
#include <Library/PeiServicesTablePointerLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/PeiServicesLib.h>
#include <Library/HobLib.h>
#include <Library/BaseMemoryLib.h>

//
// EFI_STATUS codes used in GetCpuPresenceStatus
//
#define CPU_PRESENT_SINGLE  0xFFFFFFFF    // EFI_SUCCESS? or all bits set
#define CPU_PRESENT_MULTI   0xFFFFFFFF ^ 0x40  // approximate
#define CPU_NOT_PRESENT     0
#define CPU_ERROR_STATUS    0x80000000

//
// VGA constants
//
#define VGA_TEXT_MEMORY     ((UINT16*)0xB8000)
#define VGA_CRTC_ADDR_PORT  0x3D4
#define VGA_CRTC_DATA_PORT  0x3D5
#define VGA_CURSOR_START    0x0A
#define VGA_CURSOR_SCANLINE 0x20
#define VGA_COLS            80
#define VGA_ROWS            25
#define VGA_ATTRIBUTE       7

//
// CMOS constants
//
#define RTC_ADDRESS_PORT    0x70
#define RTC_DATA_PORT       0x71
#define CMOS_CPU_PRESENCE   0x4A
#define NMI_DISABLE_BIT     0x80

//
// CPUID constants
//
#define CPUID_EXTENDED_TOPOLOGY  0x0B

//
// Progress display table entry:  { x, y }
// Terminated with x == 0xFF / -1
//
#pragma pack(1)
typedef struct {
  UINT8   X;
  UINT8   Y;
} PROGRESS_POSITION;
#pragma pack()

//
// Forward declarations
//
EFI_STATUS
EFIAPI
CpuInfoEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  );

//
// Helper function prototypes
//
EFI_STATUS
GetPeiServices (
  VOID
  );

INTN
CpuInfoDebugPrint (
  IN UINTN  ErrorLevel,
  IN CHAR8  *Format,
  ...
  );

CHAR8
VgaPrintStringAt (
  IN UINT8   X,
  IN UINT8   Y,
  IN CHAR8   *String,
  ...
  );

UINT32
AsmCpuid (
  IN     UINT32  Index,
  OUT    UINT32  *Eax   OPTIONAL,
  OUT    UINT32  *Ebx   OPTIONAL,
  OUT    UINT32  *Ecx   OPTIONAL,
  OUT    UINT32  *Edx   OPTIONAL
  );

UINT32
CpuId (
  OUT UINT32  *Result OPTIONAL
  );

UINTN
AsciiStrLen (
  IN CHAR8  *String
  );

VOID
ReadIdtr (
  OUT IA32_DESCRIPTOR  *Idtr
  );

VOID
CpuInfoReportAssertion (
  IN CHAR8   *FileName,
  IN UINTN   LineNumber,
  IN CHAR8   *Description
  );

VOID
CpuDeadLoop (
  VOID
  );

//========================================================================
//  Module Entry Point
//========================================================================

/**
  Main entry point for CPU Information PEIM.

  @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 executed successfully.
**/
EFI_STATUS
EFIAPI
CpuInfoEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  UINT8   X;
  UINT8   Y;
  UINTN   Index;
  UINT8   *TablePos;
  PROGRESS_POSITION *ProgressTable;

  //
  // Display initial POST progress: 0%
  //
  VgaPrintStringAt (0, 9, "== Progress :   0% ==");

  //
  // Iterate the progress-position table and display
  // "System initializing, please wait..." messages
  // at each position until terminator (-1) is found.
  //
  // The table is an array of PROGRESS_POSITION {X,Y}
  // entries at byte_FFE83F24.
  //
  ProgressTable = (PROGRESS_POSITION*)(UINTN)0xFFE83F24;
  if (ProgressTable[0].X != 0xFF) {
    Index = 0;
    TablePos = (UINT8*)&ProgressTable[0];
    do {
      X = ProgressTable[Index].X;
      Y = ProgressTable[Index].Y;
      //
      // Skip entry (50,y) when y is in [11..21] — these are
      // the "already displayed" slots.
      //
      if (X != 50 || Y < 11 || Y > 21) {
        VgaPrintStringAt (X, Y, &gSystemInitializing[Index * 82]);
      }
      Index++;
      TablePos = (UINT8*)&ProgressTable[Index];
    } while (ProgressTable[Index].X != 0xFF);
  }

  //
  // Install PPI or propagate descriptor to next PEIM
  //
  // NOTE: The original code calls through the PEI Services
  //       table at offset +36 (sizeof(EFI_PEI_SERVICES) + 36)
  //       with unk_FFE83F14 as the parameter. This could be
  //       InstallPpi, or a callback via SystemTable-> ?
  //       In the PEIM case, this likely dispatches a notification
  //       or installs a PPI.
  //

  return EFI_SUCCESS;
}


//========================================================================
//  VGA Print Support
//========================================================================

/**
  Writes an ASCII string to the VGA text buffer at the specified position
  (row X, column Y) with attribute 7 (white on black).

  @param[in] X       VGA column (0-based)
  @param[in] Y       VGA row (0-based)
  @param[in] String  The ASCII string to print
  @param[in] ...     Additional arguments (ignored in this implementation)

  @return The last character written.
**/
CHAR8
VgaPrintStringAt (
  IN UINT8   X,
  IN UINT8   Y,
  IN CHAR8   *String,
  ...
  )
{
  UINTN   Length;
  UINT16  *VgaPtr;
  UINTN   Index;

  Length = AsciiStrLen (String);

  //
  // Calculate VGA buffer offset: 2 bytes per cell
  // VGA memory starts at 0xB8000 (753664 in decimal)
  //
  VgaPtr = VGA_TEXT_MEMORY + (X + Y * 80);

  //
  // Hide cursor by setting cursor position to scanline 32
  //
  IoWrite8 (VGA_CRTC_ADDR_PORT, VGA_CURSOR_START);
  IoWrite8 (VGA_CRTC_DATA_PORT, VGA_CURSOR_SCANLINE);

  //
  // Write string characters with attribute 7
  //
  for (Index = 0; Index < Length; Index++) {
    *VgaPtr = (UINT16)String[Index];
    VgaPtr++;
  }

  //
  // Set attribute of the last character (foreground)
  //
  *(VgaPtr - 1) = (UINT16)((String[Length - 1]) | (VGA_ATTRIBUTE << 8));

  return String[Length - 1];
}


//========================================================================
//  CPU Presence Detection
//========================================================================

/**
  Detects CPU presence by reading CMOS register 0x4A.

  This function probes CMOS NVRAM location 0x4A (CPU presence
  register) to determine:
   - 0 = No CPU present (or single socket unpopulated)
   - 1 = Single CPU present
   - Other = Multi-CPU or error

  @retval 0               No CPU present / socket empty
  @retval 0xFFFFFFFF      Single CPU present (EFI_INVALID_PARAMETER-like status)
  @retval 0x80000000+     Multiple CPUs present
**/
INTN
GetCpuPresenceStatus (
  VOID
  )
{
  UINT8   OriginalReg;
  UINT8   CmosValue;

  //
  // Read current RTC address register, preserve NMI mask (bit 7),
  // select CMOS index 0x4A (CPU presence)
  //
  OriginalReg = IoRead8 (RTC_ADDRESS_PORT);
  IoWrite8 (RTC_ADDRESS_PORT, (OriginalReg & NMI_DISABLE_BIT) | CMOS_CPU_PRESENCE);

  //
  // Read CPU presence value from CMOS data register
  //
  CmosValue = IoRead8 (RTC_DATA_PORT);

  //
  // Interpret the value:
  //   <= 3 : direct presence value
  //   == 0 : no CPU (or fall through to MMIO probe)
  //   == 1 : single CPU
  //   == -1 / 0xFF : invalid/empty
  //   > 3 : check if zero (possible memory-mapped IO at 0xFDAF0490)
  //
  if (CmosValue > 3) {
    if (CmosValue == 0) {
      //
      // Read GPIO/MMIO register at fixed address for CPU presence
      // bit 1 set = CPU present
      //
      CmosValue = (*(volatile UINT8*)0xFDAF0490 & 2) | 1;
    }
  }

  if (CmosValue == 0) {
    return 0;       // No CPU
  }

  //
  // 0xFF means not valid/empty
  //
  if (CmosValue == 0xFF) {
    return 0;       // Empty socket
  }

  if (CmosValue == 1) {
    return CPU_PRESENT_SINGLE;      // 0xFFFFFFFF
  }

  return CPU_ERROR_STATUS;          // 0x80000000 (-2147483648)
}


//========================================================================
//  PEI Services Access
//========================================================================

/**
  Retrieves PEI Services pointer via the IDTR-based PEI Services
  Table Pointer mechanism.

  @return Pointer to EFI_PEI_SERVICES.
**/
EFI_PEI_SERVICES**
GetPeiServicesFromIdtr (
  VOID
  )
{
  IA32_DESCRIPTOR  Idtr;
  UINTN            PeiServices;

  ReadIdtr (&Idtr);

  //
  // The PEI Services pointer is stored at the DWORD immediately
  // before the IDT base address.
  //
  PeiServices = *(UINTN*)(*(UINTN*)&Idtr.Base - sizeof (UINTN));

  if (PeiServices == 0) {
    CpuInfoReportAssertion (
      __FILE__,
      __LINE__,
      "PeiServices != ((void *) 0)"
      );
  }

  return (EFI_PEI_SERVICES**)PeiServices;
}


/**
  Returns PEI Services table pointer from the IDTR-based mechanism,
  then calls the LocatePpi (or similar) service to find a PPI GUID.

  @return The PPI instance pointer (or NULL if not found).
**/
EFI_STATUS
GetPeiServices (
  VOID
  )
{
  EFI_PEI_SERVICES  **PeiServices;
  UINTN             PpiInstance;
  UINTN             SectionInstance;

  PeiServices = GetPeiServicesFromIdtr ();

  //
  // Call PEI Service at offset 32 (likely LocatePpi or similar)
  // looking for the GUID at unk_FFE83EF4.
  //
  if ((*PeiServices)->LocatePpi (
         &gUnknownPpiGuid,
         0,
         &PpiInstance,
         &SectionInstance
         ) >= 0) {
    return SectionInstance;
  }

  return 0;
}


//========================================================================
//  Debug Print (Conditional)
//========================================================================

/**
  Conditionally prints a debug message if the CPU presence status
  matches the requested error level.

  @param[in]  ErrorLevel  The error level mask
  @param[in]  Format      Format string
  @param[in]  ...         Additional arguments
**/
INTN
CpuInfoDebugPrint (
  IN UINTN  ErrorLevel,
  IN CHAR8  *Format,
  ...
  )
{
  EFI_PEI_SERVICES  **PeiServices;
  VA_LIST           Va;
  INTN              PresenceStatus;

  VA_START (Va, Format);

  PeiServices = GetPeiServicesFromIdtr ();
  if (PeiServices == NULL) {
    return 0;
  }

  PresenceStatus = GetCpuPresenceStatus ();
  if ((PresenceStatus & ErrorLevel) != 0) {
    //
    // Call PEI Services debug print at offset 0
    // (PEI_SERVICES pointer + 0 is the first function pointer)
    // This is typically DebugPrint or similar.
    //
    return (*(PeiServices + 0))(ErrorLevel, Format, (CHAR8*)Va);
  }

  return PresenceStatus;
}


//========================================================================
//  CPUID Wrappers
//========================================================================

/**
  Executes the CPUID instruction with the given input EAX.

  @param[in]  _EAX  CPUID leaf
  @param[out] a2    Returns EAX (optional)
  @param[out] a3    Returns EBX (optional)
  @param[out] a4    Returns ECX (optional)
  @param[out] a5    Returns EDX (optional)

  @return The value of EAX after CPUID.
**/
UINT32
AsmCpuid (
  IN     UINT32  _EAX,
  OUT    UINT32  *a2  OPTIONAL,
  OUT    UINT32  *a3  OPTIONAL,
  OUT    UINT32  *a4  OPTIONAL,
  OUT    UINT32  *a5  OPTIONAL
  )
{
  UINT32  _EBX, _ECX, _EDX;

  AsmCpuidEx (_EAX, 0, a2, &_EBX, &_ECX, &_EDX);

  if (a2 != NULL) *a2 = _EAX;
  if (a3 != NULL) *a3 = _EBX;
  if (a4 != NULL) *a4 = _ECX;
  if (a5 != NULL) *a5 = _EDX;

  return _EAX;
}


/**
  Executes CPUID leaf 0x0B (Extended Topology Enumeration).

  This leaf provides processor topology information such as
  the number of logical processors per core, per package, etc.

  @param[out] a1  Optional pointer to receive EAX value

  @return Always CPUID_EXTENDED_TOPOLOGY (11 = 0x0B).
**/
UINT32
CpuId (
  OUT UINT32  *a1 OPTIONAL
  )
{
  UINT32  _EBX, _ECX, _EDX;

  AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 0, &_EBX, &_ECX, &_EDX);

  //
  // Check if leaf 0x0B is supported (EBX non-zero means valid)
  //
  if (a1 != NULL) {
    *a1 = _EBX;
  }

  return CPUID_EXTENDED_TOPOLOGY;
}


//========================================================================
//  ASSERT / Debug Assert Support
//========================================================================

/**
  Reports an assertion failure via the PEI Services assertion handler.

  @param[in]  FileName     Source file name string
  @param[in]  LineNumber   Line number
  @param[in]  Description  Assertion expression text
**/
VOID
CpuInfoReportAssertion (
  IN CHAR8   *FileName,
  IN UINTN   LineNumber,
  IN CHAR8   *Description
  )
{
  EFI_PEI_SERVICES  **PeiServices;

  PeiServices = GetPeiServicesFromIdtr ();
  if (PeiServices != NULL) {
    //
    // PEI_SERVICES + 4 is ReportStatusCode or assertion handler
    //
    (*PeiServices)->ReportStatusCode (
      (EFI_STATUS_CODE_TYPE)FileName,
      (EFI_STATUS_CODE_VALUE)LineNumber,
      (EFI_STATUS_CODE_DATA*)Description
      );
  }
}


//========================================================================
//  String Helpers
//========================================================================

/**
  Returns the length of an ASCII string.

  This implementation also validates the string does not exceed
  PcdMaximumAsciiStringLength (0xF4240 = 1,000,000).

  @param[in]  String  Pointer to null-terminated ASCII string.

  @return Length of the string in characters.
**/
UINTN
AsciiStrLen (
  IN CHAR8  *String
  )
{
  UINTN  Length;

  if (String == NULL) {
    CpuInfoReportAssertion (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\String.c",
      1082,
      "String != ((void *) 0)"
      );
  }

  Length = 0;
  while (String[Length] != '\0') {
    if (Length >= 0xF4240) {
      CpuInfoReportAssertion (
        "e:\\hs\\MdePkg\\Library\\BaseLib\\String.c",
        1090,
        "Length < _gPcd_FixedAtBuild_PcdMaximumAsciiStringLength"
        );
    }
    Length++;
  }

  return Length;
}


//========================================================================
//  IDTR Access
//========================================================================

/**
  Reads the Interrupt Descriptor Table Register (IDTR).

  @param[out]  Idtr  Pointer to IA32_DESCRIPTOR structure to receive IDTR value.
**/
VOID
ReadIdtr (
  OUT IA32_DESCRIPTOR  *Idtr
  )
{
  if (Idtr == NULL) {
    CpuInfoReportAssertion (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\X86ReadIdtr.c",
      37,
      "Idtr != ((void *) 0)"
      );
    return;
  }

  AsmReadIdtr (Idtr);
}


//========================================================================
//  Memory Functions
//========================================================================

/**
  Fills a memory buffer with a specified value.

  @param[in]  buf    Pointer to buffer
  @param[in]  count  Number of bytes to fill
  @param[in]  value  Fill value

  @return Pointer to buffer.
**/
VOID *
EFIAPI
Memset (
  VOID   *buf,
  UINTN  count,
  CHAR8  value
  )
{
  return SetMem (buf, count, (UINT8)value);
}


/**
  Fills a buffer with a 32-bit pattern.

  @param[in]  buf   Pointer to buffer
  @param[in]  cnt   Number of pairs (8 bytes per pair)
  @param[in]  val1  First DWORD value
  @param[in]  val2  Second DWORD value

  @return Pointer to buffer.
**/
VOID *
EFIAPI
SetMem32 (
  OUT VOID   *buf,
  IN UINTN   cnt,
  IN UINT32  val1,
  IN UINT32  val2
  )
{
  UINTN  Index;
  UINT32 *Ptr;

  Ptr = (UINT32*)buf;
  for (Index = 0; Index < cnt; Index++) {
    Ptr[Index * 2]     = val1;
    Ptr[Index * 2 + 1] = val2;
  }

  return buf;
}


/**
  Copies a memory buffer, handling overlapping regions.

  @param[out] dst     Destination buffer
  @param[in]  src     Source buffer
  @param[in]  count   Number of bytes to copy

  @return Pointer to destination buffer.
**/
VOID *
EFIAPI
CopyMem (
  OUT VOID       *dst,
  IN  CONST VOID *src,
  IN  UINTN      count
  )
{
  UINT8  *Dst8;
  UINT8  *Src8;
  UINTN  Count;

  Dst8 = (UINT8*)dst;
  Src8 = (UINT8*)src;
  Count = count;

  //
  // Check for backwards overlap: if src < dst and src+count-1 >= dst
  // copy from end to start.
  //
  if ((UINTN)Src8 < (UINTN)Dst8 && ((UINTN)Src8 + Count - 1) >= (UINTN)Dst8) {
    //
    // Overlapping, copy backward
    //
    Src8 = &Src8[Count - 1];
    Dst8 = &Dst8[Count - 1];
    while (Count--) {
      *Dst8-- = *Src8--;
    }
    return dst;
  }

  //
  // Non-overlapping: copy aligned DWORDs first, then remainder bytes
  //
  for (; Count >= 4; Count -= 4) {
    *(UINT32*)Dst8 = *(UINT32*)Src8;
    Dst8 += 4;
    Src8 += 4;
  }

  while (Count--) {
    *Dst8++ = *Src8++;
  }

  return dst;
}