/** @file
GetCpuInfo - Lenovo HR650X BIOS CPU Information Collection DXE Driver
Source file: PurleyPlatPkg/Cpu/Dxe/GetCpuInfo/GetCpuInfo.c
Image: GetCpuInfo.efi
SHA256: 0abaf97cc019b0b5c9d98419d8ad527559307ad2afa7d8bb84dbc33d7589688e
This DXE driver collects detailed CPU information from all processors in the
system by executing CPUID instructions and reading model-specific registers
(MSRs). The collected data -- including Platform ID, Stepping, Microcode
revision, CPUID signature, core frequency, and actual operating frequency --
is displayed through an HII form in the BIOS setup menu.
The driver uses the following protocols:
- EFI CPU Architecture Protocol (to enumerate processors)
- HII Database Protocol (to manage forms)
- HII Package List Protocol (to register string packages)
- HII String Protocol (for string management)
- HII Config Access Protocol (for form callbacks)
- HII Config Routing Protocol (for configuration routing)
**/
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/DxeHobLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PrintLib.h>
#include <Library/DxePcdLib.h>
#include <Library/IoLib.h>
#include <Library/PciExpressLib.h>
#include <Library/UefiHiiServicesLib.h>
#include <Library/UefiHiiLib.h>
#include <Library/DevicePathLib.h>
#include <Library/UefiLib.h>
#include <Protocol/HiiDatabase.h>
#include <Protocol/HiiPackageList.h>
#include <Protocol/HiiString.h>
#include <Protocol/HiiConfigAccess.h>
#include <Protocol/HiiConfigRouting.h>
#include <Protocol/Cpu.h>
#include <Guid/HobList.h>
//
//============================================================================
// GLOBAL VARIABLES (.data section, 0x3D30 - 0x3DF0)
//============================================================================
//
EFI_HANDLE gImageHandle; ///< 0x3D40
EFI_SYSTEM_TABLE *gST; ///< 0x3D30
EFI_BOOT_SERVICES *gBS; ///< 0x3D38
EFI_RUNTIME_SERVICES *gRT; ///< 0x3D48
VOID *gPcdProtocol; ///< 0x3D58 PCD protocol (lazy init)
VOID *gHobList; ///< 0x3D60 HOB list pointer (lazy init)
UINT64 gPciExpressBaseAddress; ///< 0x3D68 PciExpressBaseAddress from PCD
VOID *gHiiDatabase; ///< 0x3D70 EFI_HII_DATABASE_PROTOCOL
VOID *gHiiConfigAccess; ///< 0x3D78 EFI_HII_CONFIG_ACCESS_PROTOCOL
VOID *gHiiString; ///< 0x3D80 EFI_HII_STRING_PROTOCOL
VOID *gHiiConfigRouting; ///< 0x3D88 EFI_HII_CONFIG_ROUTING_PROTOCOL
VOID *gHiiPackageList; ///< 0x3D90 EFI_HII_PACKAGE_LIST_PROTOCOL
CHAR16 *gAllCpuInfoString; ///< 0x3DA0 "Total CPU Number: <N>"
CHAR16 *gActualCpuFreqStrings; ///< 0x3DA8 Per-CPU actual frequency strings
CHAR16 *gSteppingStrings; ///< 0x3DB0 Per-CPU stepping strings
CHAR16 *gCpuCoreFreqStrings; ///< 0x3DB8 Per-CPU core frequency strings
CHAR16 *gMicroCodeRevStrings; ///< 0x3DC0 Per-CPU microcode revision strings
UINT32 gNumberOfProcessors; ///< 0x3DC8 Total number of processors
CHAR16 *gCpuIdStrings; ///< 0x3DD0 Per-CPU CPUID strings
CHAR16 *gPlatformIdStrings; ///< 0x3DD8 Per-CPU platform ID strings
CHAR16 *gCpuNameStrings; ///< 0x3DE0 Per-CPU name strings ("CPU<N>")
EFI_CPU_ARCH_PROTOCOL *gCpuProtocol; ///< 0x3DE8 EFI_CPU_ARCH_PROTOCOL
UINT64 gMaxTurboRatio; ///< 0x3D28 Cached MPERF value for frequency calc
//
//============================================================================
// GUID DEFINITIONS (.rdata section)
//============================================================================
//
// The GUIDs are defined externally in the PE/COFF .rdata section:
// unk_3B00 = EFI_CPU_ARCH_PROTOCOL_GUID
// unk_3B10 = gEfiRuntimeServicesTableGuid
// unk_3B30 = HII package list GUID for VFR
// unk_3B40 = gEfiPcdProtocolGuid
// unk_3B50 = PackageListGuid for HII string packages
// unk_3C08 = gEfiHiiStringProtocolGuid
// unk_3C18 = gEfiHiiConfigRoutingProtocolGuid
// unk_3C28 = gEfiHiiPackageListProtocolGuid
// unk_3C38 = gEfiHiiDatabaseProtocolGuid
// unk_3C48 = gEfiHobListGuid
// unk_3C58 = gEfiHiiConfigAccessProtocolGuid
// unk_3C68 = CPU Info formset GUID
// unk_3C80 = CPU Info form GUID (VFR binary data)
//
//
//============================================================================
// FORWARD DECLARATIONS
//============================================================================
//
EFI_STATUS
EFIAPI
GetCpuInfoEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
);
EFI_STATUS
CpuInfoDriverEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
);
VOID
CpuInfoCollection (
VOID
);
EFI_STATUS
CpuInfoDisplayForm (
IN EFI_HII_HANDLE HiiHandle
);
EFI_HII_HANDLE
CpuInfoRegisterPackageList (
IN CONST EFI_GUID *PackageListGuid,
...
);
VOID
CpuInfoDelayMicro (
IN UINT32 DelayCount
);
EFI_STATUS
GetPlatformLanguage (
OUT CHAR16 **PlatformLang
);
//
//============================================================================
// CPUID / MSR / I/O HELPERS (inlined wrappers)
//============================================================================
//
/**
Executes CPUID instruction with given leaf and subleaf.
@param[in] Leaf CPUID input EAX.
@param[in] SubLeaf CPUID input ECX.
@param[out] Eax Output EAX (may be NULL).
@param[out] Ebx Output EBX (may be NULL).
@param[out] Ecx Output ECX (may be NULL).
@param[out] Edx Output EDX (may be NULL).
**/
VOID
EFIAPI
CpuIdEx (
IN UINT32 Leaf,
IN UINT32 SubLeaf,
OUT UINT32 *Eax OPTIONAL,
OUT UINT32 *Ebx OPTIONAL,
OUT UINT32 *Ecx OPTIONAL,
OUT UINT32 *Edx OPTIONAL
)
{
__asm {
mov eax, Leaf
mov ecx, SubLeaf
cpuid
mov edi, Eax
test edi, edi
jz @f
mov [edi], eax
@@:
mov edi, Ebx
test edi, edi
jz @f
mov [edi], ebx
@@:
mov edi, Ecx
test edi, edi
jz @f
mov [edi], ecx
@@:
mov edi, Edx
test edi, edi
jz @f
mov [edi], edx
@@:
}
}
/**
Reads an MSR.
@param[in] Index MSR index.
@return The 64-bit MSR value.
**/
UINT64
EFIAPI
AsmReadMsr64 (
IN UINT32 Index
)
{
return __readmsr (Index);
}
/**
Writes an MSR.
@param[in] Index MSR index.
@param[in] Value 64-bit value to write.
**/
VOID
EFIAPI
AsmWriteMsr64 (
IN UINT32 Index,
IN UINT64 Value
)
{
__writemsr (Index, Value);
}
//
//============================================================================
// MODULE ENTRY POINT
//============================================================================
//
/**
GetCpuInfo DXE driver entry point.
Initializes UEFI global variables (ImageHandle, SystemTable, BootServices,
RuntimeServices), locates required protocols, reads platform type,
and dispatches the main CPU info collection routine.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
GetCpuInfoEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
//
// Call the actual driver entry
//
return CpuInfoDriverEntry (ImageHandle, SystemTable);
}
/**
Internal driver initialization.
1. Saves UEFI handles and protocols
2. Locates HII Database, PackageList, String, ConfigAccess, ConfigRouting
3. Checks platform language and configures IO
4. Reads platform type through IO port 0x80
5. Waits for IO completion (polling port 0x508)
6. In setup mode, calls CpuInfoFormUpdate() to display the form
@param[in] ImageHandle The firmware allocated handle.
@param[in] SystemTable A pointer to the EFI System Table.
@return EFI_SUCCESS in setup mode, or EFI_INVALID_PARAMETER otherwise.
**/
EFI_STATUS
CpuInfoDriverEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINT16 PlatformType;
BOOLEAN SetupMode;
UINT64 PcdAddress;
UINTN WaitValue;
//
// Step 1: Save UEFI handles
//
gImageHandle = ImageHandle;
gST = SystemTable;
gBS = SystemTable->BootServices;
gRT = SystemTable->RuntimeServices;
ASSERT (ImageHandle != NULL);
ASSERT (gST != NULL);
ASSERT (gBS != NULL);
ASSERT (gRT != NULL);
//
// Step 2: Initialize HOB list
//
GetHobList ();
//
// Step 3: Get PCD for PCI Express base address (lazy init via PCD protocol)
//
CpuInfoLocatePcdProtocol ();
gPciExpressBaseAddress = PcdGet64 (PcdPciExpressBaseAddress);
//
// Step 4: Locate HII protocols
//
Status = gBS->LocateProtocol (&gEfiHiiDatabaseProtocolGuid, NULL, &gHiiDatabase);
ASSERT_EFI_ERROR (Status);
Status = gBS->LocateProtocol (&gEfiHiiPackageListProtocolGuid, NULL, &gHiiPackageList);
ASSERT_EFI_ERROR (Status);
Status = gBS->LocateProtocol (&gEfiHiiStringProtocolGuid, NULL, &gHiiString);
ASSERT_EFI_ERROR (Status);
Status = gBS->LocateProtocol (&gEfiHiiConfigAccessProtocolGuid, NULL, &gHiiConfigAccess);
ASSERT_EFI_ERROR (Status);
Status = gBS->LocateProtocol (&gEfiHiiConfigRoutingProtocolGuid, NULL, &gHiiConfigRouting);
ASSERT_EFI_ERROR (Status);
//
// Step 5: Check and enable platform language via PCI config
// - reads byte at PciExpressBaseAddress + 0xF4244
// - if bit 7 is clear, writes 0x500 (1280) to IO port 0xF4240
//
PcdAddress = 1024068; // Offset 0xF4244 from PciExpressBaseAddress
if ((*(INT8 *)PciExpressRead32 (PcdAddress) & 0x80) == 0) {
//
// Write to IO port to enable
//
CpuInfoEnablePlatformLanguage (PcdAddress - 4); // 0xF4240
*(UINT8 *)PciExpressRead32 (PcdAddress) |= 0x80;
}
//
// Step 6: Read platform type from IO port 0x80
//
PlatformType = IoRead32 (IO_PORT_0x80);
IoRead32 (IO_PORT_0x80); // Consume/clear
SetupMode = (PlatformType & 0x200) != 0;
//
// Step 7: Wait for IO completion by polling port 0x508
//
do {
IoRead32 (IO_PORT_0x80); // Small delay via IO port read
} while (((IoRead32 (IO_PORT_0x508) + 357
- IoRead32 (IO_PORT_0x508)) & 0x800000) == 0);
//
// Step 8: Return based on setup mode
//
if (SetupMode) {
return CpuInfoCollection ();
} else {
return EFI_INVALID_PARAMETER;
}
}
//
//============================================================================
// CPU INFORMATION COLLECTION
//============================================================================
//
/**
Main CPU information collection routine.
Locates the CPU Architecture protocol to get number of processors,
allocates per-processor Unicode string buffers (8 buffers x 100 chars each),
writes label strings and per-CPU data, then calls CpuInfoReadAll() to
populate values. Registers a callback with the CPU protocol and creates
an HII form for display.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
CpuInfoCollection (
VOID
)
{
EFI_STATUS Status;
UINT32 Index;
UINT32 ProcessorNumber;
EFI_CPU_ARCH_PROTOCOL *CpuProtocol;
EFI_HII_HANDLE HiiHandle;
UINT16 *Buffer;
//
// Step 1: PCD enable check
//
if (!PcdGetBool (PcdCpuInfoDisplayEnable)) {
return EFI_SUCCESS;
}
//
// Step 2: Locate CPU Architecture Protocol
//
CpuProtocol = CpuInfoLocateCpuProtocol ();
if (CpuProtocol == NULL) {
return EFI_UNSUPPORTED;
}
//
// Step 3: Get number of processors from HOB
//
ProcessorNumber = CpuInfoGetProcessorCountFromHob ();
if (ProcessorNumber == 0) {
return EFI_NOT_FOUND;
}
gNumberOfProcessors = ProcessorNumber;
//
// Step 4: Allocate per-processor string buffers
//
gAllCpuInfoString = AllocateZeroPool (CPU_INFO_STRING_SIZE * ProcessorNumber);
gCpuNameStrings = AllocateZeroPool (CPU_INFO_STRING_SIZE * ProcessorNumber);
gPlatformIdStrings = AllocateZeroPool (CPU_INFO_STRING_SIZE * ProcessorNumber);
gSteppingStrings = AllocateZeroPool (CPU_INFO_STRING_SIZE * ProcessorNumber);
gMicroCodeRevStrings = AllocateZeroPool (CPU_INFO_STRING_SIZE * ProcessorNumber);
gCpuIdStrings = AllocateZeroPool (CPU_INFO_STRING_SIZE * ProcessorNumber);
gCpuCoreFreqStrings = AllocateZeroPool (CPU_INFO_STRING_SIZE * ProcessorNumber);
gActualCpuFreqStrings = AllocateZeroPool (CPU_INFO_STRING_SIZE * ProcessorNumber);
if (gAllCpuInfoString == NULL || gCpuNameStrings == NULL ||
gPlatformIdStrings == NULL || gSteppingStrings == NULL ||
gMicroCodeRevStrings == NULL || gCpuIdStrings == NULL ||
gCpuCoreFreqStrings == NULL || gActualCpuFreqStrings == NULL) {
CpuInfoFreeAllBuffers ();
return EFI_OUT_OF_RESOURCES;
}
//
// Step 5: Build header string "Total CPU Number: %d\n"
//
UnicodeSPrint (
gAllCpuInfoString,
CPU_INFO_STRING_SIZE,
L"Total CPU Number: %d\n",
ProcessorNumber
);
//
// Step 6: Initialize per-processor label strings
//
for (Index = 0; Index < ProcessorNumber; Index++) {
StrCpyS (
&gCpuNameStrings[Index * CPU_INFO_STRING_SIZE],
CPU_INFO_STRING_SIZE,
L"CPU"
);
StrCpyS (
&gPlatformIdStrings[Index * CPU_INFO_STRING_SIZE],
CPU_INFO_STRING_SIZE,
L"PlatformID: "
);
StrCpyS (
&gSteppingStrings[Index * CPU_INFO_STRING_SIZE],
CPU_INFO_STRING_SIZE,
L"Stepping: "
);
StrCpyS (
&gMicroCodeRevStrings[Index * CPU_INFO_STRING_SIZE],
CPU_INFO_STRING_SIZE,
L"MicroCodeRev: "
);
StrCpyS (
&gCpuIdStrings[Index * CPU_INFO_STRING_SIZE],
CPU_INFO_STRING_SIZE,
L"CPUID: "
);
StrCpyS (
&gCpuCoreFreqStrings[Index * CPU_INFO_STRING_SIZE],
CPU_INFO_STRING_SIZE,
L"CpuCoreFreq (MHz): "
);
StrCpyS (
&gActualCpuFreqStrings[Index * CPU_INFO_STRING_SIZE],
CPU_INFO_STRING_SIZE,
L"ActualCpuFreq (MHz): "
);
}
//
// Step 7: Collect actual CPU data from all processors
//
CpuInfoReadAll ();
//
// Step 8: Register callback for form update
//
((EFI_HII_CONFIG_ACCESS_PROTOCOL *)gCpuProtocol)->CallBack (
gCpuProtocol,
CpuInfoReadAll,
0, 0, 0, 0, 0
);
//
// Step 9: Create and display HII form
//
HiiHandle = CpuInfoRegisterPackageList (
&gCpuInfoFormSetGuid,
&gCpuInfoVfrBin,
NULL
);
CpuInfoDisplayForm (HiiHandle);
//
// Step 10: Free buffers
//
CpuInfoFreeAllBuffers ();
return EFI_SUCCESS;
}
/**
Reads CPU information from every processor and populates
the global string buffers.
For each processor, this function:
1. Gets stepping (CPUID.01.EAX[3:0])
2. Gets CPUID signature (CPUID.01.EAX)
3. Gets Platform ID from MSR 0x17[50:47]
4. Triggers microcode update then reads MSR 0x8B[63:32]
5. Gets core frequency from MSR 0xCE[15:8] * 100 MHz
6. Measures actual frequency using MPERF/APERF ratio
**/
VOID
CpuInfoReadAll (
VOID
)
{
UINT32 Index;
UINT32 Eax;
UINT32 Ebx;
UINT32 Ecx;
UINT32 Edx;
UINT64 PlatformIdMsr;
UINT64 MicroCodeRevMsr;
UINT64 MaxEfficiencyRatio;
UINT64 MperfValue;
UINT64 AperfValue;
UINT64 AperfDelta;
UINT64 MeasuredRatio;
UINT64 ActualFrequency;
CHAR16 Buffer[38]; // Max width for number formatting
for (Index = 0; Index < gNumberOfProcessors; Index++) {
//
// ---- CPU name ----
//
UnicodeValueToString (Buffer, 0, Index, 4);
StrCatS (
&gCpuNameStrings[Index * CPU_INFO_STRING_SIZE],
CPU_INFO_STRING_SIZE,
Buffer
);
if (Index == 0) {
//
// ---- CPUID leaf 1: stepping + CPUID signature ----
//
AsmCpuid (CPUID_VERSION_INFO, &Eax, &Ebx, &Ecx, &Edx);
// Stepping (EAX[3:0])
UnicodeValueToString (Buffer, 0, Eax & 0xF, 1);
StrCatS (
&gSteppingStrings[Index * CPU_INFO_STRING_SIZE],
CPU_INFO_STRING_SIZE,
Buffer
);
// CPUID (EAX full, printed as hex)
UnicodeValueToString (
Buffer,
FLAG_UNSIGNED | FLAG_ALTERNATE,
Eax,
8
);
StrCatS (
&gCpuIdStrings[Index * CPU_INFO_STRING_SIZE],
CPU_INFO_STRING_SIZE,
Buffer
);
}
//
// ---- Platform ID from MSR 0x17 (bits [50:47] in upper word) ----
//
PlatformIdMsr = AsmReadMsr64 (MSR_IA32_PLATFORM_ID);
UnicodeValueToString (Buffer, 0, (PlatformIdMsr >> 32) & 0xFFFF, 4);
StrCatS (
&gPlatformIdStrings[Index * CPU_INFO_STRING_SIZE],
CPU_INFO_STRING_SIZE,
Buffer
);
//
// ---- Microcode Revision ----
// Procedure: write 0 to MSR 0x8B, execute CPUID leaf 1,
// then read MSR 0x8B[63:32].
//
AsmWriteMsr64 (MSR_IA32_BIOS_SIGN_ID, 0);
AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);
MicroCodeRevMsr = AsmReadMsr64 (MSR_IA32_BIOS_SIGN_ID);
MicroCodeRevMsr >>= 32;
UnicodeValueToString (Buffer, 0, MicroCodeRevMsr, 8);
StrCatS (
&gMicroCodeRevStrings[Index * CPU_INFO_STRING_SIZE],
CPU_INFO_STRING_SIZE,
Buffer
);
//
// ---- Core Frequency ----
// MSR_PLATFORM_INFO (0xCE) byte 1 is the Maximum Efficiency Ratio.
// Core frequency = ratio * 100 MHz (bus frequency).
//
MaxEfficiencyRatio = AsmReadMsr64 (MSR_PLATFORM_INFO);
MaxEfficiencyRatio = (MaxEfficiencyRatio >> 8) & 0xFF;
UnicodeValueToString (Buffer, 0, MaxEfficiencyRatio * 100, 8);
StrCatS (
&gCpuCoreFreqStrings[Index * CPU_INFO_STRING_SIZE],
CPU_INFO_STRING_SIZE,
Buffer
);
//
// ---- Actual Operating Frequency ----
// Uses the MPERF (MSR 0xE7) and APERF (MSR 0xE8) ratio method:
//
// 1. First call: zero MPERF, delay, read MPERF as baseline
// 2. Zero APERF, delay, read APERF
// 3. ActualFreq = CoreFreq * (APERF / MPERF)
//
if (gMaxTurboRatio == 0) {
AsmWriteMsr64 (MSR_IA32_MPERF, 0);
CpuInfoDelayMicro (357);
gMaxTurboRatio = AsmReadMsr64 (MSR_IA32_MPERF);
}
AsmWriteMsr64 (MSR_IA32_APERF, 0);
CpuInfoDelayMicro (357);
AperfValue = AsmReadMsr64 (MSR_IA32_APERF);
if (AperfValue > gMaxTurboRatio) {
AperfValue = gMaxTurboRatio;
}
//
// Calculate: ratio = (APERF * 100) / MPERF (as percentage)
// freq = (ratio * CoreFreq * 100) / 10000
//
MeasuredRatio = DivU64xU64 (MultU64xU64 (AperfValue, 100), gMaxTurboRatio);
//
// actual MHz = (ratio * MaxEfficiencyRatio * 100) / 100
//
ActualFrequency = DivU64xU64 (
MultU64xU64 (MeasuredRatio, MaxEfficiencyRatio * 100),
100
);
//
// Format and append
//
UnicodeValueToString (Buffer, 0, ActualFrequency / 100, 8);
StrCatS (
&gActualCpuFreqStrings[Index * CPU_INFO_STRING_SIZE],
CPU_INFO_STRING_SIZE,
Buffer
);
}
}
//
//============================================================================
// UTILITY FUNCTIONS
//============================================================================
//
/**
Locates the PCD Protocol using gBS->LocateProtocol with lazy init.
**/
EFI_STATUS
CpuInfoLocatePcdProtocol (
VOID
)
{
EFI_STATUS Status;
if (gPcdProtocol != NULL) {
return EFI_SUCCESS;
}
Status = gBS->LocateProtocol (
&gEfiPcdProtocolGuid,
NULL,
&gPcdProtocol
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "ASSERT: !EFI_ERROR (Status)\n"));
}
return Status;
}
/**
Locates the EFI CPU Architecture Protocol with lazy init.
**/
EFI_CPU_ARCH_PROTOCOL *
CpuInfoLocateCpuProtocol (
VOID
)
{
EFI_STATUS Status;
if (gCpuProtocol != NULL) {
return gCpuProtocol;
}
Status = gBS->LocateProtocol (
&gEfiCpuArchProtocolGuid,
NULL,
(VOID **)&gCpuProtocol
);
if (EFI_ERROR (Status)) {
gCpuProtocol = NULL;
}
return gCpuProtocol;
}
/**
Searches the HOB list for CPU info data and returns the processor count.
This function traverses the HOB list looking for a GUID HOB with
CPU-related data. The HOB GUID is compared against a specific expected
GUID. The first 16 bytes past the GUID structure contain the
processor count.
@return Number of processors, or 0 on failure.
**/
UINT32
CpuInfoGetProcessorCountFromHob (
VOID
)
{
EFI_HOB_GUID_TYPE *GuidHob;
UINT32 CpuCount;
EFI_GUID *HobDataGuid;
UINT32 MagicCheck[4];
UINT32 MagicReverse[4];
//
// These magic values represent GUID comparison data
// They are precomputed from the CPU-info HOB GUID
//
MagicCheck[0] = 0xDC33DC7E;
MagicCheck[1] = 0x46DCC6BE;
MagicCheck[2] = 0x7CBB9BAD;
MagicCheck[3] = 0x38D7C422;
MagicReverse[0] = 0x7FF36C21;
MagicReverse[1] = 0x4310A9FD;
MagicReverse[2] = 0xCA6C383A;
MagicReverse[3] = 0xC08EF412;
GuidHob = GetNextGuidHob (GetHobList ());
ASSERT (GuidHob != NULL);
//
// Walk through HOBs looking for CPU info
//
while (GuidHob != NULL) {
HobDataGuid = (EFI_GUID *)((UINT8 *)GuidHob + sizeof (EFI_HOB_GENERIC_HEADER));
//
// Compare GUID to the expected CPU-info HOB GUID
//
if (CompareGuid (HobDataGuid, (EFI_GUID *)MagicCheck)) {
//
// Found CPU info HOB -- extract processor count
//
CpuCount = *(UINT32 *)((UINT8 *)GuidHob + sizeof (EFI_HOB_GUID_TYPE));
return CpuCount;
}
//
// Move to next HOB
//
GuidHob = GetNextGuidHob (
(VOID *)((UINT8 *)GuidHob + *(UINT16 *)((UINT8 *)GuidHob + 2))
);
}
ASSERT (GuidHob != NULL);
return 0;
}
/**
Microsecond-scale delay using I/O port writes.
Writes to the PCI Express configuration space (via LPC/eSPI cycle)
to create a known delay. The delay is achieved by repeatedly
writing to a port and waiting for completion.
@param[in] DelayCount Number of delay iterations (357 = ~1 tick).
**/
VOID
CpuInfoDelayMicro (
IN UINT32 DelayCount
)
{
UINT32 ShiftedCount;
UINT32 AddressRemainder;
ShiftedCount = DelayCount >> 22;
AddressRemainder = DelayCount & 0x3FFFFF;
do {
//
// Wait for previous I/O to complete by polling port 0x508
//
while (((AddressRemainder + (IoRead32 (IO_PORT_0x508) & 0xFFFFFF)
- IoRead32 (IO_PORT_0x508)) & 0x800000) == 0) {
IoRead32 (IO_PORT_0x80); // Delay via IO read
}
//
// After first iteration, always use max remainder for remaining
// shifted counts
//
AddressRemainder = 0x400000;
ShiftedCount--;
} while (ShiftedCount > 0);
}
/**
Retrieves the platform language from UEFI variable "PlatformLang".
First call determines required buffer size (expects EFI_BUFFER_TOO_SMALL),
second call retrieves the actual string.
@param[out] PlatformLang Pointer to receive allocated buffer with language string.
@return EFI_STATUS.
**/
EFI_STATUS
GetPlatformLanguage (
OUT CHAR16 **PlatformLang
)
{
EFI_STATUS Status;
UINTN StringSize;
if (PlatformLang == NULL) {
return EFI_INVALID_PARAMETER;
}
*PlatformLang = NULL;
StringSize = 0;
//
// First: query size
//
Status = gRT->GetVariable (
L"PlatformLang",
&gEfiRuntimeServicesTableGuid,
NULL,
&StringSize,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
*PlatformLang = AllocateZeroPool (StringSize);
if (*PlatformLang == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Second: get actual data
//
Status = gRT->GetVariable (
L"PlatformLang",
&gEfiRuntimeServicesTableGuid,
NULL,
&StringSize,
*PlatformLang
);
if (EFI_ERROR (Status)) {
gBS->FreePool (*PlatformLang);
*PlatformLang = NULL;
}
}
return Status;
}
/**
Frees all per-processor string buffers.
**/
VOID
CpuInfoFreeAllBuffers (
VOID
)
{
if (gAllCpuInfoString != NULL) gBS->FreePool (gAllCpuInfoString);
if (gCpuNameStrings != NULL) gBS->FreePool (gCpuNameStrings);
if (gPlatformIdStrings != NULL) gBS->FreePool (gPlatformIdStrings);
if (gSteppingStrings != NULL) gBS->FreePool (gSteppingStrings);
if (gMicroCodeRevStrings != NULL) gBS->FreePool (gMicroCodeRevStrings);
if (gCpuIdStrings != NULL) gBS->FreePool (gCpuIdStrings);
if (gCpuCoreFreqStrings != NULL) gBS->FreePool (gCpuCoreFreqStrings);
if (gActualCpuFreqStrings != NULL) gBS->FreePool (gActualCpuFreqStrings);
}
//
//============================================================================
// HII FORM DISPLAY
//============================================================================
//
/**
Displays the CPU information form in the BIOS setup.
Allocates HII op-code handles, creates the form header/end,
then populates the form with the collected per-processor data.
@param[in] HiiHandle The HII handle for the registered form.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
CpuInfoDisplayForm (
IN EFI_HII_HANDLE HiiHandle
)
{
EFI_STATUS Status;
VOID *StartOpCodeHandle;
VOID *EndOpCodeHandle;
UINTN Index;
//
// Allocate op-code handles
//
StartOpCodeHandle = HiiAllocateOpCodeHandle ();
EndOpCodeHandle = HiiAllocateOpCodeHandle ();
if (StartOpCodeHandle == NULL || EndOpCodeHandle == NULL) {
if (StartOpCodeHandle != NULL) HiiFreeOpCodeHandle (StartOpCodeHandle);
if (EndOpCodeHandle != NULL) HiiFreeOpCodeHandle (EndOpCodeHandle);
return EFI_OUT_OF_RESOURCES;
}
//
// Create form header and end markers
//
HiiCreateFormOpCode (StartOpCodeHandle, FORM_CPU_INFO_ID, 0);
HiiCreateEndOpCode (EndOpCodeHandle);
//
// Add "Total CPU Number" header text
//
HiiCreateStringOpCode (
StartOpCodeHandle,
(UINT16 *)gAllCpuInfoString,
0,
0,
0,
0,
NULL
);
//
// Add per-processor data lines (7 fields each)
//
for (Index = 0; Index < gNumberOfProcessors; Index++) {
HiiCreateStringOpCode (
StartOpCodeHandle,
&gCpuNameStrings[Index * CPU_INFO_STRING_SIZE],
0, 0, 0, 0, NULL
);
HiiCreateStringOpCode (
StartOpCodeHandle,
&gPlatformIdStrings[Index * CPU_INFO_STRING_SIZE],
0, 0, 0, 0, NULL
);
HiiCreateStringOpCode (
StartOpCodeHandle,
&gSteppingStrings[Index * CPU_INFO_STRING_SIZE],
0, 0, 0, 0, NULL
);
HiiCreateStringOpCode (
StartOpCodeHandle,
&gMicroCodeRevStrings[Index * CPU_INFO_STRING_SIZE],
0, 0, 0, 0, NULL
);
HiiCreateStringOpCode (
StartOpCodeHandle,
&gCpuIdStrings[Index * CPU_INFO_STRING_SIZE],
0, 0, 0, 0, NULL
);
HiiCreateStringOpCode (
StartOpCodeHandle,
&gCpuCoreFreqStrings[Index * CPU_INFO_STRING_SIZE],
0, 0, 0, 0, NULL
);
HiiCreateStringOpCode (
StartOpCodeHandle,
&gActualCpuFreqStrings[Index * CPU_INFO_STRING_SIZE],
0, 0, 0, 0, NULL
);
}
//
// Update the form via HII Database
//
Status = ((EFI_HII_DATABASE_PROTOCOL *)gHiiDatabase)->UpdateForm (
gHiiDatabase,
HiiHandle,
&gCpuInfoFormSetGuid,
FORM_CPU_INFO_ID,
StartOpCodeHandle,
EndOpCodeHandle
);
//
// Clean up
//
HiiFreeOpCodeHandle (StartOpCodeHandle);
HiiFreeOpCodeHandle (EndOpCodeHandle);
return Status;
}
/**
Creates and registers the HII package list with string packages.
@param[in] PackageListGuid GUID for the package list.
@param[in] ... Variable list of packages (NULL-terminated).
@return HII handle for the registered package, or 0 on failure.
**/
EFI_HII_HANDLE
CpuInfoRegisterPackageList (
IN CONST EFI_GUID *PackageListGuid,
...
)
{
VA_LIST Args;
VOID *Package;
UINTN TotalSize;
UINT32 PackageListSize;
EFI_HII_PACKAGE_LIST_HEADER *PackageListHeader;
EFI_HII_HANDLE HiiHandle;
UINT8 *CurrentPtr;
EFI_STATUS Status;
VA_START (Args, PackageListGuid);
if (PackageListGuid == NULL) {
return 0;
}
//
// Calculate total size of all packages
//
Package = VA_ARG (Args, VOID *);
TotalSize = 0;
while (Package != NULL) {
TotalSize += ReadUnaligned32 (Package) - 4;
Package = VA_ARG (Args, VOID *);
}
if (TotalSize == 0) {
return 0;
}
PackageListSize = (UINT32)TotalSize + sizeof (EFI_HII_PACKAGE_LIST_HEADER);
PackageListHeader = AllocatePool (PackageListSize);
if (PackageListHeader == NULL) {
return 0;
}
//
// Build the package list header
//
CopyGuid (&PackageListHeader->PackageListGuid, PackageListGuid);
PackageListHeader->PackageLength = PackageListSize;
CurrentPtr = (UINT8 *)(PackageListHeader + 1);
VA_START (Args, PackageListGuid);
Package = VA_ARG (Args, VOID *);
while (Package != NULL) {
UINT32 PackageSize = ReadUnaligned32 (Package) - 4;
CopyMem (CurrentPtr, (UINT8 *)Package + 4, PackageSize);
CurrentPtr += PackageSize;
Package = VA_ARG (Args, VOID *);
}
//
// Register with HII Database
//
Status = ((EFI_HII_PACKAGE_LIST_PROTOCOL *)gHiiPackageList)->NewPackageList (
gHiiPackageList,
PackageListHeader,
0,
&HiiHandle
);
gBS->FreePool (PackageListHeader);
if (EFI_ERROR (Status)) {
return 0;
}
return HiiHandle;
}
/**
Allocates a new HII op-code handle.
@return Pointer to the allocated update data.
**/
VOID *
HiiAllocateOpCodeHandle (
VOID
)
{
EFI_HII_UPDATE_DATA *UpdateData;
UINT8 *Buffer;
UpdateData = AllocateZeroPool (sizeof (EFI_HII_UPDATE_DATA));
if (UpdateData == NULL) {
return NULL;
}
Buffer = AllocatePool (512);
if (Buffer == NULL) {
gBS->FreePool (UpdateData);
return NULL;
}
UpdateData->Buffer = Buffer;
UpdateData->BufferSize = 512;
UpdateData->UsedSize = 0;
return (VOID *)UpdateData;
}
/**
Frees an HII op-code handle.
@param[in] OpCodeHandle Handle to free.
**/
VOID
HiiFreeOpCodeHandle (
IN VOID *OpCodeHandle
)
{
if (OpCodeHandle == NULL) {
return;
}
gBS->FreePool (OpCodeHandle);
}