/** @file
LegacySerialRedirection DXE Driver - Reconstructed Source
This DXE driver enables legacy serial redirection for text console I/O.
It configures the SIO (Super I/O) UART hardware via PCI configuration
space, registers SMM callbacks for runtime notification, and publishes
the Legacy Serial Redirection protocol.
Original source path: e:\hs\AmiModulePkg\LegacySerialRedirection\LegacySredir.c
Build: DEBUG_VS2015 X64, HR6N0XMLK platform
Binary: LegacySredir.efi (Lenovo HR650X BIOS)
Copyright (c) Lenovo. All rights reserved.
**/
#include <Uefi.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/BaseLib.h>
#include <Library/HobLib.h>
#include "LegacySredir.h"
//=============================================================================
// Global Variables
//=============================================================================
//
// UEFI Boot/Runtime Services Table pointers
//
EFI_BOOT_SERVICES *gBS; // 0x2400
EFI_RUNTIME_SERVICES *gRT; // 0x2408
EFI_SYSTEM_TABLE *gST; // 0x2410
//
// SIO Protocol interface (resolved from protocol GUID at 0x2260)
// Used for Super I/O register access via PCI configuration space.
//
VOID *gSioProtocol; // 0x2390
//
// SMM protocols
//
VOID *gSmmSwDispatch2Protocol; // 0x2378 - GUID 0x2290
VOID *gSmmReadyToBootProtocol; // 0x23C0 - GUID 0x22E0
VOID *gSmmBase2Protocol; // 0x23B0 - GUID 0x2280
//
// Debug Mask protocol (GUID at .rdata 0x2240)
//
VOID *gDebugMaskProtocol; // 0x23F0
//
// SIO data pointer and size (returned from SIO protocol Open at 3:7:9)
//
VOID *gSioDataPtr; // 0x2380
UINTN gSioDataSize; // 0x2368
//
// HOB list pointer (from DXE HOB Library)
//
VOID *gHobList; // 0x23F8
//
// SMM communication buffer (32 bytes, allocated at entry)
//
VOID *gCommunicationBuffer; // 0x2388
VOID *gSmmCommunicationBuffer; // 0x2430
//
// SIO PCI MMIO base address (calculated from PCI BAR)
//
UINT64 gSioPciMmioBase; // 0x2438
//
// SIO MMIO address and limit (from InitPchRcConfiguration)
//
UINT64 gSioMmioAddress; // 0x23B8
UINT64 gSioMmioLimit; // 0x2428
//
// UART Configuration structure (44 bytes at 0x2440)
//
SIO_UART_CONFIG gUartConfig;
//
// Default baud rate reference (1843200 = 115200 * 16)
//
UINTN gDefaultBaudRate; // 0x23A0
//
// State flags
//
BOOLEAN gSioSetupDone; // 0x2398
BOOLEAN gSmmNotifyDone; // 0x2399
BOOLEAN gPchRcConfigured; // 0x2371
BOOLEAN gSmmReadyToBootRegistered; // 0x2370
BOOLEAN gInitDone; // 0x2360
BOOLEAN gLegacyRedirRegistered; // 0x23A8
//
// UART configuration flags (bitfield)
//
UINT32 gUartConfigFlags; // 0x239C
//
// Extra UART config fields
//
UINT16 gWord2444; // 0x2444
UINT64 gQword2446; // 0x2446
//
// GUID structures for HOB matching
//
EFI_GUID gHobGuid1; // 0x22F0
EFI_GUID gHobGuid2; // 0x22F8
//=============================================================================
// Utility Functions
//=============================================================================
/**
Read a UINT64 from a potentially unaligned address.
Address: 0x1C38
@param[in] Buffer Pointer to read from.
@return The UINT64 value at Buffer.
**/
UINT64
EFIAPI
ReadUnaligned64 (
IN VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return *(UINT64 *)Buffer;
}
/**
Return the current debug level mask.
Address: 0x1B78
Reads CMOS register 0x4B to determine the debug verbosity level.
@return Debug level mask (DEBUG_ERROR | DEBUG_WARN typically).
**/
UINTN
EFIAPI
DebugClearMemory (
VOID
)
{
UINT8 DebugReg;
IoWrite8 (0x70, (IoRead8 (0x70) & 0x80) | 0x4B);
DebugReg = IoRead8 (0x71);
if (DebugReg > 3) {
if (DebugReg == 0) {
DebugReg = (UINT8)((MmioRead8 (0xFDAF0490) & 2) | 1);
}
}
if ((UINT8)(DebugReg - 1) > 0xFD) {
return 0;
}
if (DebugReg == 1) {
return DEBUG_ERROR;
}
return DEBUG_ERROR | DEBUG_WARN;
}
/**
Locate the Debug Mask protocol interface.
Address: 0x1998
@return Pointer to DebugMaskProtocol, or NULL.
**/
VOID *
EFIAPI
GetDebugMaskInterface (
VOID
)
{
EFI_STATUS Status;
VOID *Interface;
UINT64 Count;
if (gDebugMaskProtocol != NULL) {
return gDebugMaskProtocol;
}
Status = gBS->GetNextMonotonicCount (&Count);
if (Count <= 0x10) {
Status = gBS->LocateProtocol (
&gDebugMaskProtocolGuid,
NULL,
&Interface
);
if (EFI_ERROR (Status)) {
Interface = NULL;
}
gDebugMaskProtocol = Interface;
}
return gDebugMaskProtocol;
}
/**
Conditionally print a debug message.
Address: 0x1A18
Only prints if the debug mask protocol is available and the error
level matches the current debug level.
@param[in] ErrorLevel Debug error level.
@param[in] Format Format string.
@param[in] ... Variable arguments.
**/
VOID
EFIAPI
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
UINTN CurrentLevel;
VA_LIST VaList;
VA_START (VaList, Format);
if (GetDebugMaskInterface () == NULL) {
VA_END (VaList);
return;
}
CurrentLevel = DebugClearMemory ();
if ((CurrentLevel & ErrorLevel) != 0) {
//
// Forward to DebugMaskProtocol DebugPrint handler
//
}
VA_END (VaList);
}
/**
Process an assertion failure.
Address: 0x1A60
Reports the assertion via the DebugMaskProtocol callback.
@param[in] FileName Source file name.
@param[in] LineNumber Source line number.
@param[in] Description Assertion description.
**/
VOID
EFIAPI
DebugAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
VOID *Interface;
Interface = GetDebugMaskInterface ();
if (Interface != NULL) {
//
// Call DebugMaskProtocol.AssertCallback (at offset 8)
//
((VOID (*)(CONST CHAR8 *, UINTN, CONST CHAR8 *))Interface)(
FileName,
LineNumber,
Description
);
}
}
/**
Compare two HOB GUIDs (uses unaligned 64-bit comparison).
Address: 0x1BC8
@param[in] HobEntry Pointer to a HOB entry.
@return TRUE if the GUID at the fix offset matches HobEntry.
**/
BOOLEAN
EFIAPI
HobGuidMatch (
IN VOID *HobEntry
)
{
EFI_GUID *EntryGuid;
EntryGuid = (EFI_GUID *)HobEntry;
return (ReadUnaligned64 (&gHobGuid1) == ReadUnaligned64 (EntryGuid) &&
ReadUnaligned64 ((UINT8 *)&gHobGuid1 + 8) == ReadUnaligned64 ((UINT8 *)EntryGuid + 8));
}
/**
Get the HOB list from the system table configuration table.
Address: 0x1AA0
Scans the configuration table for matching HOB GUID.
@return Pointer to the HOB list, or NULL.
**/
VOID *
EFIAPI
GetSystemHobList (
VOID
)
{
UINTN Index;
EFI_GUID *GuidTable;
if (gHobList != NULL) {
return gHobList;
}
gHobList = NULL;
if (gST->NumberOfTableEntries > 0) {
GuidTable = (EFI_GUID *)gST->ConfigurationTable;
for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
if (HobGuidMatch ((UINT8 *)GuidTable + Index * 24)) {
gHobList = *(VOID **)((UINT8 *)GuidTable + Index * 24 + 16);
break;
}
}
if (gHobList == NULL) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = Not found)\n"));
ASSERT_EFI_ERROR (EFI_NOT_FOUND);
}
}
if (gHobList == NULL) {
ASSERT_EFI_ERROR (EFI_NOT_FOUND);
}
return gHobList;
}
//=============================================================================
// Hardware I/O Helpers
//=============================================================================
/**
Write a byte to either I/O port or memory-mapped register.
Address: 0x112C
Determines register width from HIBYTE(gDword2468):
4 = 32-bit MMIO, 2 = 16-bit MMIO, 1 = 8-bit MMIO.
@param[in] Address Base address (I/O port or MMIO).
@param[in] MemoryMapped TRUE for MMIO, FALSE for I/O.
@param[in] Offset Register offset.
@param[in] Value Byte value to write.
@return EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
IoOrMemWrite (
IN UINTN Address,
IN BOOLEAN MemoryMapped,
IN UINT32 Offset,
IN UINT8 Value
)
{
UINT8 RegWidth;
RegWidth = (UINT8)(gUartConfig.RegisterWidth);
if (MemoryMapped) {
switch (RegWidth) {
case 4:
*(volatile UINT32 *)(Address + 4 * Offset) = Value;
break;
case 2:
*(volatile UINT16 *)(Address + 2 * Offset) = Value;
break;
default:
*(volatile UINT8 *)(Address + Offset) = Value;
break;
}
} else {
IoWrite8 ((UINT16)(Address + Offset), Value);
}
return EFI_SUCCESS;
}
/**
Initialize UART configuration registers.
Address: 0x130C (largest function, 1675 bytes)
Reads the "Setup" and "TerminalSerialVar" UEFI variables to
configure the SIO_UART_CONFIG structure (baud, data bits, parity).
@param[out] UartConfig The UART config structure to initialize.
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
UartInitConfigRegisters (
OUT SIO_UART_CONFIG *UartConfig
)
{
//
// Reads:
// gRT->GetVariable (L"Setup", &gSetupVarGuid, ...)
// gRT->GetVariable (L"TerminalSerialVar", &gTerminalSerialVarGuid, ...)
//
// Parses serial config from variable data (baud, data bits, stop,
// parity) and populates UartConfig structure.
//
// Sets gUartConfigFlags, gUartConfig, gXmmword2450, gQword2460,
// gDword2468 register width field.
//
return EFI_SUCCESS;
}
/**
Program SIO UART registers with baud rate divisor and line control.
Address: 0x830
Calculates the baud rate divisor from gDefaultBaudRate (1843200)
and programs it into the UART registers via I/O or memory-mapped
access depending on whether Config->DataBuffer is non-NULL.
@param[in] Config SIO_UART_CONFIG describing the UART.
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
SioUartRegisterSetup (
IN SIO_UART_CONFIG *Config
)
{
UINT16 Divisor;
if (gDefaultBaudRate == 0) {
gDefaultBaudRate = 1843200;
}
Divisor = (UINT16)(gDefaultBaudRate / (16 * Config->UartCount));
if (gDefaultBaudRate % (16 * Config->UartCount)) {
Divisor++;
}
if (Divisor == 0 || (Divisor & 0xFFFF0000) != 0) {
return EFI_INVALID_PARAMETER;
}
//
// Set Divisor Latch Access Bit (DLAB = 0x80)
// Write divisor low/high, then clear DLAB
//
if (Config->DataBuffer != NULL) {
//
// Memory-mapped register write
//
if (Config->RegisterWidth == 4) {
*(UINT32 *)(Config->DataBuffer + 12) = 0x80;
*(UINT32 *)Config->DataBuffer = Divisor & 0xFF;
*(UINT32 *)(Config->DataBuffer + 4) = Divisor >> 8;
*(UINT32 *)(Config->DataBuffer + 12) = 0;
} else if (Config->RegisterWidth == 2) {
*(UINT16 *)(Config->DataBuffer + 6) = 0x80;
*(UINT16 *)Config->DataBuffer = Divisor & 0xFF;
*(UINT16 *)(Config->DataBuffer + 2) = Divisor >> 8;
*(UINT16 *)(Config->DataBuffer + 6) = 0;
} else {
*(UINT8 *)(Config->DataBuffer + 3) = 0x80;
*(UINT16 *)Config->DataBuffer = Divisor;
*(UINT8 *)(Config->DataBuffer + 3) = 0;
}
} else {
//
// Direct I/O port write
//
IoWrite8 ((UINT16)Config->IoPort + 3, 0x80);
IoWrite8 ((UINT16)Config->IoPort, (UINT8)Divisor);
IoWrite8 ((UINT16)Config->IoPort + 1, (UINT8)(Divisor >> 8));
IoWrite8 ((UINT16)Config->IoPort + 3, 0);
}
return EFI_SUCCESS;
}
/**
Initialize PCH RC configuration for SIO MMIO space.
Address: 0x608
Reads the "PchRcConfiguration" UEFI variable. If present with valid
data, allocates MMIO space (901120 * 0x4000) via a PCH protocol.
Otherwise calculates MMIO base from internal loc_40E/loc_413 offsets.
Sets gSioPciMmioBase, gSioMmioAddress, gSioMmioLimit, gUartConfig.
Marks gPchRcConfigured = TRUE on success.
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
InitPchRcConfiguration (
VOID
)
{
//
// If loc_40E is 0, return EFI_NOT_STARTED.
// Attempt to read the "PchRcConfiguration" variable (size 1495).
// If variable has config bytes 83 and 197 set to 1:
// - Allocate 0xDC000 * 0x4000 via PchRcProtocol
// - Write SIO data pointer to MMIO
// Otherwise:
// - Calculate base from internal offsets
// - Write PCI config for MMIO BAR
// - Copy SIO data into MMIO
//
// Calculate gSioPciMmioBase = gSioMmioAddress + *(UINT16*)(gSioMmioAddress + 11)
// Copy UART config from MMIO to gUartConfig
//
return EFI_SUCCESS;
}
/**
Search PCI space for $SBC/$SBF signatures and program SIO UART.
Address: 0x43C
Searches PCI configuration space (from PciStart through 0xF0000)
for "$SBC" and "$SBF" signature markers. When found, writes the
UART config data: (UartConfig >> 4) & 0xF000, UartConfig, 31.
@param[in] UartConfig UART configuration value.
@param[in] PciStart Starting offset in PCI config space.
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
SioSetupRegisterDevice (
IN UINT64 UartConfig,
IN UINTN PciStart
)
{
EFI_STATUS Status;
UINTN Offset;
UINTN SearchPtr;
if (gSioSetupDone) {
return EFI_SUCCESS;
}
if (gSioProtocol == NULL) {
Status = gBS->LocateProtocol (&gSioProtocolGuid, NULL, &gSioProtocol);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Open PCI config space at offset 8
//
Status = gSioProtocol->OpenPciConfig (8, &Offset);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// Search for $SBC signature, write config
//
for (SearchPtr = Offset; SearchPtr < 0xF0000; SearchPtr++) {
UINTN CmpPtr;
for (CmpPtr = SearchPtr; CmpPtr < SearchPtr + 4; CmpPtr++) {
if (*(UINT8 *)CmpPtr != "$SBC"[CmpPtr - SearchPtr]) {
break;
}
}
if ((CmpPtr - SearchPtr) >= 4) {
*(UINT16 *)(SearchPtr + 4) = (UartConfig >> 4) & 0xF000;
*(UINT16 *)(SearchPtr + 6) = (UINT16)UartConfig;
*(UINT16 *)(SearchPtr + 8) = 31;
Offset = SearchPtr + 4;
break;
}
}
//
// Write back the offset
//
Status = gSioProtocol->WritePciConfig (8, &Offset);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// Search for $SBF signature, write same config
//
for (SearchPtr = Offset; SearchPtr < 0xF0000; SearchPtr++) {
UINTN CmpPtr;
for (CmpPtr = SearchPtr; CmpPtr < SearchPtr + 4; CmpPtr++) {
if (*(UINT8 *)CmpPtr != "$SBF"[CmpPtr - SearchPtr]) {
break;
}
}
if ((CmpPtr - SearchPtr) >= 4) {
*(UINT16 *)(SearchPtr + 4) = (UartConfig >> 4) & 0xF000;
*(UINT16 *)(SearchPtr + 6) = (UINT16)UartConfig;
*(UINT16 *)(SearchPtr + 8) = 31;
Offset = SearchPtr + 4;
break;
}
}
//
// Close PCI config space
//
gSioProtocol->ClosePciConfig ();
gSioSetupDone = TRUE;
return EFI_SUCCESS;
}
/**
Notify SMM runtime services during boot.
Address: 0x954
Builds an SMM communication buffer with GUID {6BBFF0F0-424B-A60B-
0B5A-8F1BE063E7C7} and sends it via SMM Communication Protocol.
@param[in] Context Context value (gQword2446).
**/
VOID
EFIAPI
SmmRuntimeServicesNotify (
IN UINT64 Context
)
{
EFI_STATUS Status;
UINT32 NotifyGuid[4];
VOID *SmmCommProtocol;
if (gSmmNotifyDone) {
return;
}
Status = gBS->LocateProtocol (&gSmmCommunicationGuid, NULL, &SmmCommProtocol);
if (EFI_ERROR (Status)) {
return;
}
//
// Build notification GUID
//
NotifyGuid[0] = 0x6BBFF0F0;
NotifyGuid[1] = 0x424BA60B;
NotifyGuid[2] = 0x0B5A8F1B;
NotifyGuid[3] = 0xE063E7C7;
CopyMem (gCommunicationBuffer, NotifyGuid, 16);
*(UINT64 *)((UINT8 *)gCommunicationBuffer + 16) = 8;
CopyMem ((UINT8 *)gCommunicationBuffer + 24, &Context, sizeof (UINT64));
Status = SmmCommProtocol->Communicate (
SmmCommProtocol,
gCommunicationBuffer,
&gSmmCommunicationBuffer
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
gSmmNotifyDone = TRUE;
}
//=============================================================================
// Main Driver Functions
//=============================================================================
/**
Register Legacy Serial Redirection with SMM SW Dispatch 2.
Address: 0xA50 (off_2310[0] = this function)
Main initialization: locates SMM protocols, initializes UART config,
sets up PCH RC, searches SIO, and registers with SMM SW Dispatch 2.
@param[in] Context Unused.
@param[in] SmmContext Context for SMM registration.
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
LegacySerialRedirectionRegister (
IN VOID *Context,
IN UINTN SmmContext
)
{
EFI_STATUS Status;
if (gLegacyRedirRegistered) {
return EFI_NOT_STARTED;
}
//
// Locate SMM SW Dispatch 2 Protocol
//
if (gSmmSwDispatch2Protocol == NULL) {
Status = gBS->LocateProtocol (
&gSmmSwDispatch2Guid,
NULL,
&gSmmSwDispatch2Protocol
);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// First-time init: UART config, PCH RC, SMM notify
//
if (!gInitDone) {
Status = UartInitConfigRegisters (&gUartConfig);
if (EFI_ERROR (Status)) {
return Status;
}
gUartConfigFlags |= 0x390;
if (gQword2446 != 0) {
gUartConfigFlags |= 4;
}
gUartConfig.Flags = gUartConfigFlags;
if (!gPchRcConfigured) {
Status = InitPchRcConfiguration ();
if (EFI_ERROR (Status)) {
return Status;
}
}
SmmRuntimeServicesNotify (gQword2446);
gInitDone = TRUE;
}
//
// Setup SIO UART registers and register with SMM SW Dispatch 2
//
if (!gSmmReadyToBootRegistered ||
!EFI_ERROR (SioSetupRegisterDevice (gSioPciMmioBase, SmmContext)))
{
UINTN SwSmiConfig[6];
ZeroMem (SwSmiConfig, sizeof (SwSmiConfig));
SwSmiConfig[0] = 0;
SwSmiConfig[2] = 10;
SwSmiConfig[4] = 5;
//
// Register first SMI handler
//
gSmmSwDispatch2Protocol->Register (
gSmmSwDispatch2Protocol,
gSioMmioAddress >> 4,
*(UINT16 *)(gSioMmioLimit + 9),
SwSmiConfig,
0,
0
);
//
// Register second SMI handler (different SwSmiInputValue)
//
SwSmiConfig[0] = 1;
Status = gSmmSwDispatch2Protocol->Register (
gSmmSwDispatch2Protocol,
gSioMmioAddress >> 4,
*(UINT16 *)(gSioMmioLimit + 9),
SwSmiConfig,
0,
0
);
gLegacyRedirRegistered = TRUE;
}
return Status;
}
/**
Unregister Legacy Serial Redirection from SMM SW Dispatch 2.
Address: 0xC84 (off_2310[1] = this function)
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
LegacySerialRedirectionUnregister (
VOID
)
{
EFI_STATUS Status;
UINTN SwSmiConfig[6];
if (!gLegacyRedirRegistered) {
return EFI_NOT_STARTED;
}
if (gSmmSwDispatch2Protocol == NULL) {
Status = gBS->LocateProtocol (
&gSmmSwDispatch2Guid,
NULL,
&gSmmSwDispatch2Protocol
);
if (EFI_ERROR (Status)) {
return Status;
}
}
if (gSioMmioAddress == 0) {
return EFI_NOT_STARTED;
}
//
// Use unregister action (SwSmiConfig[0] = 2)
//
ZeroMem (SwSmiConfig, sizeof (SwSmiConfig));
SwSmiConfig[0] = 2;
Status = gSmmSwDispatch2Protocol->Register (
gSmmSwDispatch2Protocol,
gSioMmioAddress >> 4,
*(UINT16 *)(gSioMmioLimit + 9),
SwSmiConfig,
0,
0
);
gLegacyRedirRegistered = FALSE;
return Status;
}
/**
Register SMM Ready To Boot callback and invoke immediately.
Address: 0xD48
@return EFI_STATUS from the callback invocation.
**/
EFI_STATUS
EFIAPI
SmmReadyToBootCallbackRegister (
VOID
)
{
EFI_STATUS Status;
if (gSmmReadyToBootProtocol == NULL) {
Status = gBS->LocateProtocol (
&gSmmReadyToBootGuid,
NULL,
&gSmmReadyToBootProtocol
);
if (EFI_ERROR (Status)) {
return Status;
}
}
gSmmReadyToBootRegistered = TRUE;
//
// Invoke the ReadyToBoot callback immediately
//
return ((EFI_STATUS (*)(VOID *))gSmmReadyToBootProtocol)(gSmmReadyToBootProtocol);
}
/**
SMM SW Dispatch 2 callback handler.
Address: 0xD94
Called when an SMI is triggered for SW Dispatch 2.
Re-initializes UART config and notifies SMM runtime services.
@param[in] Context Restore TPL context.
**/
VOID
EFIAPI
SmmSwDispatch2Callback (
IN UINTN Context
)
{
if (!EFI_ERROR (UartInitConfigRegisters (&gUartConfig))) {
SmmRuntimeServicesNotify (gQword2446);
gBS->RestoreTpl (Context);
} else {
SmmRuntimeServicesNotify (0);
}
}
/**
Communicate with SMM Foundation and decide ReadyToBoot callback.
Address: 0xDDC
Uses SMM Base 2 protocol to communicate with the SMM Foundation.
Based on the status and SMM mode, registers or unregisters the
Ready To Boot callback.
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
SmmBase2Communicate (
VOID
)
{
EFI_STATUS Status;
VOID *CommBuffer;
UINTN CommSize;
VOID *SmmCommProtocol;
UINT8 InSmm;
CommSize = 8;
Status = gBS->SmmAllocatePool (
EfiRuntimeServicesData,
CommSize,
&CommBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = gBS->SmmCommunicate (CommBuffer, &SmmCommProtocol);
if (!EFI_ERROR (Status)) {
if (gSmmBase2Protocol != NULL && *(UINT64 *)gSmmBase2Protocol != 0) {
//
// Check if in SMM and SMM state != 3
//
Status = gSmmBase2Protocol->InSmm (&InSmm);
if (!EFI_ERROR (Status) && InSmm != 3) {
if (*(UINT8 *)((UINTN)gSmmBase2Protocol + 16) != 0) {
//
// Register ReadyToBoot callback
//
return SmmReadyToBootCallbackRegister ();
} else {
//
// Unregister ReadyToBoot callback
//
return ((EFI_STATUS (*)(VOID *))gSmmReadyToBootProtocol)(gSmmReadyToBootProtocol);
}
}
}
}
return Status;
}
//=============================================================================
// Entry Point
//=============================================================================
/**
Main DXE driver entry point for LegacySerialRedirection.
Address: 0xED4
Initializes SMM communication buffer, locates SIO protocol,
registers protocol notification callbacks:
- SMM Ready To Boot (GUID 0x22E0)
- SMM Base 2 communicate via SetTimer
- SMM SW Dispatch 2 initialization
- SMM SW Dispatch 2 data setup
@param[in] ImageHandle Driver image handle.
@param[in] SystemTable UEFI system table.
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
LegacySredirDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *Registration;
VOID *DispatchRegistration;
//
// Save protocol pointers
//
gST = SystemTable;
gBS = SystemTable->BootServices;
gRT = SystemTable->RuntimeServices;
//
// Allocate SMM communication buffer (32 bytes, EfiReservedMemoryType)
//
Status = gBS->AllocatePool (
EfiReservedMemoryType,
SMM_COMM_BUFFER_SIZE,
&gCommunicationBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Locate SIO Protocol (open with params 3, 7, 9)
//
Status = gBS->LocateProtocol (&gSioProtocolGuid, NULL, &gSioProtocol);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
return Status;
}
Status = gSioProtocol->Open (3, 7, 9, &gSioDataPtr, &gSioDataSize);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
return Status;
}
//
// Register protocol notification for SMM Ready To Boot
//
Registration = NULL;
Status = gBS->RegisterProtocolNotify (
&gSmmReadyToBootGuid,
SmmReadyToBootCallbackRegister,
&Registration
);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// Register SMM Base 2 periodic callback via SetTimer (period = 8)
//
Status = gBS->SetTimer (
SmmBase2Communicate,
TimerPeriodic,
8
);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// Register protocol notification for SMM SW Dispatch 2
//
Status = gBS->RegisterProtocolNotify (
&gSmmLegacySerialGuid,
(EFI_EVENT_NOTIFY)SmmReadyToBootCallbackRegister,
&DispatchRegistration
);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// Register protocol notification for SMM Dispatch 2 data
//
Status = gBS->RegisterProtocolNotify (
&gSmmDispatchDataGuid,
(EFI_EVENT_NOTIFY)SmmSwDispatch2Callback,
NULL
);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
return EFI_SUCCESS;
}
//=============================================================================
// _ModuleEntryPoint (DXE standard entry, supplied by lib)
//=============================================================================
//
// Address: 0x390
// Provided by UefiBootServicesTableLib + UefiRuntimeServicesTableLib.
// Initializes ImageHandle, gST, gBS, gRT globals, calls GetSystemHobList,
// then invokes LegacySredirDriverEntryPoint.
//