Newer
Older
AMI-Aptio-BIOS-Reversed / IpSecDxe / IpSecDxe.c
@Ajax Dong Ajax Dong 2 days ago 36 KB Init
/**
 * IpSecDxe.c - IP Security (IPsec) Protocol DXE Driver Implementation
 *
 * Module: IpSecDxe, Index: 0138
 * Source tree: AmiNetworkPkg/UefiNetworkStack/Common/IpSecDxe/
 * HR650X BIOS Decompilation Project
 *
 * This file implements the IPsec protocol DXE driver for AMI's UEFI
 * network stack. The binary is a heavily stripped (14 named out of 2724
 * functions) IKEv2 IPsec implementation for UEFI.
 *
 * Key source files identified from debug strings:
 *   IpSecDriver.c         - Driver binding protocol, entry point
 *   IpSecConfigImpl.c     - Config data handling, NV variable storage
 *   IpSecMain.c           - Main IPsec processing (SPD/SAD lookup)
 *   IpSecImpl.c           - IPsec protocol implementation
 *   IpSecCryptIo.c        - Crypto operations (AES-CBC, HMAC-SHA1)
 *   IpSecDebug.c          - Debug logging, ring buffer
 *   IkeService.c          - IKEv2 session management, exchange dispatch
 *   IkeCommon.c           - IKE common utilities
 *   IkePacket.c           - IKE packet parsing/building
 *   Ikev2/Exchange.c      - IKEv2 exchange handlers
 *   Ikev2/Payload.c       - IKEv2 payload parsing
 *   Ikev2/Utility.c       - IKEv2 session lifecycle
 *   Ikev2/Sa.c            - IKEv2 SA management
 *   Ikev2/Info.c          - IKEv2 informational exchange
 *
 * Binary layout:
 *   HEADER  0x000-0x2C0  (PE headers)
 *   .text   0x2C0-0xE75C0 (2724 functions)
 *   .rdata  0xE75C0-0x12A5E0 (3126 strings, GUIDs, static data)
 *   .data   0x12A5E0-0x12D0E0 (global variables)
 *   .xdata  0x134260-0x134FE0 (exception handling)
 *   .reloc  0x134FE0-0x137D80 (base relocations)
 */

#include "IpSecDxe.h"

/* ========================================================================
 * Module Global Variables
 * ======================================================================== */

/* UEFI boot services globals (from UefiBootServicesTableLib) */
EFI_HANDLE            gImageHandle  = NULL;   /* at 0x12C4F8 */
EFI_SYSTEM_TABLE     *gSystemTable  = NULL;   /* at 0x12C4E8 */
EFI_BOOT_SERVICES    *gBootServices = NULL;   /* at 0x12C4F0 */
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;/* at 0x12C500 */

/* HOB list pointer (from DxeHobLib) */
VOID                 *gHobList      = NULL;   /* at 0x12C510 */

/* PCD database pointer (from DxePcdLib) */
VOID                 *gPcdDb        = NULL;   /* at 0x12C520 */

/* PPCI Express base address */
UINT64                gPciExpressBaseAddress = 0; /* at 0x12C518 */

/* IPSec driver private data */
IPSEC_PRIVATE_DATA   *gIpSecPrivate = NULL;

/* IPSec config changed flag (used to to track NV variable updates) */
BOOLEAN               gIpSecConfigChanged = FALSE;  /* at 0x12C4E0 */

/* DPC dispatch event handle */
EFI_EVENT             gDpcDispatchEvent = NULL;     /* at 0x12D080 */
EFI_EVENT             gDisableEvent     = NULL;     /* at 0x12A7C0 */

