Newer
Older
AMI-Aptio-BIOS-Reversed / DpcDxe / DpcDxe.c
@Ajax Dong Ajax Dong 2 days ago 17 KB Init
/** @file
  DpcDxe.c -- Deferred Procedure Call (DPC) Driver implementation

  This DXE driver implements EFI_DPC_PROTOCOL for the UEFI network stack
  (AmiNetworkPkg). Deferred Procedure Calls allow drivers to schedule
  callback functions to be executed at TPL_CALLBACK level, useful for
  deferring work from interrupt handlers or high-TPL code.

  Architecture:
    - 32 priority levels (0-31), each with its own doubly-linked list
    - A free list of DPC_ENTRY structures is maintained
    - Entries are allocated from the free list (or from pool) and queued
    - DpcDispatchDpc() drains one priority level at a time, executing
      each entry's callback and returning it to the free list
    - DpcQueueDepth and DpcMaxQueueDepth track statistics

  Module:  DpcDxe.efi
  Source:  AmiNetworkPkg/UefiNetworkStack/Common/DpcDxe/Dpc.c
  Image:   Lenovo ThinkSystem HR650X BIOS (SHA256: a77c4daf...)
**/

#include <Uefi.h>
#include <Protocol/Dpc.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/BaseLib.h>
#include <Library/MemoryAllocationLib.h>
#include "DpcDxe.h"

// ==========================================================================
// Global data
// ==========================================================================

//
// Protocol interface instance -- EFI_DPC_PROTOCOL
//   +0x00: DpcQueueDpc    (at 0x518)
//   +0x08: DpcDispatchDpc (at 0x698)
//
EFI_DPC_PROTOCOL  gDpcProtocolInterface;

//
// Head of the free DPC_ENTRY pool (at 0x1140)
//
LIST_ENTRY  gDpcFreeListHead;

//
// DPC queue heads -- one LIST_ENTRY per priority level (0..31)
// Array at 0x11C0 .. 0x13AF (32 entries * 16 bytes each)
//
LIST_ENTRY  gDpcQueue[DPC_NUM_QUEUES];

//
// Statistics (at 0x1160, 0x1170)
//
UINT64      gDpcQueueDepth     = 0;
UINT64      gDpcMaxQueueDepth  = 0;

//
// Protocol registration handle (at 0x1168)
//
EFI_HANDLE  gDpcProtocolHandle = NULL;

//
// Debug output protocol (at 0x1198)
//
EFI_DEBUG_OUTPUT_PROTOCOL  *gDpcDebugProtocol = NULL;

//
// HOB list pointer (at 0x11A0)
//
VOID        *gDpcHobList = NULL;

// ==========================================================================
// Linked-list helper functions
// ==========================================================================

/**
  Validates a LIST_ENTRY pointer, ASSERT-ing that ForwardLink and BackLink
  are non-NULL.

  @param[in]  List  Pointer to the LIST_ENTRY to validate.

  @retval TRUE   List is valid.
**/
BOOLEAN
EFIAPI
DpcInternalIsListValid (
  IN LIST_ENTRY  *List
  )
{
  ASSERT (List != NULL);
  ASSERT (List->ForwardLink != NULL);
  ASSERT (List->BackLink != NULL);
  return TRUE;
}

/**
  Inserts a new entry at the tail of a doubly-linked list.

  @param[in]  ListHead  Pointer to the list head.
  @param[in]  Entry     Pointer to the entry to insert.

  @return ListHead.
**/
LIST_ENTRY *
EFIAPI
DpcInternalInsertTailList (
  IN LIST_ENTRY  *ListHead,
  IN LIST_ENTRY  *Entry
  )
{
  DpcInternalIsListValid (ListHead);
  Entry->ForwardLink              = ListHead;
  Entry->BackLink                 = ListHead->BackLink;
  ListHead->BackLink->ForwardLink = Entry;
  ListHead->BackLink              = Entry;
  return ListHead;
}

/**
  Checks whether a doubly-linked list is empty.

  @param[in]  ListHead  Pointer to the list head.

  @retval TRUE   The list is empty (head points to itself).
  @retval FALSE  The list is not empty.
**/
BOOLEAN
EFIAPI
DpcInternalIsListEmpty (
  IN LIST_ENTRY  *ListHead
  )
{
  DpcInternalIsListValid (ListHead);
  return (ListHead->ForwardLink == ListHead);
}

