/** @file
PiSmmCpuDxeSmm.c - SMM CPU Driver Implementation
This is the SMM CPU driver for the Purley platform with Skylake-SP Xeon processors.
It initializes SMM execution environment, manages SMI handlers, handles S3 resume
transitions, and provides multi-processor services.
Source files identified from build strings:
- PiSmmCpuDxeSmm.c - Main module
- SmmFeatures.c - SMM feature MSRs (SMM save state, etc.)
- MpService.c - MP services
- CpuS3.c - S3 resume
- X64\\PageTbl.c - Page table setup
- SmmFeatures.c (suffix) - SMM feature MSR access
Auto-generated: AutoGen.c (EDK2 build infrastructure)
EDK2 Library classes linked:
- UefiBootServicesTableLib
- UefiRuntimeServicesTableLib
- BaseLib (CPUID, MSR, bitfield, switch stack, paging)
- BaseMemoryLibRepStr
- BasePrintLib
- BaseSynchronizationLib
- BaseDebugLibSerialPort
- BaseXApicX2ApicLib
- CpuExceptionHandlerLib
- SmmMemoryAllocationLib
- SmmPciExpressLib
- DxePcdLib
- MtrrLib
Build: DEBUG_VS2015 X64, HR6N0XMLK platform
**/
#include "PiSmmCpuDxeSmm.h"
//
// Global data initialized at module entry
//
EFI_HANDLE gImageHandle = NULL; // 0x108F0
EFI_SYSTEM_TABLE *gST = NULL; // 0x10698 - SystemTable alias
EFI_BOOT_SERVICES *gBS = NULL; // 0x108E8
UINT64 gSmst = NULL; // 0x10900 - SMM System Table (gSmst)
UINT64 gPcdProtocol = NULL; // 0x10908
UINT64 gPciExpressBase = 0; // 0x10910 - PCIe config base address
UINT64 gTimerPeriod = 0; // 0x10920
//
// SMM S3 Resume State - pointer to S3 resume structure in SMRAM
//
// Checked by SmmRestoreCpu() at 0x1C3C for:
// Signature "SMM_S_32" (0x32335F33534D4D53) -> use AsmDisablePaging64
// Signature "SMM_S_64" (0x34365F33534D4D53) -> use SwitchStack
//
SMM_S3_RESUME_STATE *mSmmS3ResumeState = NULL; // 0x10690
//
// CPU configuration
//
UINT32 mNumberOfCpus = 0; // 0x118A4
UINT32 mBspIndex = 0; // 0x118A0
UINT32 mCpusExiting = 0; // 0x11870 - APs remaining to finish init
UINT32 mGdtSize = 0; // 0x118C8
UINT64 mStartupRoutine = 0; // 0x11880
UINT64 mGdtIdtTable = 0; // 0x11878
UINT64 mGdtBuffer = 0; // 0x11898
UINT64 mMtrrTable = 0; // 0x118A8
UINT64 mGdtrProfile = 0; // 0x11888
UINT64 mIdtrProfile = 0; // 0x11890
UINT64 mPreSmmInitRegisterTable = 0; // 0x118B0
UINT64 mRegisterTable = 0; // 0x118B8
UINT64 mGdtForAp = 0; // 0x118C0
UINT64 mApDoneFlag = 0; // 0x11910
UINT8 mApStartPhase = 0; // 0x11918
//
// GDT/IDT allocations for APs
//
UINT64 mGdtForApAlloc = 0; // 0x10890
UINT64 mIdtForApAlloc = 0; // 0x10898
UINT64 mExcptHandlerAlloc = 0; // 0x108A0
//
// MSR Spin Locks
//
UINT64 mMsrSpinLocks = 0; // 0x108A8 - base address
UINT64 mMsrSpinLockCount = 0; // 0x108B0
UINT64 mMsrSpinLockMax = 0; // 0x105F0
//
// CPU enabling bitmap
//
UINT64 mCpuEnabledBitmap = 0; // 0x108B8
//
// State flags
//
UINT8 mGdtIdtReady = 0; // 0x10888
UINT8 mSmmCodeAccessCheck = 0; // 0x10889
UINT8 mSmrrConfigured = 0; // 0x118E9
UINT8 mCodeAccessCheck = 0; // 0x106A0
//
// Exception handler base/length
//
UINT64 mExceptionHandlerBase = 0; // 0x10970
UINT64 mExceptionHandlerEnd = 0; // 0x10978
//
// SMM CPU private data array (off_10378 at 0x10378)
// An 11-entry array of pointers to per-CPU SMM data:
// [0]: Pointer0
// [1]: SMM CPU private data (contains per-CPU register tables)
// [2]: Per-CPU register table entries (24 bytes each)
// [8]: APIC ID mapping
//
volatile UINT64 *gSmmCpuPrivate = (volatile UINT64 *)0x10378;
/**
_ModuleEntryPoint - SMM CPU Driver Entry Point
Called by SMM foundation on driver load. The first thing it does is save
ImageHandle/SystemTable, then initialize the module.
Flow:
1. Save UEFI handles via sub_C4C()
2. Save global status variable qword_10788 = 0x8000000000000001
3. Acquire init lock via sub_370() on unk_10790
4. Call SmmInit() (sub_2390) to run full initialization
5. If SmmInit succeeds, release lock and wait
6. On failure, run SmmUninit() to clean up
@param[in] ImageHandle EFI image handle
@param[in] SystemTable EFI system table
@return EFI_SUCCESS or error status
**/
EFI_STATUS
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
// 0xB90
SmmEntryPointSaveHandles(ImageHandle); // sub_C4C
gStatus = EFI_ALREADY_STARTED; // qword_10788 = 0x8000000000000001
if (!SmmAcquireLock(&InitLock)) { // sub_370(&unk_10790)
// Lock acquired - we are the first to init
gStatus = SmmInit(ProtocolNotifyFn, SystemTable); // sub_2390
if (gStatus >= 0 || gStatus < 0) { // always true after init
gStatus = gStatus; // preserve status
}
SmmReleaseLock(&InitLock); // sub_8A60(&unk_10790)
SmmWaitForEvent(&InitLock, -1); // sub_560(&unk_10790, -1)
// Debug asserts for build info
DebugPrint(0, "((BOOLEAN)(0==1))", ...);
}
if (EFI_ERROR(gStatus)) {
SmmUninit(gSmramRanges); // sub_9DE0(qword_115A8)
}
return gStatus;
}
/**
SmmInit - Main SMM Initialization
The largest function in the module (~3500 lines). This initializes all
SMM infrastructure:
1. Initialize SMM Foundation:
- Locate and save SMM System Table (gSmst)
- Register SMI handlers (SmiHandlerDispatch, SmiHandlerFeatureMsr)
- Initialize CPU features and SMM save state
2. Initialize MP Services:
- Determine number of CPUs (mNumberOfCpus)
- Allocate per-CPU structures (register tables, GDT/IDT)
- Configure SMM code access check
- Initialize page tables via InitPaging() (sub_314C)
- Initialize long mode via InitLongMode() (sub_3394)
- Program per-CPU register tables via InitMp() (sub_2110)
3. Initialize Interrupts and Exception Handling:
- Set up IDT entries for SMM
- Initialize exception handler stubs
- Set up GDT with SMM-specific descriptors
4. Initialize S3 Resume Path:
- Save S3 resume state
- Register SMM S3 resume callback
5. Start APs:
- Send SIPI to all APs
- Wait for APs to complete initialization
- Program MTRRs on each CPU
Called from _ModuleEntryPoint during driver load.
Source file: PiSmmCpuDxeSmm.c
Reference: e:\\hs\\PurleySktPkg\\Override\\IA32FamilyCpuPkg\\PiSmmCpuDxeSmm\\PiSmmCpuDxeSmm.c
**/
UINT64
SmmInit (
UINT64 *ProtocolNotifyFn,
EFI_SYSTEM_TABLE *SystemTable
)
{
// 0x2390 - Entry
// Extensive initialization sequence spanning SmmFeatures.c,
// MpService.c, CpuS3.c, and PageTbl.c functions
// Step 1: Initialize SMM protocol interfaces
// Locate SMM System Table (gSmst) at qword_10900
// Register communication handler
// Set up SMI entry/exit
// Step 2: CPU detection
// CPUID to determine CPU family (SNB/HSW/SKX/KNL/IVT)
// sub_3694, sub_36DC, sub_3710 used in dispatchers
// Step 3: Page table initialization
// InitPaging(&SmrrBase, &SmrrSize, &SmrrEnd) (sub_314C)
// - Gets SMRAM ranges via EFI_SMM_ACCESS2_PROTOCOL
// - Programs SMRR base/size MSRs
// - Sets up 4KB page tables in SMRAM
// Step 4: Long mode setup
// InitLongMode(EntryCount, ReservedBit) (sub_3394)
// - CPUID feature matching against dword_104C0+ et al
// - Writes "INTEL RSVD" string if reserved space
// Step 5: MP data initialization
// InitMp() (sub_2110)
// - Allocates mAcpiCpuData.MtrrTable (608 bytes)
// - Allocates mAcpiCpuData.GdtrProfile (10 bytes)
// - Allocates mAcpiCpuData.IdtrProfile (10 bytes)
// - Copies RegisterTable and PreSmmInitRegisterTable
// - Sets up mGdtForAp with combined GDT/IDT/ExcptHandler
// - Sets byte_10888 = 1 (GDT/IDT ready)
// Step 6: SMM features initialization
// Registers SmiHandlerDispatch() as SwSmiHandler
// Registers SmiHandlerFeatureMsr() for feature MSR access
// Step 7: MP wake and startup
// Start APs with StartupRoutine
// Each AP calls ProgramRegisterTable() for its register table entries
// Wait for all APs via mCpusExiting counter
}
/**
SmiHandlerDispatch - SMI Handler Dispatcher
Handles System Management Interrupts. Dispatches based on CPU model
detected by IsCpuSandyBridge(), IsCpuKnightsLanding(), IsCpuIvyTown().
For each CPU model, writes to model-specific MSR ranges:
SandyBridge (0x306F0): read/write 0x410xx MSR range
KnightsLanding (0x50650): read/write 0x4107C MSR
IvyTown (0x50670): read/write 0x411xx MSR range
Source: SmmFeatures.c
Reference: e:\\hs\\PurleySktPkg\\Override\\IA32FamilyCpuPkg\\PiSmmCpuDxeSmm\\SmmFeatures.c
@param[in] CpuIndex CPU index
@param[in] ReadWrite 0=read, 1=write
@return MSR value read or written
**/
UINT64
SmiHandlerDispatch (
UINT64 CpuIndex,
INT32 ReadWrite
)
{
// 0x3BD0
UINT64 MsrValue;
UINT64 MsrIndex;
MsrValue = 0;
// Check CPU model and dispatch to correct MSR handling
if (!IsCpuSandyBridge()) {
// Not SNB/HSW/SKX - try other models
if (IsCpuKnightsLanding()) {
AcquireSpinLock(&SmmFeatureLock); // sub_A0DC
if (ReadWrite == 0) {
// Read 0x4107C
MsrIndex = PciExpressAddress(...) | 0x4107C;
goto read_msr;
}
// Write to 0x41050/0x41054
// ...
} else if (IsCpuIvyTown()) {
// IVT MSR handling at 0x4115x - 0x4117x range
// ...
}
} else {
// SNB/HSW/SKX - use 0x860xx range
// ...
}
read_msr:
MsrValue = ReadMsr(MsrIndex);
ReleaseSpinLock(&SmmFeatureLock);
return MsrValue;
}
/**
SmmRestoreCpu - SMM S3 Resume Path
Called during S3 resume to return to PEI phase. Checks the S3 resume
state signature and uses either SwitchStack() or AsmDisablePaging64()
to transition back to the PEI S3 resume vector.
Signatures checked:
"SMM_S_64" (0x34365F33534D4D53) -> AsmDisablePaging64() path
"SMM_S_32" (0x32335F33534D4D53) -> SwitchStack() path
Source: PiSmmCpuDxeSmm.c
Reference: e:\\hs\\PurleySktPkg\\Override\\IA32FamilyCpuPkg\\PiSmmCpuDxeSmm\\PiSmmCpuDxeSmm.c
@param[in] ImageHandle Not used
@param[in] SystemTable Not used
**/
VOID
SmmRestoreCpu (
VOID
)
{
// 0x1C3C
SMM_S3_RESUME_STATE *S3State;
UINT64 Status;
DEBUG((DEBUG_INFO, "SmmRestoreCpu()\n"));
S3State = mSmmS3ResumeState; // qword_10690
if (S3State == NULL) {
DEBUG((DEBUG_INFO, "No context to return to PEI Phase\n"));
CpuDeadLoop();
}
if (S3State->Signature == SMM_S3_SIG_64) {
// 64-bit S3 resume using AsmDisablePaging64
// Save IDT, set up page tables, init exception handler
// ...
Status = SmmS3Init(); // sub_B45C
if (EFI_ERROR(Status)) {
DEBUG((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR(Status);
}
}
// Initialize interrupt state for APs
InitInterruptState(); // sub_524C
LaunchS3Resume(); // sub_1A90
// Program register table for BSP
ProgramRegisterTable(); // sub_4F70
// Set up return state
mCpusExiting = mNumberOfCpus - 1;
// Set return function
*((UINT64 *)mGdtIdtTable + 1) = mGdtBuffer; // GDT pointer
*((UINT64 *)mGdtIdtTable + 3) = (UINT64)sub_51CC; // Return function
// Start APs with SIPI
StartupAP((UINT32)mStartupRoutine);
while (mCpusExiting) {
CpuPause();
}
// Determine resume method based on signature
DEBUG((DEBUG_INFO, "SMM S3 Return CS = %x\n", S3State->ReturnCs));
DEBUG((DEBUG_INFO, "SMM S3 Return Entry Point = %x\n", S3State->ReturnEntry));
DEBUG((DEBUG_INFO, "SMM S3 Return Context1 = %x\n", S3State->ReturnCtx1));
DEBUG((DEBUG_INFO, "SMM S3 Return Context2 = %x\n", S3State->ReturnCtx2));
DEBUG((DEBUG_INFO, "SMM S3 Return Stack Pointer = %x\n", S3State->ReturnSp));
if (S3State->Signature == SMM_S3_SIG_32) {
DEBUG((DEBUG_INFO, "Call SwitchStack() to return to S3 Resume in PEI Phase\n"));
SwitchStack(
(VOID *)(UINTN)S3State->ReturnEntry,
(VOID *)(UINTN)S3State->ReturnCtx1,
(VOID *)(UINTN)S3State->ReturnCtx2,
(VOID *)(UINTN)S3State->ReturnSp
); // sub_8384
}
if (S3State->Signature == SMM_S3_SIG_64) {
DEBUG((DEBUG_INFO, "Call AsmDisablePaging64() to return to S3 Resume in PEI Phase\n"));
AsmDisablePaging64(
S3State->ReturnCs,
(UINT32)(UINTN)S3State->ReturnEntry,
(UINT32)(UINTN)S3State->ReturnCtx1,
(UINT32)(UINTN)S3State->ReturnCtx2,
(UINT32)(UINTN)S3State->ReturnSp
); // sub_5E0
}
DEBUG((DEBUG_INFO, "No context to return to PEI Phase\n"));
CpuDeadLoop();
}
/**
InitMp - Initialize Multi-Processor Data
Allocates and copies ACPI CPU data structures used for MP initialization:
- MTRR table (608 bytes per CPU)
- GDTR profile (10 bytes = GDT limit + base)
- IDTR profile (10 bytes = IDT limit + base)
- PreSmmInitRegisterTable (24 bytes per CPU entry)
- RegisterTable (24 bytes per CPU entry)
- Combined GDT/IDT/ExceptionHandler allocation
Source: PiSmmCpuDxeSmm.c
Reference: e:\\hs\\PurleySktPkg\\Override\\IA32FamilyCpuPkg\\PiSmmCpuDxeSmm\\PiSmmCpuDxeSmm.c
**/
UINT64
InitMp (
VOID
)
{
// 0x2110
ACPI_CPU_DATA *AcpiCpuData;
// Get ACPI CPU data via protocol
AcpiCpuData = (ACPI_CPU_DATA *)LocateProtocol(); // sub_8AA8
// Allocate and copy MTRR table
mMtrrTable = (UINT64)AllocatePool(6, 608); // sub_9C8C
CopyMem(mMtrrTable, *(AcpiCpuData->MtrrTable), 608);
// Allocate and copy GDTR profile
mGdtrProfile = (UINT64)AllocatePool(6, 10);
CopyMem(mGdtrProfile, AcpiCpuData->GdtrProfile, 10);
// Allocate and copy IDTR profile
mIdtrProfile = (UINT64)AllocatePool(6, 10);
CopyMem(mIdtrProfile, AcpiCpuData->IdtrProfile, 10);
// Allocate and copy PreSmmInitRegisterTable (24 bytes per CPU)
mPreSmmInitRegisterTable = (UINT64)AllocatePool(6, 24 * mNumberOfCpus);
CopyRegisterTable(mPreSmmInitRegisterTable, AcpiCpuData->PreSmmInitRegisterTable, mNumberOfCpus);
// Allocate and copy RegisterTable (24 bytes per CPU)
mRegisterTable = (UINT64)AllocatePool(6, 24 * mNumberOfCpus);
CopyRegisterTable(mRegisterTable, AcpiCpuData->RegisterTable, mNumberOfCpus);
// Allocate combined GDT/IDT/Exception handler region
mGdtForApAlloc = (UINT64)AllocatePool(6,
*(UINT16 *)mGdtrProfile + 2 + mGdtSize + *(UINT16 *)mIdtrProfile);
mIdtForApAlloc = mGdtForApAlloc + *(UINT16 *)mGdtrProfile + 1;
mExcptHandlerAlloc = mIdtForApAlloc + *(UINT16 *)mIdtrProfile + 1;
CopyMem(mGdtForApAlloc, *(UINT64 *)(mGdtrProfile + 2), *(UINT16 *)mGdtrProfile + 1);
CopyMem(mIdtForApAlloc, *(UINT64 *)(mIdtrProfile + 2), *(UINT16 *)mIdtrProfile + 1);
CopyMem(mExcptHandlerAlloc, mGdtForAp, mGdtSize);
mGdtIdtReady = 1;
DEBUG((64, "PcdCpuSmmCodeAccessCheckEnable = %d\n", 1));
mCodeAccessCheck = 1;
return 0;
}
/**
ProgramRegisterTable - Program CPU Register Table
Iterates through a CPU register table entry structure programming
MSRs and control registers as specified. Each entry is 24 bytes:
+0x00: UINT32 TableLength (number of entries)
+0x04: UINT32 MsrIndex
+0x08: UINT8 StartBit
+0x09: UINT8 BitsLength
+0x10: UINT64 Value
Supports entry types based on Count value:
1 = Program MSR via BitField read/modify/write
3 = Cache control (wbinvd/flush)
0 = Raw MSR write (when BitsLength < 0x40) or direct MSR write
Source: PiSmmCpuDxeSmm.c (MpService.c)
**/
UINT64
ProgramRegisterTable (
UINT32 *RegisterTableEntry
)
{
// 0x4F70
// Entry processing:
// Count == 1: BitField read -> modify -> write
// Read MSR, mask StartBit..StartBit+BitsLength-1, write Value
// Uses BitFieldRead64/Write64 then CR writes (sub_410=cr0, sub_420=cr3, etc.)
// Count == 3: Cache maintain (wbinvd or just clean)
// Count == 0 && BitsLength < 0x40:
// Look up MSR in mMsrSpinLocks, acquire lock, read/modify/write MSR, release
// Count == 0: direct write via __writemsr()
}
/**
InitPaging - Initialize SMM Page Tables and SMRR
Gets SMRAM ranges via EFI_SMM_ACCESS2_PROTOCOL, finds the lowest SMRAM
range above 0x100000, and programs SMRR base/size MSRs.
Source: X64/PageTbl.c
Reference: e:\\hs\\PurleySktPkg\\Override\\IA32FamilyCpuPkg\\PiSmmCpuDxeSmm\\X64\\PageTbl.c
@param[out] SmrrBase SMRR base address
@param[out] SmrrSize SMRR size
@param[out] SmrrEnd End of SMRAM range (IEDRAM end)
**/
UINT64
InitPaging (
UINT32 *SmrrBase,
UINT32 *SmrrSize,
INT32 *SmrrEnd
)
{
// 0x314C
// 1. Get SMM Access2 Protocol
// 2. Get SMRAM ranges (Status = EFI_BUFFER_TOO_SMALL expected)
// 3. Allocate SMRAM range buffer
// 4. Get SMRAM ranges from protocol
// 5. Find lowest available range >= 0x100000
// 6. Merge adjacent ranges
// 7. Program SmrrBase = range base, SmrrSize = merged size
// 8. Add 4MB IEDRAM padding to SmrrSize
// 9. Set mSmrrConfigured = 1
// 10. Log SMRR base/size
}
/**
DebugPrint - EDK2 DEBUG macro implementation
Writes debug output to serial port based on error level mask.
Detects debug level via CMOS port 0x70/0x71 register 0x4C.
The output is formatted via AsciiSPrint and written via SerialPortWrite.
Source: BaseDebugLibSerialPort\\DebugLib.c
Reference: e:\\hs\\MdePkg\\Library\\BaseDebugLibSerialPort\\DebugLib.c
**/
UINT8
DebugPrint (
UINT64 ErrorLevel,
CHAR8 *Format,
...
)
{
// 0x9AA4
// 1. Check if Format is NULL -> ASSERT
// 2. Read CMOS register 0x4C to get debug level
// 3. Check if ErrorLevel matches current debug mask
// 4. If matching: format string via AsciiSPrint, write to serial port
}
/**
AssertBreak - ASSERT macro implementation
Formats and prints "ASSERT [Module] File(Line): Expression\n"
then breaks.
Source: BaseDebugLibSerialPort\\DebugLib.c
**/
UINT64
AssertBreak (
UINT64 FileName,
UINT32 LineNumber,
UINT64 Expression
)
{
// 0x9B78
// AsciiSPrint("ASSERT [%a] %a(%d): %a\n", ...)
// Then break via SerialPortWrite
}
/**
PciExpressAddress - Convert PCI address to MMIO address
Validates that the address is in PCIe config space (bits 63-28 must be zero),
then adds the PCIe MMIO base address from gPciExpressBase (qword_10910).
Source: SmmPciExpressLib\\PciExpressLib.c
Reference: e:\\hs\\MdePkg\\Library\\SmmPciExpressLib\\PciExpressLib.c
@param[in] Address PCI bus/device/function/register address
@return MMIO address for PCIe config access
**/
UINT64
PciExpressAddress (
UINT64 Address
)
{
// 0x9BCC
if ((Address & ~0xFFFFFFF) != 0) {
ASSERT(((VOID *)0) == (VOID *)0); // ASSERT((Address & ~0xfffffff) == 0)
}
return Address + gPciExpressBase;
}
/**
AcquireSpinLock - Acquire spin lock with timeout
Waits in a loop for the spin lock to be released (value == 1),
then atomically acquires it (value = 2). Has a timeout based on
TSC and gTimerPeriod to detect deadlocks.
Source: BaseSynchronizationLib\\SynchronizationMsc.c
Reference: e:\\hs\\MdePkg\\Library\\BaseSynchronizationLib\\SynchronizationMsc.c
@param[in] SpinLock Pointer to spin lock value
**/
UINT64
AcquireSpinLock (
UINT64 SpinLock
)
{
// 0xA0DC
// Check if already acquired via IsSpinLockAcquired
// If not:
// StartTime = ReadTsc()
// Timeout = 10000000 * gTimerPeriod / 0xF4240
// while not acquired:
// CpuPause()
// if (elapsed >= Timeout) ASSERT
}
/**
ReleaseSpinLock - Release spin lock
Sets the spin lock value to 1 (released).
Source: BaseSynchronizationLib\\SynchronizationMsc.c
**/
UINT64
ReleaseSpinLock (
UINT64 SpinLock
)
{
// 0xA20C
// ASSERT(SpinLock != NULL)
// ASSERT(*SpinLock == 2 || *SpinLock == 1)
// *SpinLock = 1
}
/**
SmiHandlerFeatureMsr - SMM Feature MSR Read/Write
Handles reads and writes to SMM feature MSRs based on the detected
CPU model. Dispatches to correct MSR range:
- SandyBridge: 0x860xx range
- KnightsLanding: 0x410xx range (including 0x4107C for status)
- IvyTown: 0x411xx range (5 sub-ranges for 5 MSR indices)
Source: SmmFeatures.c
Reference: e:\\hs\\PurleySktPkg\\Override\\IA32FamilyCpuPkg\\PiSmmCpuDxeSmm\\SmmFeatures.c
@param[in] CpuIndex CPU index
@param[in] ReadWrite 0=read, 1=write
@param[in] Value MSR value (for writes)
@return written MSR value or read value
**/
UINT64
SmiHandlerFeatureMsr (
UINT64 CpuIndex,
INT32 ReadWrite,
UINT64 Value
)
{
// 0x4344
// Same dispatch pattern as SmiHandlerDispatch but
// specifically handles MSR read/write at:
// 0x41050/0x41054 (KNL write high/low)
// 0x41058 (KNL read)
// 0x4107C (KNL status)
// 0x41150-0x41174 (IVT per MSR index)
// 0x86050/0x86054/0x86058 (SNB)
}
/**
SendSmiIpi - Send SMI IPI to a processor
Sends an SMI IPI via the local APIC. For xAPIC mode writes to the
APIC ICR register. For x2APIC mode uses the x2APIC MSR (0x830).
Source: BaseXApicX2ApicLib\\BaseXApicX2ApicLib.c
Reference: e:\\hs\\UefiCpuPkg\\Library\\BaseXApicX2ApicLib\\BaseXApicX2ApicLib.c
@param[in] ApicId Destination APIC ID
@param[in] IpiType Type of IPI to send
**/
UINT64
SendSmiIpi (
UINT32 ApicId,
UINT32 IpiType
)
{
// 0xA598
// if (GetApicMode() == xAPIC) {
// ASSERT(ApicId <= 0xFF);
// Save eflags, cli
// Write APIC ICR register via memory-mapped APIC
// Wait for ICR to be accepted
// } else {
// // x2APIC mode: use MSR 0x830
// __writemsr(0x830, IpiType | (ApicId << 32));
// }
}
/**
StartupAP - Start Application Processor
Sends INIT-SIPI-SIPI sequence to start an AP at the given startup routine.
Source: BaseXApicX2ApicLib\\BaseXApicX2ApicLib.c
**/
UINT64
StartupAP (
UINT32 StartupRoutine,
...
)
{
// 0xA78C
// ASSERT(StartupRoutine < 0x100000)
// ASSERT((StartupRoutine & 0xFFF) == 0)
// Send INIT IPI (0xC4500 = delivery + INIT)
// Delay 10ms
// Send SIPI with startup page
// Delay 200us
// Send SIPI again
}
/**
GetCpuIndex - Get Current CPU Index
Determines the executing CPU's index by checking APIC ID against
the registered CPU list. Uses CPUID leaf 0xB (Extended Topology)
if available, or legacy CPUID leaf 1 for APIC ID.
@return CPU index (0-based)
**/
UINT64
GetCpuIndex (
VOID
)
{
// 0xA6C4
// if (GetApicMode() != 1) {
// return GetApicId(); // x2APIC: APIC ID == CPU index
// }
// CPUID leaf 0xB: get x2APIC ID
// If leaf 0xB available:
// CPUID(0xB, ...) -> get logical processor count and x2APIC ID
// else:
// CPUID(1, ...) -> get initial APIC ID from EBX[31:24]
}
//
// CPU Feature Checks
//
/**
IsCpuSandyBridge - Check for SNB/HSW/SKX Family CPU
Checks CPUID leaf 1 EAX register for family/model/stepping:
Sandy Bridge-EP: 0x306F0
Haswell-EP: 0x406F0 (263920)
Skylake-SP: 0x506F0 (329312)
@retval TRUE CPU is SNB-EP, HSW-EP, or SKX-SP
@retval FALSE CPU is not one of these
**/
BOOLEAN
IsCpuSandyBridge (
VOID
)
{
// 0x3694
INT32 CpuVersion;
Cpuid(1, &CpuVersion, 0, 0, 0); // sub_470
CpuVersion &= 0xFFF0FF0;
return (CpuVersion == CPU_FEATURE_SNB_EP ||
CpuVersion == CPU_FEATURE_HSW_EP ||
CpuVersion == CPU_FEATURE_SKX);
}
/**
IsCpuKnightsLanding - Check for KNL CPU
Checks CPUID leaf 1 for Knights Landing (0x50650 = 329296).
@retval TRUE CPU is KNL
@retval FALSE CPU is not KNL
**/
BOOLEAN
IsCpuKnightsLanding (
VOID
)
{
// 0x36DC
INT32 CpuVersion;
Cpuid(1, &CpuVersion, 0, 0, 0);
return (CpuVersion & 0xFFF0FF0) == CPU_FEATURE_KNL;
}
/**
IsCpuIvyTown - Check for IVT CPU
Checks CPUID leaf 1 for Ivy Town (0x50670 = 329328).
@retval TRUE CPU is IVT
@retval FALSE CPU is not IVT
**/
BOOLEAN
IsCpuIvyTown (
VOID
)
{
// 0x3710
INT32 CpuVersion;
Cpuid(1, &CpuVersion, 0, 0, 0);
return (CpuVersion & 0xFFF0FF0) == CPU_FEATURE_IVT;
}
/**
CpuDeadLoop - Infinite loop / Halt
Used for unrecoverable error conditions (no S3 resume context).
**/
VOID
CpuDeadLoop (
VOID
)
{
while (TRUE)
;
}