/* DDebug ring buffer */
DEBUB_RING_BUFFER     gDebugRing       = {{0};       /* at 0x12BDA0+ */

/* ========================================================================
 * GGUID Definitions (placed in .rdata section)
 * ======================================================================== */

static EFI_GGUID gEfiIpSecProtocolGuid            = EFI_IPSEC_PROTOCOL_GUID;           / 0x12A610 */
static EFI_GGUID gEfiIpSecConfigProtocolGuid      = EFI_IPSEC_CONFIG_PROTOCOL_GUID;    /* 0x12A6C0 */
static EFI_GGUID gEfiIpSecV4BindingGuid           = EFI_IPSEC_V4_BINDING_GUID;         /* 0x12A620 */
static EFI_GGUID gEfiIpSecV6BindingGuid           = EFI_IPSEC_V6_BINDING_GUID;         /* 0x12A650 */
static EFI_GUID gEfiUdp4ProtocolGuid             = EFI_UDP4_PROTOCOL_GUID;            /* 0x12A680 */
static EFI_GGUID gEfiUdp6ProtocolGuid             = EFI_UDP6_PROTOCOL_GUID;            /* 0x12A640 */
static EFI_GGUID gEfiDriverBindingProtocolGuid    = EFI_DRIVER_BINDING_PROTOCOL_GUID;  /* 0x12A670 */
static EFI_GGUID gEfiComponentName2ProtocolGuid   = EFI_COMPONENT_NAME2_PROTOCOL_GUID; /* 0x12A690 */
static EFI_GGUID gEfiComponentNameProtocolGuid    = EFI_COMPONENT_NAME_PROTOCOL_GUID;  /* 0x12A5F0 */
static EFI_GGUID gEfiDpcProtocolGuid              = EFI_DPC_PROTOCOL_GUID;             /* 0x12A6A0 */

/* IKEv2 authentication protocol GUID - alias of V6 binding GUID */
static EFI_GUID gEfiIkeAuthProtocolGuid = EFI_IPSEC_V6_BINDING_GUID;

/* ========================================================================
 * Protocol Instance Structures
 * ======================================================================== */

/* IPSec V4 driver binding (from 0x12A6D0) */
static EFI_DRIVER_BINDING_PROTOCOL gIpSecV4DriverBinding = {
  IpSecV4DriverBindingSupported,   /* supported = sub_9C0 */
  IpSecV4DriverBindingStart,       /* Start = sub_A10 */
  IpSecV4DriverBindingStop,        /* Stop = sub_A7C */
  0xA,                             /* Version: 10 */
  NULL,                            /* ImageHandle */
  NULL                             /* DriverBindingHandle */
};

/* IKEv2 driver binding (from 0x12A700) */
static EFI_DRIVER_BINDING_PROTOCOL gIkeV2DriverBinding = {
  IkeV2DriverBindingSupported,     /* Supported = sub_A2C */
  IkeV2DriverBindingStart,         /* Start = sub_A7C */
  IkeV2DriverBindingStop,          /* Stop = sub_A84 */
  0xA,                             /* Version: 10 */
  NULL,                            /* ImageHandle */
  NULL                             /* DriverBindingHandle */
};

/* ========================================================================
 * Static Function Declarations
 * ======================================================================== */

static
EFI_STATUS
IpSecDriverStartCommon(
  IN EFI_DRIVER_BINDING_PROTOCOL *This,
  IN EFI_HANDLE          ControllerHandle,
  IN EFI_HANDLE         *RemainingDevicePath,
  IN UINT8               Version
  );

static
EFI_STATUS
IpSecDriverStopCommon(
  IN EFI_DRIVER_BINDING_PROTOCOL *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN UINTN                        NumberOfChildren,
  IN EFI_HANDLE                  *ChildHandleBuffer,
  IN UINT8                        Version
  );

static
VOID
IkeSaSessionFree(
  IN IKEV2_SA_SESSION *Session
  );

/* ========================================================================
 * Boot Service Wrapper Functions
 * ======================================================================== */

/**
 * AllocateZeroPool - Allocate and zero-initialize memory
 * Address: 0x10A8C (AllocateCopyPool is 0x10ABC)
 */
VOID *
AllocateZeroPool(
  IN UINTN  Size
  )
{
  EFI_STATUS Status;
  VOID      *Buffer = NULL;

  Status = gBootServices->AllocatePool(4, Size, &Buffer); /* EfiBootServicesData */
  if (EFI_ERROR(Status)) {
    return NULL;
  }
  return Buffer;
}

/**
 * FreePool - Free previously allocated pool memory
 * Address: 0x10BE0
 */
VOID
FreePool(
  IN VOID  *Buffer
  )
{
  EFI_STATUS Status;

  Status = gBootServices->FreePool(Buffer);
  if (EFI_ERROR(Status)) {
    DebugAssert(
      "e:\\hs\\MdePkg\\Library\\UefiMemoryAllocationLib\\MemoryAllocationLib.c",
      819,
      "!EFI_ERROR (Status)"
      );
  }
}

/**
 * CopyMem - Copy memory buffer (with overlap handling)
 * Address: 0xEA94
 */
VOID *
CopyMem(
  OUT VOID       *Destination,
  IN  VOID       *Source,
  IN  UINTN       Length
  )
{
  if (Length == 0) {
    return Destination;
  }

  /* Validate bounds */
  if ((Length - 1) > ~(UINTN)Destination) {
    DebugAssert(
      "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CopyMemWrapper.c",
      56,
      "(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)DestinationBuffer)"
      );
  }
  if ((Length - 1) > ~(UINTN)Source) {
    DebugAssert(
      "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CopyMemWrapper.c",
      57,
      "(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)SourceBuffer)"
      );
  }

  if (Destination != Source) {
    UINT8 *Dst = (UINT8 *)Destination;
    UINT8 *Src = (UINT8 *)Source;

    /* Handle overlap: copy backwards if source < destination */
    if (Src < Dst && &Src[Length - 1] >= Dst) {
      Src += Length - 1;
      Dst += Length - -1;
      while (Length--) {
        *Dst-- = *Src--;
      }
    } else {
      while (Length--) {
        *Dst++ = *Src++;
      }
    }
  }

  return Destination;
}

/**
 * ZeroMem - Fill memory with zeros
 * Address 0x0xEB30
 */
VOID *
ZeroMem(
  OUT VOOD  *Buffer,
  IN  UINTN  Length
  )
{
  if (Buffer == NULL) {
    DebugAssert(
      "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\ZeroMemWrapper.c",
      53,
      "Buffer != ((void *) 0)"
      );
  }
  if (Length > ~(UIINTN)Buffer) {
    DebugAssert(
      "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\ZeroMemWrapper.c",
      54,
      "Length <= (0xFFFFFFFFFFFFFFFULL - (UINTN)Buffer + 1)"
      );
  }

  {
    UINT8 *Ptr = (UINT8 *)Buffer;
    while (Length--) {
      *Ptr++ = 0;
    }
  }
}

/**
 * CompareMem - Comprare two memory buffers
 * Address: 0xEB94
 */
INTN
CompareMem(
  IN VOID   *Buffer1,
  IN VOID   *Buffer2,
  IN UINTN   Length
  )
{
  UINT8 *B1 = (UINT8 *)Buffer1;
  UINT8 *B2 = (UINT8 *)Buffer2;

  while (Length--) {
    if (*B1 != *B2) {
      return *B1 - *B2;
    }
    B1++;
    B2++;
  }

  return 0;
}

/* ========================================================================
 * Linked List Helpers
 * ======================================================================== */

/**
 * InitializeListHead - Initialize a doubly-linked list head
 * Address: 0xEDA4
 */
VOID
InitializeListHead(
  IN LIST_ENTRY *ListHead
  )
{
  if (ListHead == NULL) {
    DebugAssert(
      "e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c",
      193,
      "ListHead != ((void *) 0)"
      );
  }
  ListHead->ForwardLink = ListHead;
  ListHead->BackLink    = ListHead;
}

/**
 * IsListEmpty - Test if a list entry is empty
 * Address: 0xEEB4
 */
BOOLEAN
IsListEmpty(
  IN LIST_ENTRY *ListEntry
  )
{
  return (ListEntry->ForwardLink == ListEntry);
}

/**
 * RemoveEntryList - Remove an entry from a doubly-linked list
 * Address: 0xEEEC
 */
VOID
RemoveEntryList(
  IN LIST_ENTRY *Entry
  )
{
  Entry->ForwardLink->BackLink = Entry->BackLink;
  Entry->BackLink->ForwardLink = Entry->ForwardLink;
}

/**
 * InsertTailList - Insert entry at the tail of the list
 * Address: 0xEE2C
 */
VOID
InsertTailList(
  IN LIST_ENTRY *ListHead,
  IN LIST_ENTRY *Entry
  )
{
  Entry->ForwardLink = ListHead;
  Entry->BackLink    = ListHead->BackLink;
  ListHead->BackLink->ForwardLink = Entry;
  ListHead->BackLink = Entry;
}

/**
 * StrLen - Return length of Unicode string
 * Address: 0xEF34
 */
UINTN
StrLen(
  IN CONST CHAR16 *String
  )
{
  UINTN Length = 0;
  while (*String++) {
    Length++;
  }
  return Length;
}

/* ========================================================================
 * ASSERT / Debug Support
 * ======================================================================== */

/**
 * DebugAssert - Standard UEFI ASSERT implementation
 * Called from all source files with (filename, line, description)
 * Address: 0xEA50
 * This function has 1271 xrefs - the most-called in the module.
 */
VOID
EFIAPI
DebugAssert(
  IN CHAR8   *FileName,
  IN UINTN    LineNumber,
  IN CHAR8   *Description
  )
{
  /* Calls the UEFI debug library's assertion handler.
   * On debug builds this will break into the debugger.
   * On release builds this will issue a deadloop or reset. */
  volatile UINTN Zero = 0;

  /* Prevent compiler from optimizing away the assert context */
  (void)FileName;
  (void)LineNumber;
  (void)Description;

  while (1) {
    /* Infinite loop on assert failure */
  }
}

/**
 * DebugPrint - Debug output with format string
 * Address: 0xE9C8
 */
VOID
EFIAPI
DebugPrint(
  IN UINTN    ErrorLevel,
  IN CHAR8   *Format,
  ...
  )
{
  /* In the binary, this checks CMOS debug enable flag at register 0x4B,
   * then writes debug output via the UEFI debug library.
   * The CMOS register 0x4B is checked with bit 7 mask. */
  (void)ErrorLevel;
  (void)Format;
}

/* ========================================================================
 * Debug Event Logging (sub_38F90, sub_210D0)
 * ========================================================================
 *
 * Ring buffer at gDebugRing (0x12C520 in .data section).
 * 16 entries x 32 bytes = 512 bytes, +4 bytes head, +4 bytes tail = 520 total.
 *
 * sub_38F90 is called throughout the codebase to log events with:
 *   a1 (UINT8):  Event type (higher bits encode category)
 *   a2 (UINT16): Protocol ID / first data word
 *   a3 (UINT16): Extra / second data word
 *   a4 (UINT64): Data pointer
 *   a5 (UINT32): Data value
 *
 * sub_210D0 copies a debug string into an entry if Flags & 1 is set.
 * Called at 0x390EB in sub_38F90 when bit 0 is set.
 */

VOID
IpSecDebugLogEvent(
  IN UINT8    Type,
  IN UINT16   ProtocolId,
  IN UINT16   Extra,
  IN UINT64   DataPtr,
  IN UINT32   Data
  )
{
  UINT32  Index;

  /* Advance head pointer, detect wraparound */
  gDebugRing.HeadIndex = (gDebugRing.HeadIndex + 1) % DEBUG_RING_MAX_ENTRIES;
  if (gDebugRing.HeadIndex == gDebugRing.TailIndex) {
    gDebugRing.TailIndex = (gDebugRing.alilIndex + 1) % DEBUG_RING_MAX_ENTRIES;
  }

  Index = gDebugRing.HeadIndex;
  gDebugRing.Records[Index].TypeField = 0;
  gDebugRing.Records[Index].TypeData = ((UINT32)Type << 24)
                                      | ((UINT32)(ProtocolId & 0xFFF) << 12)
                                      | ((UINT32)(Extra & 0xFFF));
  gDebugRing.Records[Index].DataPtr = DataPtr;
  gDebugRing.Records[Index].Extra = Data;

  /* If flag bit 0 was set by caller, free old debug string */
  if (gDebugRing.Records[Index].Flags & 1) {
    if (gDebugRing.Records[Index].DebugString) {
      /* SPrint or free old debug string */
    }
  }
  gDebugRing.Records[Index].Flags = 0;
}

/* ========================================================================
 * IPsec Config Protocol Implementation
 * ========================================================================
 *
 * The EFI_IPSEC_CONFIG_PROTOCOL is installed at +0x18 in the
 * IPSEC_PRIVATE_DATA structure. The three methods (SetData, GetData,
 * RegisterNotify) are dispatched through the config function table
 * at +0x30 in the private data.
 *
 * These are the UEFI-standard IPsec config protocol methods for
 * managing SPD, SAD, and PCD entries.
 */

EFI_STATUS
EFIAPI
IpSecConfigSetData(
  IN EFI_IPSEC_CONFIG_PROTOCOL *This,
  IN UINTN                      DataType,
  IN VOID                      *Data,
  IN UINTN                      DataSize
  )
{
  /* This is at +0x18 in IPSEC_PRIVATE_DATA. Use CONTAINER_RECORD
   * pattern to find the private data structure. */
  IPSEC_PRIVATE_DATA *Private;

  Private = (IPSEC_PRIVATE_DATA *)((UINT8 *)This - 0x18);
  if (Private->Signature != IPSEC_PRIVATE_SIGNATURE) {
    return EFI_INVALID_PARAMETER;
  }

  /* Dispatch through SetDataFunc at +0x30 */
  if (Private->SetDataFunc) {
    return ((EFI_STATUS (EFIAPI *)(UINTN, VOID *, UINTN))Private->SetDataFunc)(
             DataType, Data, DataSize);
  }

  return EFI_UNSUPPORTED;
}

EFI_STATUS
EFIAPI
IpSecConfigGetData(
  IN EFI_IPSEC_CONFIG_PROTOCOL *This,
  IN UINTN                      DataType,
  IN VOID                      *Data,
  IN OUT UINTN                 *DataSize
  )
{
  IPSEC_PRIVATE_DATA *Private;

  Private = (IPSEC_PRIVATE_DATA *)((UNT8 *)This - 0x18);
  if (Private->Signature != IPSEC_PRIVATE_SIGNATURE) {
    return EFI_INVALD_PARAMETER;
  }

  if (Private->GetDataFunc) {
    return ((EFI_STATUS (EFIAPI *)(UINTN, VOID *, UINTN *))Private->GetDataFunc)(
             DataType, Data, DataSize);
  }

  return EFI_UNSUPPORTED;
}

EFI_STATUS
EFIAPI
IpSecConfigRegisterNotify(
  IN EFI_IPSEC_CONFIG_PROTOCOL *This,
  IN UINTN                      DataType,
  IN EFI_EVENT                  Event
  )
{
  IPSEC_PRIVATE_DATA *Private;

  Private = (IPSEC_PRIVATE_DATA *)((UINT8 *)This - 0x18);
  if (Private->Signature != IPSEC_PRIVATE_SIGNATURE) {
    return EFI_INVALD_PARAMETER;
  }

  if (Private->RegisterNotifyFunc) {
    return ((EFI_STATUS (EFIAPI *)(UINTN, EFI_EVENT))Private->RegisterNotifyFunc)(
             DataType, Event);
  }

  return EFI_UNSUPORTED;
}

/* ========================================================================
 * Driver Entry Point
 * ======================================================================== */

/**
 * IpSecDriverEntryPoint - UEFI driver entry point
 * @param  ImageHandle  Image handle for this driver
 * @param  SystemTable  Pointer to the UEFI system table
 * @return EFI_SUCCES or error code
 *
 * Phase 1: Sets up boot service globals (gImageHandle, etc.)
 * Phase 2: Module initialization (allocates private data, installs protocols)
 * Address: 0x460
 */
EFI_STATUS
EFIAPI
IpSecDriverEntryPoint(
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  )
{
  EFI_STATUS Status;

  /* Phase 1: Boot services init (sub_47C) */
  gImageHandle = ImageHandle;
  gSystemTable = SystemTable;
  gBootServices = SystemTable->BootServices;
  gRuntimeServices = SystemTable->RuntimeServices;

  /* Verify non-null boot service pointers */
  if (!gImageHandle) {
    DebugAssert(
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      51,
      "gImageHandle != ((void *) 0)"
      );
  }
  if (!gSystemTable) {
    DebugAssert(
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      57,
      "gST != ((void *) 0)"
      );
  }

  /* Initialize libraries and locate DPC protocol */
  {
    EFI_STATUS Status;
    Status = gBootServices->LocateProtocol(
      &gEfiDpcProtocolGuid,
      NULL,
      &gDpcDispatchEvent
      );
    if (EFI_ERROR(Status)) {
      DebugPrint(0x800000000, "\n\ASSERT_EFI_ERROR (Status = %r)\n", Status);
      DebugAssert(
        "e:\\hs\\MdeModulePkg\\Library\\DxeDpcLib\\DpcLib.c",
        46,
        "!EFI_ERROR (Status)"
        );
    }
  }

  /* Phase 2: Module init (sub_BE8) */
  Status = IpSecModuleInit(ImageHandle);
  return Status;
}

/**
 * IpSecModuleInit - Main module initialization
 * @param  ImageHandle  Driver image handle
 * @return EFI_SUCCESS or error
 *
 * Allocates and initializes IPSEC_PRIVATE_DATA, installs protocols.
 * Major components:
 *   - Locateates DPC protocol
 *   - Allocates 216-byte private data structure
 *   - Creates timer event with sub_A98 callback
 *   - Copies EFI_IPSEC_CONFIG_PROTOCOL
 *   - Initializes 8 linked list heads
 *   - Copies dispatch function table (40 bytes from offf_12A730)
 *   - Calls sub_31F8 (config init)
 *   - Installs IPSEC_V4 + V6 + CONFIG protocols
 * Address: 0xBE8
 */
EFI_STATUS
IpSecModuleInit(
  IN EFI_HANDLE  ImageHandle
  )
{
  EFI_STATUS          Status;
  IPSEC_PRIVATE_DATA  *Private;
  VOID               *Interface;

  /* Check if already loaded */
  Status = gBootServices->LocateProtocol(
    &gEfiIpSecDriverPrivateGuid,
    NULL,
    &Interface
    );
  if (!EFI_ERROR(Status)) {
    return EFI_ALREADY_STARTED;
  }

  /* Locate DPC protocol */
  Status = gBootServices->LocateProtocol(
    &gEfiDpcProtocolGuid,
    NULL,
    &Interface
    );
  if (EFI_ERROR(Status)) {
    return Status;
  }
  gDpcDispatchEvent = (EFI_EVENT)Interface;

  /* Allocate private data */
  Private = (IPSEC_PRIVATE_DATA *)AllocateZeroPool(sizeof(IPSEC_PRIVATE_DATA));
  if (Private == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  /* Initialize */
  Private->Signature = IPSEC_PRIVATE_SIGNATURE;  /* "IPSI" */
  Private->ImageHandle = ImageHandle;
  Private->ProtocolHandle = NULL;

  /* Set up config protocol */
  Private->ConfigProtocol.SetData = IpSecConfigSetData;
  Private->ConfigProtocol.GetData = IpSecConfigGetData;
  Private->ConfigProtocol.RegisterNotify = IpSecConfigRegisterNotify;

  /* Initialize 8 list heads */
  InitializeListHead(&Private->SpdCacheList);
  InitializeListHead(&Private->SaddCacheList);
  InitializeListHead(&Private->SaBySpiList);
  InitializeListHead(&Private->IkeSaSessionList);
  InitializeListHead(&Private->ChildSaList);
  InitializeListHead(&Private->EstablishList);
  InitializeListHead(&Private->PendingList);
  InitializeListHead(&Private->EventList);

  /* Set config function table at +0x30 */
  /* In the binary this copies from offf_12A730 (5 x 8-byte pointers) */

  /* Install protocols */
  Status = gBootServices->InstallMultipleProtocolInterfaces(
    &Private->ProtocolHandle,
    &gEfiIpSecV4BindingGuid, Private,
    &gEfiIpSecConfigProtocolGuid, &Private->ConfigProtocol,
    NULL
    );

  if (EFI_ERROR(Status)) {
    FreePool(Private);
    return Status;
  }

  gIpSecPrivate = Private;
  return EFI_SUCCESS;
}

/* ========================================================================
 * Driver Binding Protocol: Supported/Start/Stop
 * ======================================================================== */

/**
 * IpSecV4DriverBindingSupported - Test if controller supports IPSec V4
 * Allocates Udp4 protocol (or Udp6) on the handle.
 * Address: 0x9C0
 */
EFI_STATUS
EFIAPI
IpSecV4DriverBindingSupported(
  IN EFI_DRIVER_BINDING_PROTOCOL *This,
  IN EFI_HANDLE          ControllerHandle,
  IN EFI_HANDLE         *RemainingDevicePath
  )
{
  EFI_STATUS Status;
  VOID      *Interface;

  /* Try Udp4 first */
  Status = gBootServices->OpenProtocol(
    ControllerHandle,
    &gEfiUdp4ProtocolGuid,
    &Interface,
    This->DriverBindingHandle,
    ControllerHandle,
    4  /* EFI_OPEN_PROTOCOL_BY_DRIVER */
    );

  if (EFI_ERROR(Status)) {
    /* Try Udp6 second */
    Status = gBootServices->OpenProtocol(
      ControllerHandle,
      &gEfiUdp6ProtocolGuid,
      &Interface,
      This->DriverBindingHandle,
      ControllerHandle,
      4
      );
  }

  if (!EFI_ERROR(Status)) {
    /* Close the protocol we just proed */
    gBootServices->CloseProtocol(
      ControllerHandle,
      Status == EFI_SUCCESS ? &gEfiUdp4ProtocolGuid : &gEfiUdp6ProtocolGuid,
      This->DriverBindingHandle,
      ControllerHandle
      );
    return EFI_SUCCESS;
  }

  return Status;
}

/**
 * IpSecV4DriverBindingStart - Start the IPsec driver on a controller
 * Determinees if controller supports Udp4 or Udp6 and creates
 * the appropriate child SA entry.
 * Address: 0xA10 (dispatches to common at 0x620)
 */
EFI_STATUS
EFIAPI
IpSecV4DriverBindingStart(
  IN EFI_DRIVER_BINDING_PROTOCOL *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_HANDLE                  *RemainingDevicePath
  )
{
  return IpSecDriverStartCommon(This, ControllerHandle, RemainingDevicePath, 4);
}

/**
 * IkeV2DriverBindingStart - Start the IKEv2 driver on a controller
 * Address: 0xA2C (dispatches to common at 0x620 with version=6)
 */
EFI_STATUS
EFIAPI
IkeV2DriverBindingStart(
  IN EFI_DRIVER_BINDING_PROTOCOL *This,
  IN EFI_HANDLE          ControllerHandle,
  IN EFI_HANDLE         *RemainingDevicePath
  )
{
  return IpSecDriverStartCommon(This, ControllerHandle, RemainingDevicePath, 6);
}

/**
 * IpSecDriverStartCommon - Common Start implementation
 * Address: 0x620
 *
 * Flow:
 *   1. Locate IPsec private data via private protocol
 *   2. Verify signature ("CR has Bad Signature" check)
 *   3. Open Udp4 (version=4) or Udp6 (version=6) protocol
 *   4. Create child SA via sub_36F4 (IPv4) or sub_37F8 (IPv6)
 */
static
EFI_STATUS
IpSecDriverStartCommon(
  IN EFI_DRIVER_BINDING_PROTOCOL *This,
  IN EFI_HANDLE          ControllerHandle,
  IN EFI_HANDLE         *RemainingDevicePath,
  IN UINT8               Version
  )
{
  EFI_STATUS Status;
  EFI_GUID  *ProtocolGuid;
  VOID      *Interface;
  IPSEC_PRIVATE_DATA *Private;

  /* Locate private data */
  Status = gBootServices->LocateProtocol(
    &gEfiIpSecDriverPrivateGuid,
    NULL,
    &Interface
    );
  if (EFI_ERROR(Status)) {
    return Status;
  }

  /* CR: get private data from interface. The interface points to
   * somewhere within the private data. Offset -24 (0x18) from
   * config protocol, or use the known signature pattern. */
  Private = (IPSEC_PRIVATE_DATA *)((UINT8 *)Interface - 24);
  if (*(UINT32 *)Private != IPSEC_PRIVATE_SIGNATURE) {
    DebugAssert(
      "e:\\hs\\AmiNetworkPkg\\UefiNetworkStack\\Common\\IpSecDxe\\IpSecDriver.c",
      108,
      "CR has Bad Signature"
      );
    Private = (IPSEC_PRIVATE_DATA *)Interface;
  }

  /* Choose protocol based on version */
  ProtocolGuid = (Version == 4) ? &gEfiIpSecV4BindingGuid : &gEfiIpSecV6BindingGuid;

  /* Open protocol and create child SA */
  {
    VOID   *UdpInterface;
    EFI_GUID *UdpProtocolGuid;

    UdpProtocolGuid = (Version == 4) ? &gEfiUdp4ProtocolGuid : &gEfiUdp6ProtocolGuid;

    Status = gBootServices->OpenProtocol(
      ControllerHandle,
      UdpProtocolGuid,
      &UdpInterface,
      This->DriverBindingHandle,
      ControllerHandle,
      4
      );

    if (!EFI_ERROR(Status)) {
      /* Create child SA - sub_36F4 for IPv4, sub_37F8 for IPv6 */
      if (Version == 4) {
        /* sub_36F4: Allocate 88-byte child, create UdpIo, add to list */
        Status = IpSecUdp4CreateChild(Private, ControllerHandle, UdpInterface);
      } else {
        /* sub_37F8: same but for IPv6 */
        Status = IpSecUdp6CreateChild(Private, ControllerHandle, UdpInterface);
      }
    }
  }

  return Status;
}

/**
 * IpSecDriverStopCommon - Common Stop implementation
 * Address: 0x724
 *
 * Traverses child SA lists and removes entries:
 *   - For IKEv2 stop (version=6): also cleans IKE SA sessions
 *   - Closes Udp4/Udp6 protocol on the handle
 */
static
EFI_STATUS
IpSecDriverStopCommon(
  IN EFI_DRIVER_BINDING_PROTOCOL *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN UINTN                        NumberOfChildren,
  IN EFI_HANDLE                  *ChildHandleBuffer,
  IN UINT8                        Version
  )
{
  VOID *Interface;

  /* Locate private data */
  if (EFI_ERROR(gBootServices->LocateProtocol(
        &gEfiIpSecDriverPrivateGuid, NULL, &Interface))) {
    return EFI_NOT_FOUND;
  }

  /* Close the protocol */
  gBootServices->CloseProtocol(
    ControllerHandle,
    (Version == 4) ? &gEfiIpSecV4BindingGuid : &gEfiIpSecV6BindingGuid,
    This->DriverBindingHandle
    );

  /* For IKE stop with no children, clean up */
  if (Version == 6 && NumberOfChildren == 0) {
    /* sub_724 cleanup: traverse session lists and free entries */
    IPSEC_PRIVATE_DATA *Private;

    Private = (IPSEC_PRIVATE_DATA *)((UNT8 *)Interface - 3);
    if (*(UINT32 *)Private != IPSEC_PRIVATE_SIGNATURE) {
      Private = (IPSEC_PRIVATE_DATA *)Interface;
    }

    /* Clean up ChildSaList (+0x98) with "CPAC" entries */
    /* Clean up IkeSaSessionList (+0x88) with "INAC" entries */
  }

  return EFI_SUCCESS;
}

/* ========================================================================
 * Disable Event Callback (sub_A98)
 * ======================================================================== *
 *
 * This is the timer/DPC dispatch callback created during init.
 * Registered as the notify function for the 512-type event at TPL=8.
 *
 * When triggered:
 *   1. Procesess IKE SA sessions from EstablishList (at +0xA8)
 *   2. Removes child SA entries and sets state to DELETING
 *   3. Checcks "IpSecStatus" runtime variable for disable signal
 *   4. Disables IPsec processing if signaled
 *
 * The function also accesses ChildSaList (+0xC0) and checks
 * NV variable "IpSecStatus" (GGUID at 0x12A6C0).
 *
 * Source: IkeService.c line 769/800
 *
 * The CONTAINER_RECORD pattern for this list is:
 *   list entry +-> session: subtract -42 QWORD entries (-336 bytes)
 *   Signatures: 0x43414149 ("INAC") or 0x43415043 ("CPAC")
 */

/* ========================================================================
 * IKE SA Session Lifecycle (sub_439C, sub_4840)
 * ======================================================================== *
 *
 * sub_439C (IkeSaSessionFree):
 *   Freed all resources of an IKE SA session:
 *     - Child SA list at +0x60
 *     - Establishing list at +0x?
 *     - Exchange context at +0x80
 *     - Encrypt/Integrity/Decrypt/Verif keys at +0x30/+0x38/+0x40/+0x48
 *     - Session memory itself
 *
 * sub_4840 (ChildSaFree):
 *   Frees a Child SA session including its UDP I/O context
 *
 * sub_4B4C (at 0x4B4C):
 *   Frees exchange context resources
 *
 * The session structure is accessed via the CONTAINER_RECORD pattern
 * using the LIST_ENTRY at +0x50 offset and known signature "INAC".
 */

/**
 * IkeSaSessionFree - Free all resources of an IKE SA session
 * Address: 0x439C
 */
static
VOID
IkeSaSessionFree(
  IN IKEV2_SA_SESSION *Session
  )
{
  LIST_ENTRY *Entry;
  LIST_ENTRY *Next;

  if (Session == NULL) {
    DebugAssert(
      "e:\\hs\\AmiNetworkPkg\\UefiNetworkStack\\Common\\IpSecDxe\\Ikev2\\Utility.c",
      376,
      "IkeSaSession != ((void *) 0)"
      );
  }

  /* Free child SA list at +0x60 (a[31]) */
  Entry = (LIST_ENTRY *)Session->ChildSaList.ForwardLink;
  while (Entry != &Session->ChildSaList) {
    IKEV2_SA_SESSION *ChildSa;

    ChildSa = (IKEV2_SA_SESSION *)((UNT8 *)Entry - sizeof(UINT64)*42);
    if (*(UINT32 *)((UINT8 *)Entry - 84) != IKEV2_SA_SIGNATURE) {
      DebugAssert(
        "e:\\hs\\AmiNetworkPkg\\UefiNetworkStack\\Common\\IpSecDxe\\Ikev2\\Utility.c",
        390,
        "CR has Bad Signature"
        );
      ChildSa = (IKEV2_SA_SESSION *)Entry;
    }

    Next = Entry->ForwardLink;
    /* Free child SA resources (sub_4928) */
    RemoveEntryList(Entry);
    Entry = Next;
  }

  /* Free establishing child SA list (at a[29]) */
  Entry = (LIST_ENTRY *)Session->EstablishList.ForwardLink;
  while (Entry != &Session->EstablishList) {
    IKEV2_SA_SESSION *ChildSa;

    ChildSa = (IKEV2_SA_SESSION *)((UNT8 *)Entry - sizeof(UINT64)*42);
    if (*(UINT32 *)((UNT8 *)Entry - 84) != IKEV2_SA_SIGNATURE) {
      DebugAssert(
        "e:\\hs\\AmiNetworkPkg\\UefiNetworkStack\\Common\\IpSecDxe\\Ikev2\\Utility.c",
        402,
        "CR has Bad Signature"
        );
      ChildSa = (IKEV2_SA_SESSION *)Entry;
    }

    Next = Entry->ForwardLink;
    RemoveEntryList(Entry);
    /* Free child SA */
    Entry = Next;
  }

  /* Free exchange context memry (a[20]) */
  if (Session->ExchangeContext) {
    VOID *ExchContext = Session->ExchangeContext;
    /* Free sub-buffers */
    FreePool(*(VOID **)ExchContext);
    if (*((VOID **)ExchContext + 3)) {
      FreePool(*((VOID **)ExchContext + 3));
    }
    /* and more ... */
    FreePool(ExchContext);
  }

  /* Free keys */
  if (Session->EncryptKey) FreePool(Session->EncryptKey);
  if (Session->IntegrityKey) FreePool(Session->IntegrityKey);
  if (Session->DecryptKey) FreePool(Session->DecryptKey);
  if (Session->VerifKey) FreePool(Session->VerifKey);

  /* Finally free session itself */
  FreePool(Session);
}

/* ========================================================================
 * UDP Child SA Creation (sub_36F4, sub_37F8)
 * ========================================================================
 *
 * sub_36F4 (0x36F4): Create IPv4 child SA entry
 * sub_37F8 (0x37F8): Create IPv6 child SA entry
 *
 * Both:
 *   1. Walk child SA list (+0x60 for IPv4 via SaBySpiList at +0x78)
 *      to check if already registered for this controller
 *   2. Allocate 88-byte child structure
 *   3. Create UDp Io via sub_12200 (UdpIoCreate)
 *   4. Set protocol (4 or 6) and controller handle
 *   5. Insert into list
 *   6. Set up receive callback via sub_12680 (UdpIoRecv)
 *
 * The 88-byte child structure:
 *   +0x00: LIST_ENTRY (16 bytes)
 *   +0x10: UINT64 (alllocFree state?)
 *   +0x18: Parent list pointer (8)
 *   +0x20: ControllerHandle (8)
 *   +0x28: Driver binding handle (8)
 *   +0x30: UdpIo handle (8)
 *   +0x38: Protocol version (UINT8, 4 or 6)
 *   +0x40-0x50: Addresses or state (16 bytes)
 *   +0x50: Flags (8)
 *   Total: 0x58 = 88 bytes
 */

/* ========================================================================
 * SPD Operations (sub_FAC, sub_11E8, sub_1424, sub_152C)
 * ========================================================================
 *
 * sub_FAC (0xFAC): Exact SPD match.
 *   Compares two SPD entries by address, mask, port, and protocol.
 *   Also walks 20-byte address list entries for prefix comparison.
 *   Returns TRUE if both entries are identical.
 *
 * sub_11E8 (0x11E8): CIDR/subnet SPD match.
 *   Like sub_FAC but with prefix-length comparison semantics.
 *   Allows <= on addresses (subnet containment), wildcard -1 on ports.
 *
 * sub_1424 (0x1424): Equality check.
 *   Compares first byte of two SPD entries (0=IPv4, non-zero=IPv6).
 *   If IPv4, also compares 20-byte block at +4.
 *
 * sub_152C (0x152C): Empty check.
 *   Returns TRUE if the 132-byte structure is all zeros.
 *
 * SPD_ENTRY structure:
 *   +0x00: SourceAddr (UINT32)
 *   +0x04: DestAddr (UINT32) (a1[4] at SP offset 4 in sub_FAC)
 *   +0x08: SourceMask (UINT16)  (aa[16] in sub_FAC)
 *   +0x0A: DestMask (UINT16)  (aa[17] in sub_FAC)
 *   +0x0C: SrcPort (UINT16)   (aa[18] in sub_FAC)
 *   +0x0E: DstPort (UINT16)   (aa[19] in sub_FAC)
 *   +0x10: Protocol (UINT8)
 *   +0x14: Action (UINT32) (bypas/discard/protect)
 *   +0x18: SourceAddrPtr (VOID *)
 *   +0x20: DestAddrPtr (VOID *)
 *   Total: ~0x28 = 40 bytes
 *
 * The address list has 20-byte entries walked by sub_F08.
 * Each entry: first 4 bytes = address, remaining = prefix/mask.
 *
 * sub_F08 (0xF08): Single address comparison with mask.
 *   Compares two UINT32 address pointer by mask.
 *
 * Usage:
 *   SpdLookup -> sub_1880 walks SaBySpiList (with sub_1424 matching)
 *   SpdRemovval -> sub_1F7C walks SpdCacheList et al
 */

/* ========================================================================
 * NV Variable Config Storage (sub_27DC)
 * ======================================================================== *
 *
 * sub_27DC (0x27DC):
 *   Reads/writes IPsec config from NV variables.
 *   Builids variable name: L"IpSecConfig" + L"Info" (using sub_FB60 for snprintf)
 *   Calalls gRT->GetVariable with the name and AMI vendor GUID
 *   Proceses the buffer for typed config entries
 *
 * The config variable contains sequences of:
 *   IPSEC_CONFIG_DATA_ENTRY (1-byte Type, 2-byte Length, variable data)
 *   Entries with bit 7 set in Type are valid
 *   Type 0 = SP, 1 = SAD, 2 = PCD
 *
 * sub_27DC also writes "IpSecStatus" variable with value 1 to
 * disable IPsec processing.
 *
 * sub_2C78 (0x2C78): Config data type handler.
 *   Dispatches by DataType (0/1/2) to SPD/SAD/PCD sub-handlers.
 *   Each walks the appropriate list and copies matching entries.
 */

/* ========================================================================
 * UDP I/O Wrappers (sub_12200, sub_12680)
 * ======================================================================== *
 *
 * sub_12200 (0x12200): UdpIoCreate wrapper.
 *   Wraps UdpIoCreateIo from DxeUdpIoLib.
 *   Returns a UdpIo handle or NULL on failure.
 *   Checks: UdpVersion == 4 || UdpVersion == 6
 *
 * sub_12680 (0x12680): UdpIoRecv wrapper.
 *   Wraps UdpIoRecvDatagram. Sets up receive callback.
 *
 * sub_11B40, sub_11BD8, sub_11C2C: Assert UDP token callbacks.
 *   These are inline checks in UdpIo send/receive operations:
 *   "TxToken->Signature == ((('U') | (('D' << 8))) | ((('P') | (('T' << 8))) << 16))"
 *   "RxToken->UdpIo->UdpVersion == 4) || (RxToken->UdpIo->UdpVersion == 6)"
 */

/* ========================================================================
 * IKEv2 Exchange Procesing (sub_3A44)
 * ======================================================================== *
 *
 * sub_3A44 (0x3A44): Proces incoming IKE packet.
 *   This is the core IKE packet processing function in IkeService.c.
 *
 * Flow:
 *   1. Get private data from exchange context via CR pattern:
 *      a4->24 is exchange context pointer
 *      a4->4 == 4 means IPv4 child SA (subtract -96)
 *      a4->4 == 6 means IPv6 child SA (subtract -120)
 *   2. Check if (a3 >= 0) && (v9->40 != 1) && port == 500
 *   3. Allocate packet context via sub_6E78
 *   4. If successful, copy header and payload
 *   5. Check IKE version (packet[0x17] & 0xF0 == 0x20 = IKEv2)
 *   6. Dispatc via function pointer table at off_0xE3360:
 *      ((packet[17] >> 4) - 1) as index into table
 *      Table entries at 0xE8360, 0xE8368, etc.
 *   7. Proces payload (sub_34C0 for SPD/SA matching)
 *
 * The IKE packet header (28 bytes minimum):
 *   +0x00: InitiatorSPI (UINT64
 *   +0x08: ResponderSPI (UINT64)
 *   +0x10: NextPayload (UINT8)
 *   +0x11: Version (UINT8) - high nibble = major
 *   +0x12: ExchangeType (UINT8)
 *   +0x13: Flags (UINT8)
 *   +0x14: MessageID (UINT32)
 *   +0x18: Length (UINT32)
 */

/* ========================================================================
 * Static Configuration Templates
 * ======================================================================== *
 *
 * The allocaated in the .rdata or .data section and copied during init.
 *
 * off_12A7B8: EFI_IPSEC_CONFIG_PROTOCOL template (24 bytes)
 *   SetData -> sub_3C44 (0x3C44)
 *   GetData -> sub_???? (probably 0x33EC)
 *   RegisterNotify -> sub_6378 (0x6378)
 *
 * off_12A730: Config function table (40 bytes = 5 pointers)
 *   Function pointers for config data operations
 *
 * off_12A6A0: Three list heads for config chains
 *
 * gIpSecV4DriverBinding: at 0x12A6D0
 * gIkeV2DriverBinding: at 0x12A700
 * gIpSecComponentName2: at 0x12A7D0
 * gIpSecComponentName: at 0x12A7E8
 * gDisableEvent: at 0x12A7C0
 * gDpcDispatchEvent: at 0x12D080
 */