/**
  Removes an entry from a doubly-linked list.

  @param[in]  Entry  Pointer to the entry to remove.

  @return The ForwardLink of the removed entry.
**/
LIST_ENTRY *
EFIAPI
DpcInternalRemoveEntryList (
  IN LIST_ENTRY  *Entry
  )
{
  ASSERT (!DpcInternalIsListEmpty (Entry));
  Entry->ForwardLink->BackLink = Entry->BackLink;
  Entry->BackLink->ForwardLink = Entry->ForwardLink;
  return Entry->ForwardLink;
}

// ==========================================================================
// Memory management
// ==========================================================================

/**
  Allocates a single DPC_ENTRY structure from pool (32 bytes).

  @return Pointer to the allocated DPC_ENTRY, or NULL on failure.
**/
DPC_ENTRY *
EFIAPI
DpcAllocatePool (
  VOID
  )
{
  return (DPC_ENTRY *)AllocatePool (sizeof (DPC_ENTRY));
}

// ==========================================================================
// Debug output support
// ==========================================================================

/**
  Locates the Debug Output Protocol.

  @return EFI_STATUS of the LocateProtocol call.
**/
EFI_STATUS
EFIAPI
DpcGetDebugOutputProtocol (
  VOID
  )
{
  EFI_STATUS  Status;

  if (gDpcDebugProtocol != NULL) {
    return EFI_SUCCESS;
  }

  Status = gBS->LocateProtocol (
                  &gEfiDebugPortProtocolGuid,
                  NULL,
                  (VOID **)&gDpcDebugProtocol
                  );

  if (EFI_ERROR (Status)) {
    gDpcDebugProtocol = NULL;
  }

  return Status;
}

/**
  Debug assertion handler -- called by ASSERT() macros to report failures
  with source file and line information.

  @param[in]  FileName     Source file name (ASCII).
  @param[in]  LineNumber   Line number in source file.
  @param[in]  Description  Assertion condition description.
**/
VOID
EFIAPI
DpcDebugAssert (
  IN CHAR8  *FileName,
  IN UINTN  LineNumber,
  IN CHAR8  *Description
  )
{
  if (gDpcDebugProtocol != NULL) {
    gDpcDebugProtocol->Write (
                         FileName,
                         LineNumber,
                         Description
                         );
  }
}

/**
  Reports an ASSERT_EFI_ERROR condition with formatted status output.
  Determines the appropriate CMOS-based error severity level and reports
  the error if it matches the given StatusCode filter.

  @param[in]  StatusCode  Error code/tag to filter on.
  @param[in]  Format      ASCII format string (e.g. "\nASSERT_EFI_ERROR (Status = %r)\n").
  @param[in]  ...         Variable arguments (the EFI_STATUS code).
**/
VOID
EFIAPI
DpcReportAssertError (
  IN     UINTN       StatusCode,
  IN     CONST CHAR8 *Format,
  IN     ...
  )
{
  VA_LIST     Marker;
  EFI_STATUS  Status;
  UINT8       CmosIndex;
  UINT8       CmosValue;

  DpcGetDebugOutputProtocol ();

  if (gDpcDebugProtocol != NULL) {
    //
    // Read CMOS status -- check error reporting level.
    // Port 0x70 index, port 0x71 data.
    //
    CmosIndex = __inbyte (0x70);
    __outbyte (0x70, CmosIndex & 0x80 | 0x4B);
    CmosValue = __inbyte (0x71);

    if (CmosValue > 3) {
      if (CmosValue == 0) {
        CmosValue = (UINT8)((*(UINT8 *)(UINTN)0xFDAF0490 & 2) | 1);
      }
    }

    //
    // Map CMOS value to EFI error severity.
    //
    if (CmosValue == 1) {
      Status = EFI_ERROR_WARNING;       // 0x80000004
    } else {
      Status = EFI_ERROR_ERROR;          // 0x80000006
    }

    if ((Status & StatusCode) != 0) {
      VA_START (Marker, Format);
      gDpcDebugProtocol->Write (StatusCode, Format, Marker);
      VA_END (Marker);
    }
  }
}

