/**
* SmmControl.efi - SMM Control Driver for HR650X BIOS (Purley/PCH LBG)
*
* Decompiled from: HR650X_3647_AJAX_BIOS_ORIGINAL.pe_structured/pe_files/0115_SmmControl_3d5e22bb70ab/SmmControl.efi.i64
* MD5: 73fdb81ed9d9b5280a077140f6bcf6b8
* SHA256: 3d5e22bb70ab549bc25fbb775ebf517bbf578777201e8787bba5aa5fd6c9a2a5
*
* File: SmmControl.c
* Description: SMM Control Runtime DXE driver for PCH SMI management.
* Provides EFI_SMM_CONTROL2_PROTOCOL and EFI_SMM_COMMUNICATION_PROTOCOL
* interfaces for SMI generation, status handling, and communication.
*/
#include "SmmControl.h"
//
// ---------------------------------------------------------------------------
// Global Variables
// ---------------------------------------------------------------------------
//
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBootServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices_0 = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices_1 = NULL;
UINT64 gBootServices_0 = 0;
UINT16 gSmiIoPortBase = 0; // word_4458: SMI I/O port base address
EFI_SMM_CONTROL2_PROTOCOL *gSmmControl2 = NULL; // psub_18AC
EFI_SMM_COMMUNICATION_PROTOCOL *gSmmCommunication = NULL; // psub_1970
UINT32 gSignature = 0x63736D70; // 'pmsc' = 1668494441
EFI_HANDLE gSmmImageHandle = NULL;
VOID *gMmPciUsra = NULL; // qword_43E0
VOID *gHobList = NULL; // qword_43E8
VOID *gPcdDb = NULL; // qword_4408
VOID *gDsEntry = NULL; // qword_43D8
VOID *gDebugLib = NULL; // qword_43D0
UINT8 gDebugLevel = 0; // n113
VOID *gSmiHobData = NULL; // qword_43F0
UINT64 gSmiHobDataCount = 0; // qword_43F8
UINT64 gSmiControlAddress = 0; // qword_4438
UINTN gCmCpuCount = 0; // n2, initialized to 2
//
// Event handles
//
EFI_EVENT gEventRestoreTpl = NULL; // qword_43C0
EFI_EVENT gEventVirtualAddrChange = NULL; // qword_4440
EFI_EVENT gEventRuntimeReady = NULL; // qword_4450
EFI_EVENT gEventNotify = NULL; // qword_43B8
//
// ---------------------------------------------------------------------------
// Forward declarations of static functions
// ---------------------------------------------------------------------------
//
static
EFI_STATUS
SmiActivate (
VOID
);
static
EFI_STATUS
PciExpressInit (
VOID
);
static
VOID
SmiClearGpeStatus (
VOID
);
static
EFI_STATUS
GetDebugLibProtocol (
VOID
);
static
EFI_STATUS
LocateProtocol (
IN EFI_GUID *ProtocolGuid,
OUT VOID **Interface
);
static
BOOLEAN
CompareGuid (
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
);
static
UINT64
ReadUnaligned64 (
IN CONST VOID *Buffer
);
static
EFI_STATUS
GetConfigurationTable (
IN EFI_GUID *TableGuid,
OUT VOID **Table
);
//
// ===========================================================================
// I/O Port Helpers (wrappers around BaseIoLibIntrinsic with assertion)
// ===========================================================================
//
/**
* Read a 16-bit value from an I/O port (word-aligned).
*/
UINT16
IoRead16 (
IN UINT16 Port
)
{
ASSERT ((Port & 1) == 0);
return IoRead16 (Port);
}
/**
* Write a 16-bit value to an I/O port (word-aligned).
*/
UINT16
IoWrite16 (
IN UINT16 Port,
IN UINT16 Value
)
{
ASSERT ((Port & 1) == 0);
return IoWrite16 (Port, Value);
}
/**
* Read a 32-bit value from an I/O port (dword-aligned).
*/
UINT32
IoRead32 (
IN UINT16 Port
)
{
ASSERT ((Port & 3) == 0);
return IoRead32 (Port);
}
/**
* Write a 32-bit value to an I/O port (dword-aligned).
*/
UINT32
IoWrite32 (
IN UINT16 Port,
IN UINT32 Value
)
{
ASSERT ((Port & 3) == 0);
return IoWrite32 (Port, Value);
}
/**
* Read a 16-bit MMIO register from PCH LPC (word-aligned MMIO address).
*/
UINT16
MmioRead16 (
IN UINT16 *Address
)
{
ASSERT (((UINTN)Address & 1) == 0);
return *Address;
}
//
// ===========================================================================
// Debug Library Protocol (dynamically located)
// ===========================================================================
//
/**
* Initialize debug protocol -- locate EFI_DEBUG_PROTOCOL for debug output.
*/
EFI_STATUS
InitDebugLib (
VOID
)
{
EFI_STATUS Status;
UINT64 Pages;
if (gDebugLib != NULL) {
return EFI_SUCCESS;
}
//
// Check if BootServices is available and we have enough memory
//
if (gBootServices_0 != 0) {
Pages = gBootServices_0->AllocatePages (AllocateAnyPages, EfiBootServicesData, 31);
gBootServices_0->FreePages (Pages, 1);
if (Pages <= 0x10) {
//
// Low memory condition: try to locate the debug protocol
//
Status = LocateProtocol (&gEfiDebugProtocolGuid, NULL, &gDebugLib);
if (EFI_ERROR (Status)) {
gDebugLib = NULL;
}
return Status;
}
}
return EFI_SUCCESS;
}
/**
* Debug print -- conditionally output to debug console based on error level.
*/
VOID
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
EFI_DEBUG_PROTOCOL *DebugProtocol;
UINTN DebugLevel;
UINT8 CmosIndex;
UINT8 CmosValue;
UINTN AllowedLevels;
UINTN AllowedPrintLevel;
VA_LIST Va;
DebugProtocol = (EFI_DEBUG_PROTOCOL *)InitDebugLib ();
AllowedLevels = 0;
if (DebugProtocol != NULL) {
//
// Read CMOS diagnostic register to determine current debug level
//
CmosIndex = IoRead8 (0x70);
IoWrite8 (0x70, (CmosIndex & 0x80) | 0x4B);
DebugLevel = IoRead8 (0x71);
if (DebugLevel > 3) {
DebugLevel = 3;
}
switch (DebugLevel) {
case 1:
AllowedLevels = DEBUG_ERROR;
AllowedPrintLevel = DEBUG_ERROR;
break;
case 2:
AllowedLevels = (UINTN)-1;
AllowedPrintLevel = DEBUG_WARN;
break;
case 3:
AllowedLevels = (UINTN)-1;
AllowedPrintLevel = DEBUG_INFO;
break;
default:
AllowedLevels = (UINTN)-1;
AllowedPrintLevel = DEBUG_ERROR;
break;
}
if ((AllowedLevels & ErrorLevel) != 0) {
VA_START (Va, Format);
DebugProtocol->Print (ErrorLevel, Format, Va);
VA_END (Va);
}
}
}
/**
* Debug assert -- print assertion failure message.
*/
VOID
DebugAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
EFI_DEBUG_PROTOCOL *DebugProtocol;
DebugProtocol = (EFI_DEBUG_PROTOCOL *)InitDebugLib ();
if (DebugProtocol != NULL) {
DebugProtocol->Assert (FileName, LineNumber, Description);
}
}
//
// ===========================================================================
// Event Notification Callbacks
// ===========================================================================
//
/**
* Notification callback for TPL restore event -- clears BootServices reference.
*/
VOID
EFIAPI
OnRestoreTpl (
IN EFI_EVENT Event,
IN VOID *Context
)
{
gBootServices_0 = 0;
}
/**
* Notification callback for virtual address change -- unregisters the debug protocol.
*/
VOID
EFIAPI
OnVirtualAddressChange (
VOID
)
{
if (gDebugLib != NULL) {
gRuntimeServices_0->ConvertPointer (0, &gDebugLib);
}
}
/**
* Notification callback for runtime ready event -- unregisters runtime services pointer.
*/
VOID
EFIAPI
OnRuntimeReady (
VOID
)
{
gRuntimeServices_1->ConvertPointer (0, &gRuntimeServices_1);
}
/**
* Notification callback for SMI HOB cleanup -- frees HOB data on exit.
*/
VOID
EFIAPI
OnSmiHobCleanup (
VOID
)
{
UINT64 Index;
if (gSmiHobData != NULL && gSmiHobDataCount > 0) {
for (Index = 0; Index < gSmiHobDataCount; Index++) {
gRuntimeServices->ConvertPointer (0, (VOID **)((UINT8 *)gSmiHobData + 8 + Index * 16));
}
gRuntimeServices->ConvertPointer (0, &gSmiHobData);
}
}
//
// ===========================================================================
// Protocol Implementation
// ===========================================================================
//
/**
* SMM Communication Protocol: Communicate handler.
*
* Handles communication with SMM code via the SMM Communication Protocol.
* Validates the CommBuffer and CommSize, triggers SMI, and returns status.
*
* @param[in] This Pointer to the EFI_SMM_COMMUNICATION_PROTOCOL instance.
* @param[in,out] CommBuffer Pointer to the communication buffer.
* @param[in,out] CommSize Pointer to the size of the communication buffer.
*
* @retval EFI_SUCCESS SMI was triggered successfully.
* @retval EFI_INVALID_PARAMETER Invalid parameter was provided.
*/
EFI_STATUS
EFIAPI
SmmCommunicationCommunicate (
IN CONST EFI_SMM_COMMUNICATION_PROTOCOL *This,
IN OUT VOID *CommBuffer OPTIONAL,
IN OUT UINTN *CommSize OPTIONAL
)
{
//
// Defer to the non-periodic activation path
//
if (CommBuffer != NULL && CommSize != NULL) {
return SmmControl2Handler (
NULL,
(UINT8 *)CommBuffer,
(UINT8 *)CommSize,
FALSE,
FALSE
);
}
return EFI_INVALID_PARAMETER;
}
/**
* SMM Control2 Protocol: Activate/Trigger SMI.
*
* Triggers an SMI by:
* 1. Clearing PM1 status and SMI status
* 2. Setting the SMI control port to generate SMI
* 3. Writing the command/data ports (0xB3/0xB2)
*
* @param[in] This Pointer to the EFI_SMM_CONTROL2_PROTOCOL instance.
* @param[in,out] CommandPort Optional command port value written to 0xB3.
* @param[in,out] DataPort Optional data port value written to 0xB2.
* @param[in] Periodic Whether periodic activation is requested (not supported).
* @param[in] Activation TRUE to activate, FALSE to deactivate.
*
* @retval EFI_SUCCESS SMI was triggered.
* @retval EFI_INVALID_PARAMETER Periodic is TRUE (not supported).
* @retval EFI_DEVICE_ERROR Failed to clear status registers.
*/
EFI_STATUS
EFIAPI
SmmControl2Handler (
IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,
IN OUT UINT8 *CommandPort OPTIONAL,
IN OUT UINT8 *DataPort OPTIONAL,
IN BOOLEAN Periodic OPTIONAL,
IN BOOLEAN Activation OPTIONAL
)
{
EFI_STATUS Status;
UINT8 CommandValue;
UINT8 DataValue;
if (Periodic) {
DEBUG ((DEBUG_ERROR, "Invalid parameter\n"));
return EFI_INVALID_PARAMETER;
}
CommandValue = 0xFF;
DataValue = 0;
if (CommandPort != NULL) {
CommandValue = *CommandPort;
}
if (DataPort != NULL) {
DataValue = *DataPort;
}
Status = SmiActivate ();
if (EFI_ERROR (Status)) {
return Status;
}
//
// Clear SMI status and trigger SMI via control port
//
IoWrite32 (gSmiIoPortBase + 48, IoRead32 (gSmiIoPortBase + 48) | 0x21);
DEBUG ((
DEBUG_INFO,
"The SMI Control Port at address %x will be written to %x.\n",
gSmiIoPortBase + 48,
IoRead32 (gSmiIoPortBase + 48) | 0x21
));
IoWrite32 (gSmiIoPortBase + 48, IoRead32 (gSmiIoPortBase + 48) | 0x21);
//
// Write SMI command and data ports
//
IoWrite8 (0xB3, DataValue);
IoWrite8 (0xB2, CommandValue);
return EFI_SUCCESS;
}
/**
* SMM Control2 Protocol: Clear SMI status (empty -- deferred to activation).
*/
EFI_STATUS
EFIAPI
SmmControl2Clear (
IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,
IN BOOLEAN Periodic OPTIONAL
)
{
if (Periodic) {
return EFI_INVALID_PARAMETER;
}
return SmiActivate ();
}
//
// ===========================================================================
// SMI Activation and Status Handling
// ===========================================================================
//
/**
* Activate SMI -- clear PM1 and SMI status, enable SMI generation.
*/
EFI_STATUS
SmiActivate (
VOID
)
{
UINT16 IoPortBase;
UINT16 Pm1Status;
UINT16 SmiStatus;
IoPortBase = gSmiIoPortBase;
DEBUG ((
DEBUG_INFO,
"The PM1 Status Port at address %x will be written to %x.\n",
IoPortBase,
0x800
));
IoWrite16 (IoPortBase, 0x800);
SmiStatus = IoPortBase + 52;
DEBUG ((
DEBUG_INFO,
"The SMI Status Port at address %x will be written to %x.\n",
SmiStatus,
0x20
));
IoWrite32 (SmiStatus, 0x20);
DEBUG ((
DEBUG_INFO,
"The SMI Control Port at address %x will be written to %x.\n",
IoPortBase + 48,
IoRead32 (IoPortBase + 48) | 2
));
IoWrite32 (IoPortBase + 48, IoRead32 (IoPortBase + 48) | 2);
return EFI_SUCCESS;
}
//
// ===========================================================================
// GPE/SMI Status Initialization
// ===========================================================================
//
/**
* Initialize GPE and SMI status registers during entry.
*/
VOID
SmiClearGpeStatus (
VOID
)
{
UINT64 GpeCount;
UINT64 SmiCount;
PCM_PCI_SMI_CONTEXT SmiContext;
PCM_PCI_GPE_CONTEXT GpeContext;
UINT64 Index;
UINT16 GpeBase;
UINT16 SmiBase;
//
// Check TCO status; if not set, reset PM1/GPE status
//
if ((IoRead16 (gSmiIoPortBase + 4) & 1) == 0) {
//
// Reset PM1 status registers
//
IoWrite16 (gSmiIoPortBase, 0xFFFF);
IoWrite16 (gSmiIoPortBase + 2, 0);
IoWrite16 (gSmiIoPortBase + 4, 0);
//
// Clear GPE status
//
IoWrite32 (gSmiIoPortBase + 140, 0xFFFFFDFF);
IoWrite32 (gSmiIoPortBase + 156, 0x40000);
}
//
// Enable all GPIO GPEs
//
GpeContext = (PCM_PCI_SMI_CONTEXT *)GetGpioGpeInfo (&GpeCount);
if (GpeContext != NULL) {
for (Index = 0; Index < GpeCount; Index++) {
if (GpeContext[Index].GpeRegNum != -1) {
*(volatile UINT32 *)(GpeContext[Index].GpioBaseAddr | ((GpeContext[Index].GpeReg | 0xFD00) << 16)) = 0;
}
}
}
//
// Disable all GPIO SMIs
//
SmiContext = (PCM_PCI_SMI_CONTEXT *)GetGpioSmiInfo (&SmiCount);
if (SmiContext != NULL) {
for (Index = 0; Index < SmiCount; Index++) {
if (SmiContext[Index].SmiRegNum != -1) {
*(volatile UINT32 *)(SmiContext[Index].GpioBaseAddr | ((SmiContext[Index].SmiReg | 0xFD00) << 16)) = (UINT32)-1;
}
}
}
//
// Disable GPE and SMI events on the SMI I/O port
//
GpeBase = gSmiIoPortBase + 68;
SmiBase = gSmiIoPortBase + 52;
IoWrite32 (GpeBase, 0xFFFF);
IoWrite32 (SmiBase, 0xFFFFFFFF);
IoWrite32 (gSmiIoPortBase + 48, (IoRead32 (gSmiIoPortBase + 48) & 0x80) | 0x21);
}
//
// ===========================================================================
// PCH Support and Detection
// ===========================================================================
//
/**
* Get GPIO GPE information for this PCH SKU.
*
* @param[out] Count Number of GPE entries.
*
* @return Pointer to the GPE context array, or NULL if unsupported.
*/
VOID *
GetGpioGpeInfo (
OUT UINT64 *Count
)
{
UINT16 LpcDeviceId;
if (gCmCpuCount == 2) {
LpcDeviceId = MmioRead16 ((UINT16 *)(PchLpcMmioRead (0) + 2));
if (((LpcDeviceId + 24128) & 0xFF70) != 0) {
DEBUG ((DEBUG_ERROR, "Unsupported PCH SKU, LpcDeviceId: 0x%04x!\n", LpcDeviceId));
ASSERT (FALSE);
} else {
gCmCpuCount = 1;
}
}
if (gCmCpuCount == 1) {
*Count = 13;
return &gGpioGpeInfoTable;
}
*Count = 0;
return NULL;
}
/**
* Read a register via MM PCI from PCH LPC bridge.
*
* @param[in] Offset Register offset in LPC config space.
*
* @return 64-bit MMIO address for the register.
*/
UINT64
PchLpcMmioRead (
IN UINT16 Offset
)
{
UINT64 Address;
UINT32 Bar;
UINT32 Data;
Address = MmPciBase (
DEFAULT_PCI_BUS_NUMBER,
PCI_DEVICE_NUMBER_PCH_LPC,
PCI_FUNCTION_NUMBER_PCH_LPC,
0
);
//
// Build a temporary MMIO address to read from LPC
//
Data = MmioRead32 ((UINT32 *)(UINTN)Address);
return MmioRead32 ((UINT32 *)(UINTN)(Address + Offset));
}
//
// ===========================================================================
// PCD and Protocol Location Helpers
// ===========================================================================
//
/**
* Locate a protocol by GUID.
*/
EFI_STATUS
LocateProtocol (
IN EFI_GUID *ProtocolGuid,
OUT VOID **Interface
)
{
return gBootServices->LocateProtocol (ProtocolGuid, NULL, Interface);
}
/**
* Compare two GUIDs (via ReadUnaligned64).
*/
BOOLEAN
CompareGuid (
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
{
return ReadUnaligned64 (Guid1) == ReadUnaligned64 (Guid2) &&
ReadUnaligned64 ((UINT8 *)Guid1 + 8) == ReadUnaligned64 ((UINT8 *)Guid2 + 8);
}
/**
* Read unaligned 64-bit value (via BaseLib).
*/
UINT64
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return *(UINT64 *)Buffer;
}
/**
* Read unaligned 64-bit value at offset 8.
*/
UINT64
ReadUnaligned64At8 (
IN CONST VOID *Buffer
)
{
return ReadUnaligned64 ((UINT8 *)Buffer + 8);
}
//
// ===========================================================================
// Configuration Table Lookup
// ===========================================================================
//
/**
* Locate a configuration table by GUID.
*
* @param[in] TableGuid GUID of the table to find.
* @param[out] Table Pointer to receive the table pointer.
*
* @retval EFI_SUCCESS Table found.
* @retval EFI_NOT_FOUND Table not found.
* @retval EFI_INVALID_PARAMETER Invalid parameter.
*/
EFI_STATUS
GetConfigurationTable (
IN EFI_GUID *TableGuid,
OUT VOID **Table
)
{
EFI_STATUS Status;
UINTN Index;
EFI_CONFIGURATION_TABLE *ConfigTable;
if (TableGuid == NULL) {
ASSERT (TableGuid != NULL);
return EFI_INVALID_PARAMETER;
}
if (Table == NULL) {
ASSERT (Table != NULL);
return EFI_INVALID_PARAMETER;
}
*Table = NULL;
if (gSystemTable->NumberOfTableEntries == 0) {
return EFI_NOT_FOUND;
}
ConfigTable = (EFI_CONFIGURATION_TABLE *)gSystemTable->ConfigurationTable;
for (Index = 0; Index < gSystemTable->NumberOfTableEntries; Index++) {
if (CompareGuid (TableGuid, &ConfigTable[Index].VendorGuid)) {
*Table = ConfigTable[Index].VendorTable;
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
/**
* Initialize HOB list pointer from configuration table.
*/
EFI_STATUS
InitHobList (
VOID
)
{
EFI_STATUS Status;
if (gHobList == NULL) {
Status = GetConfigurationTable (&gEfiHobListGuid, &gHobList);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, ASSERT_EFI_ERROR (Status)));
ASSERT (!EFI_ERROR (Status));
}
if (gHobList == NULL) {
ASSERT (gHobList != NULL);
}
}
return EFI_SUCCESS;
}
/**
* Initialize PCD database pointer.
*/
EFI_STATUS
InitPcdDb (
VOID
)
{
EFI_STATUS Status;
if (gPcdDb == NULL) {
Status = LocateProtocol (&gEfiPcdProtocolGuid, &gPcdDb);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, ASSERT_EFI_ERROR (Status)));
ASSERT (!EFI_ERROR (Status));
}
if (gPcdDb == NULL) {
ASSERT (gPcdDb != NULL);
}
}
return EFI_SUCCESS;
}
/**
* Initialize MM PCI base library protocol.
*/
EFI_STATUS
InitMmPciBase (
VOID
)
{
EFI_STATUS Status;
//
// Locate MM_PCI_USRA protocol if needed
//
if (gMmPciUsra == NULL) {
Status = gBootServices->LocateProtocol (&gEfiMmPciUsraProtocolGuid, NULL, &gMmPciUsra);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, ASSERT_EFI_ERROR (Status)));
ASSERT (!EFI_ERROR (Status));
}
if (gMmPciUsra == NULL) {
ASSERT (gMmPciUsra != NULL);
}
}
return EFI_SUCCESS;
}
/**
* Get the current PCD pointer via the PCD protocol.
*/
EFI_STATUS
GetPcdProtocol (
VOID
)
{
return InitPcdDb ();
}
//
// ===========================================================================
// Driver Entry Point and Initialization
// ===========================================================================
//
/**
* UEFI Driver Entry Point.
*
* Initializes global state, locates protocols, registers events, and
* installs SMM Control2 and SMM Communication protocols.
*/
EFI_STATUS
EFIAPI
SmmControlDriverEntryInit (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_BOOT_SERVICES *BootServices;
SMM_CONTROL_DRIVER_PRIVATE *Private;
VOID *Registration;
gImageHandle = ImageHandle;
gSystemTable = SystemTable;
gBootServices = SystemTable->BootServices;
gRuntimeServices = SystemTable->RuntimeServices;
//
// Assert required pointers
//
ASSERT (gImageHandle != NULL);
ASSERT (gSystemTable != NULL);
ASSERT (gBootServices != NULL);
ASSERT (gRuntimeServices != NULL);
//
// Initialize the PCH SMI I/O base address from the HOB
// and configure the SMI I/O ports
//
Status = SmmControlDriverEntryInit ();
if (EFI_ERROR (Status)) {
goto Unload;
}
return Status;
Unload:
SmmControlDriverUnload ();
return Status;
}
/**
* Driver initialization after entry point (main init body).
*
* Determines the SMI I/O port base from PCH LPC decode, sets up protocol
* interfaces, registers SMI control events, and initializes GPIO/SMI status.
*/
EFI_STATUS
SmmControlDriverEntryInit (
VOID
)
{
EFI_STATUS Status;
UINT16 LpcDecode;
UINT16 IoPortBase;
UINT64 SmiControlAddr;
DEBUG ((DEBUG_INFO, "SmmControlDriverEntryInit() Start\n"));
Status = EFI_SUCCESS;
//
// Read LPC I/O decode registers via MMIO to get SMI port base
//
IoPortBase = MmioRead16 ((UINT16 *)(PchLpcMmioRead (2)));
LpcDecode = MmioRead16 ((UINT16 *)(PchLpcMmioRead (2) + 64));
if (MmioRead16 ((UINT16 *)(UINTN)(PchLpcMmioRead (0) + 64)) == 0xFFFF) {
//
// PCH not initialized yet -- should not happen
//
ASSERT (FALSE);
gSmiIoPortBase = LpcDecode & 0xFFFC;
} else {
gSmiIoPortBase = LpcDecode & 0xFFFC;
}
if (gSmiIoPortBase == 0) {
DEBUG ((DEBUG_ERROR, "SMI I/O port base is zero -- PCH not initialized.\n"));
return EFI_NOT_READY;
}
//
// Initialize protocol structures
//
gSmiControlAddress = 0;
gSmmControl2 = &gSmmControl2Protocol;
gSignature = SIGNATURE_32 ('p', 'm', 's', 'c');
gSmmCommunication = &gSmmCommunicationProtocol;
gSmmImageHandle = ImageHandle;
//
// Register SMI HOB notification protocol
//
BootServices->RegisterProtocolNotify (
&gSmiHobGuid,
&gSmiImageHandle,
&gSmmControl2,
NULL
);
//
// Register EFI virtual address change event
//
Status = gBootServices->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
OnVirtualAddrChange,
NULL,
&gEventVirtualAddrChange
);
ASSERT_EFI_ERROR (Status);
//
// Initialize GPIO/SMI status
//
SmiInitGpe ();
DEBUG ((DEBUG_INFO, "SmmControlDriverEntryInit() End\n"));
return Status;
}
/**
* Driver unload handler.
*
* Frees allocated resources including SMI HOB data, event handles,
* and protocol interfaces.
*/
EFI_STATUS
SmmControlDriverUnload (
VOID
)
{
//
// Free SMI HOB data if allocated
//
if (gSmiHobData != NULL) {
OnSmiHobCleanup ();
}
//
// Close all registered events
//
if (gEventVirtualAddrChange != NULL) {
gBootServices->CloseEvent (gEventVirtualAddrChange);
}
if (gEventRestoreTpl != NULL) {
gBootServices->CloseEvent (gEventRestoreTpl);
}
if (gEventRuntimeReady != NULL) {
gBootServices->CloseEvent (gEventRuntimeReady);
}
if (gEventNotify != NULL) {
gBootServices->CloseEvent (gEventNotify);
}
//
// Free PciExpress protocol registration
//
if (gPcdDb != NULL) {
gBootServices->CloseEvent ((EFI_EVENT)gPcdDb);
}
return EFI_SUCCESS;
}
//
// ===========================================================================
// PCI Express Library Initialization
// ===========================================================================
//
/**
* Initialize PCI Express library -- locate and register protocol notify
* for PCI Express protocol.
*/
EFI_STATUS
PciExpressInit (
VOID
)
{
EFI_STATUS Status;
EFI_EVENT Event;
//
// Register for protocol notify on the MM-PCI USRA protocol
//
Status = gBootServices->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
OnPciExpressProtocolInstall,
NULL,
&Event
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, ASSERT_EFI_ERROR (Status)));
ASSERT (!EFI_ERROR (Status));
}
return Status;
}