Newer
Older
AMI-Aptio-BIOS-Reversed / LegacySredir / LegacySredir.c
@Ajax Dong Ajax Dong 2 days ago 26 KB Init
/** @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.
//