// ==========================================================================
// GUID comparison and HOB list initialization
// ==========================================================================

/**
  Read an unaligned 64-bit value from a buffer.

  @param[in]  Buffer  Pointer to the buffer.

  @return The 64-bit value.
**/
UINT64
EFIAPI
DpcReadUnaligned64 (
  IN UINT64  *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *Buffer;
}

/**
  Compares two EFI_GUIDs by comparing their first and second 64-bit
  halves (little-endian GUID layout).

  @param[in]  Guid1  First GUID to compare.
  @param[in]  Guid2  Second GUID to compare.

  @retval TRUE   Both halves match -- GUIDs are equal.
  @retval FALSE  GUIDs differ.
**/
BOOLEAN
EFIAPI
DpcCompareGuid (
  IN EFI_GUID  *Guid1,
  IN EFI_GUID  *Guid2
  )
{
  return (DpcReadUnaligned64 ((UINT64 *)Guid1) == DpcReadUnaligned64 ((UINT64 *)Guid2) &&
          DpcReadUnaligned64 ((UINT64 *)Guid1 + 1) == DpcReadUnaligned64 ((UINT64 *)Guid2 + 1));
}

/**
  Initializes the gDpcHobList pointer by scanning the DXE Configuration
  Table for the DXE Services Table GUID.

  Must be called before any HOB services are accessed.

  @return VOID.
**/
VOID
EFIAPI
DpcHobInit (
  VOID
  )
{
  UINTN       Index;
  EFI_GUID    DxeServicesGuid = EFI_DXE_SERVICES_TABLE_GUID;

  if (gDpcHobList != NULL) {
    return;
  }

  gDpcHobList = NULL;

  if (gST->NumberOfTableEntries > 0) {
    for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
      if (DpcCompareGuid (
            &DxeServicesGuid,
            &gST->ConfigurationTable[Index].VendorGuid
            )) {
        gDpcHobList = gST->ConfigurationTable[Index].VendorTable;
        break;
      }
    }
  }

  if (gDpcHobList == NULL) {
    DpcReportAssertError (
      0x80000000LL,
      "\nASSERT_EFI_ERROR (Status = %r)\n",
      0x800000000000000EuLL
      );
    ASSERT (FALSE);  // "!EFI_ERROR (Status)"
  }
}

// ==========================================================================
// EFI_DPC_PROTOCOL: DpcQueueDpc
// ==========================================================================

/**
  Enqueues a Deferred Procedure Call.

  The caller specifies a DpcToken (priority level 4..31), a procedure
  callback, and an optional context argument. The procedure executes
  when DpcDispatchDpc() runs for that priority level.

  @param[in]  This             The EFI_DPC_PROTOCOL instance.
  @param[in]  DpcToken         Priority level (must be >= 4 and <= 0x1F).
  @param[in]  DpcProcedure     Function pointer to execute at DPC dispatch.
  @param[in]  DpcContext       Optional context pointer passed to the function.

  @retval EFI_SUCCESS           DPC was queued successfully.
  @retval EFI_INVALID_PARAMETER DpcToken out of range, or DpcProcedure is NULL.
  @retval EFI_OUT_OF_RESOURCES  No DPC entries available (free list empty
                                and pool allocation failed).
**/
EFI_STATUS
EFIAPI
DpcQueueDpc (
  IN EFI_DPC_PROTOCOL   *This,
  IN UINT32             DpcToken,
  IN EFI_DPC_PROCEDURE  DpcProcedure,
  IN VOID               *DpcContext    OPTIONAL
  )
{
  EFI_STATUS  Status;
  DPC_ENTRY   *Entry;
  UINTN       Tpl;
  UINTN       RetryCount;

  //
  // Validate DpcToken (must be in range 4..0x1F).
  //
  if ((DpcToken < 4) || (DpcToken > 0x1F)) {
    return EFI_INVALID_PARAMETER;
  }

  if (DpcProcedure == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Raise TPL to serialize access to the DPC free list and queues.
  //
  Tpl = gBS->RaiseTPL (TPL_DPC_PROCESSING);
  Status = EFI_SUCCESS;

  //
  // Check if the free list has an entry.
  //
  if (DpcInternalIsListEmpty (&gDpcFreeListHead)) {
    //
    // Free list is empty -- try to allocate more entries.
    //
    if (Tpl <= 16) {
      //
      // TPL is high but still low enough for AllocatePool.
      // Loop up to DPC_ALLOCATE_BATCH (64) times, dropping and
      // raising TPL each iteration.
      //
      for (RetryCount = 0; RetryCount < DPC_ALLOCATE_BATCH; RetryCount++) {
        //
        // Restore TPL so AllocatePool can work.
        //
        gBS->RestoreTPL (Tpl);

        //
        // Allocate a new DPC entry.
        //
        Entry = DpcAllocatePool ();

        //
        // Raise TPL back.
        //
        gBS->RaiseTPL (TPL_DPC_PROCESSING);

        if (Entry != NULL) {
          //
          // Check again if free list is still empty (another thread
          // may have freed entries while TPL was lowered).
          //
          if (DpcInternalIsListEmpty (&gDpcFreeListHead)) {
            //
            // Free list is still empty -- add this entry to it.
            //
            DpcInternalInsertTailList (&gDpcFreeListHead, &Entry->List);
          }
        }
      }

      //
      // After the loop, check if we now have an entry.
      //
      if (DpcInternalIsListEmpty (&gDpcFreeListHead)) {
        Status = EFI_OUT_OF_RESOURCES;
        goto Done;
      }

    } else {
      //
      // TPL is too high for pool allocation.
      //
      Status = EFI_OUT_OF_RESOURCES;
      goto Done;
    }
  }

  //
  // Take an entry from the free list.
  //
  Entry  = BASE_CR (gDpcFreeListHead.ForwardLink, DPC_ENTRY, List);
  DpcInternalRemoveEntryList (&Entry->List);

  //
  // Populate the DPC entry.
  //
  Entry->DpcProcedure = (VOID *)DpcProcedure;
  Entry->DpcContext   = DpcContext;

  //
  // Queue to the correct priority-level list.
  //
  DpcInternalInsertTailList (&gDpcQueue[DpcToken], &Entry->List);

  //
  // Update statistics.
  //
  gDpcQueueDepth++;
  if (gDpcQueueDepth > gDpcMaxQueueDepth) {
    gDpcMaxQueueDepth = gDpcQueueDepth;
  }

  Status = EFI_SUCCESS;

Done:
  gBS->RestoreTPL (Tpl);
  return Status;
}

// ==========================================================================
// EFI_DPC_PROTOCOL: DpcDispatchDpc
// ==========================================================================

/**
  Dispatches all pending DPC entries.

  Scans priority levels from highest (31) down to the current TPL,
  executing the callback of each queued entry and returning it to
  the free list.

  @param[in]  This  The EFI_DPC_PROTOCOL instance.

  @retval EFI_SUCCESS      At least one DPC was dispatched.
  @retval EFI_NOT_FOUND    No DPCs were queued.
**/
EFI_STATUS
EFIAPI
DpcDispatchDpc (
  IN EFI_DPC_PROTOCOL  *This
  )
{
  EFI_STATUS  Status;
  UINTN       Priority;
  DPC_ENTRY   *Entry;

  Status = EFI_NOT_FOUND;

  //
  // Raise TPL to DPC_PROCESSING to serialize.
  //
  Priority = gBS->RaiseTPL (TPL_DPC_PROCESSING);

  if (gDpcQueueDepth == 0) {
    goto Done;
  }

  //
  // Process priorities from the raised TPL (31) downwards.
  //
  if (Priority <= 31) {
    for ( ; ; ) {
      //
      // If this priority queue has entries, drain it.
      //
      if (!DpcInternalIsListEmpty (&gDpcQueue[Priority])) {
        Status = EFI_SUCCESS;

        do {
          DpcInternalIsListValid (&gDpcQueue[Priority]);

          //
          // Take the first entry from the queue.
          //
          Entry  = BASE_CR (gDpcQueue[Priority].ForwardLink,
                            DPC_ENTRY,
                            List);
          DpcInternalRemoveEntryList (&Entry->List);

          //
          // Decrement the queue depth counter.
          //
          gDpcQueueDepth--;

          //
          // Drop TPL to the priority level so the callback
          // can execute at its intended TPL.
          //
          gBS->RestoreTPL (Priority);

          //
          // Execute the deferred procedure.
          //
          ((EFI_DPC_PROCEDURE)Entry->DpcProcedure)(Entry->DpcContext);

          //
          // Raise TPL back, and return the entry to the free list.
          //
          gBS->RaiseTPL (TPL_DPC_PROCESSING);
          DpcInternalInsertTailList (&gDpcFreeListHead, &Entry->List);

        } while (!DpcInternalIsListEmpty (&gDpcQueue[Priority]));
      }

      //
      // Move to the next lower priority level.
      //
      if (Priority == 0) {
        break;
      }
      Priority--;
    }
  }

Done:
  gBS->RestoreTPL (Priority);
  return Status;
}

// ==========================================================================
// Driver initialization
// ==========================================================================

/**
  Main driver initialization entry installed via EFI_DPC_PROTOCOL.

  Initializes all DPC queue heads (32 entries), checks for
  duplicate protocol installation, then installs the
  EFI_DPC_PROTOCOL onto a protocol handle.

  @param[in]  ImageHandle  Module's EFI image handle.
  @param[in]  SystemTable  UEFI System Table.

  @retval EFI_SUCCESS          Protocol installed.
  @retval EFI_ALREADY_STARTED  Protocol already installed.
  @retval Others               InstallProtocolInterface failure.
**/
EFI_STATUS
EFIAPI
DpcDriverEntry (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  UINTN       QueueIndex;

  //
  // Check if DPC protocol is already installed.
  //
  Status = gBS->LocateProtocol (
                  &gEfiDpcProtocolGuid,
                  NULL,
                  (VOID **)&gDpcProtocolHandle
                  );
  if (!EFI_ERROR (Status)) {
    //
    // Protocol already registered -- this is a bug in the calling driver.
    //
    DpcDebugAssert (
      "e:\\hs\\AmiNetworkPkg\\UefiNetworkStack\\Common\\DpcDxe\\Dpc.c",
      326,
      "&gEfiDpcProtocolGuid already installed in database"
      );
  }

  //
  // Initialize all 32 DPC queue heads.
  //
  for (QueueIndex = 0; QueueIndex < DPC_NUM_QUEUES; QueueIndex++) {
    DpcInternalIsListValid (&gDpcQueue[QueueIndex]);
    gDpcQueue[QueueIndex].ForwardLink = &gDpcQueue[QueueIndex];
    gDpcQueue[QueueIndex].BackLink    = &gDpcQueue[QueueIndex];
  }

  //
  // Install the DPC protocol.
  //
  Status = gBS->InstallProtocolInterface (
                  &gDpcProtocolHandle,
                  &gEfiDpcProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  &gDpcProtocolInterface
                  );

  if (EFI_ERROR (Status)) {
    DpcReportAssertError (
      0x80000000LL,
      "\nASSERT_EFI_ERROR (Status = %r)\n",
      Status
      );
    DpcDebugAssert (
      "e:\\hs\\AmiNetworkPkg\\UefiNetworkStack\\Common\\DpcDxe\\Dpc.c",
      344,
      "!EFI_ERROR (Status)"
      );
  }

  return Status;
}

/**
  Standard UEFI DXE driver entry point.

  Initializes the UEFI Boot Services, Runtime Services, System Table,
  and Image Handle globals. Then initializes the HOB list and installs
  the DPC protocol.

  @param[in]  ImageHandle  Module's EFI image handle.
  @param[in]  SystemTable  UEFI System Table.

  @return DpcDriverEntry() return value.
**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  //
  // Initialize UEFI globals.
  //
  gImageHandle  = ImageHandle;
  ASSERT (ImageHandle != NULL);

  gST = SystemTable;
  ASSERT (gST != NULL);

  gBS = SystemTable->BootServices;
  ASSERT (gBS != NULL);

  gRT = SystemTable->RuntimeServices;
  ASSERT (gRT != NULL);

  //
  // Initialize HOB list for DXE phase services.
  //
  DpcHobInit ();

  //
  // Install the DPC protocol.
  //
  return DpcDriverEntry (ImageHandle, SystemTable);
}