Newer
Older
AMI-Aptio-BIOS-Reversed / Udp6Dxe / Udp6Dxe.c
@Ajax Dong Ajax Dong 2 days ago 48 KB Init
/*++

Udp6Dxe.c - Udp6Dxe module implementation

Source: AmiNetworkPkg/UefiNetworkStack/Ipv6/Udp6Dxe/
Files: Udp6Driver.c, Udp6Main.c, Udp6Impl.c

--*/

#include "Udp6Dxe.h"

/*------------------------------------------------------------------------------
 * Global variables
 *----------------------------------------------------------------------------*/

EFI_HANDLE gImageHandle        = NULL;
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBS         = NULL;
EFI_RUNTIME_SERVICES *gRT      = NULL;
EFI_HANDLE gControllerHandle   = NULL;
UINT16 gUdp6RandomPort         = 0;
EFI_HANDLE gNetworkStackHandle = NULL;

/* EFI_DRIVER_BINDING_PROTOCOL instance */
EFI_DRIVER_BINDING_PROTOCOL gUdp6DriverBinding = {
  Udp6DriverBindingSupported,
  Udp6DriverBindingStart,
  Udp6DriverBindingStop,
  0x10,
  NULL,
  NULL
};

/* EFI_COMPONENT_NAME_PROTOCOL instances (stubs) */
EFI_COMPONENT_NAME_PROTOCOL gUdp6ComponentName = {
  Udp6ComponentNameGetDriverName,
  Udp6ComponentNameGetControllerName,
  L"en"
};

EFI_COMPONENT_NAME2_PROTOCOL gUdp6ComponentName2 = {
  Udp6ComponentNameGetDriverName,
  Udp6ComponentNameGetControllerName,
  L"en"
};

/* EFI_UDP6_SERVICE_BINDING_PROTOCOL instance (installed per-service) */
EFI_SERVICE_BINDING_PROTOCOL gUdp6ServiceBindingTemplate = {
  Udp6ServiceBindingCreateChild,
  Udp6ServiceBindingDestroyChild
};

/*
 * Protocol function table template - copied into each UDP6_INSTANCE at +0x020.
 * The template bytes at 0x92E0 store in source-file order (matching how the
 * compiler laid out the initializer in Udp6Main.c), which happens to match
 * the EFI_UDP6_PROTOCOL struct member order from the UEFI spec:
 *   [0] +0x00: 0x244C -> GetModeData
 *   [1] +0x08: 0x253C -> Configure
 *   [2] +0x10: 0x290C -> Groups
 *   [3] +0x18: 0x2A58 -> Transmit
 *   [4] +0x20: 0x2D7C -> Receive (or Cancel)
 *   [5] +0x28: 0x2EC0 -> Cancel (or Receive)
 *   [6] +0x30: 0x2FF4 -> Poll
 */
STATIC CONST EFI_UDP6_PROTOCOL gUdp6ProtocolTemplate = {
  Udp6GetModeData,   /* +0x00 */
  Udp6Configure,     /* +0x08 */
  Udp6Groups,        /* +0x10 */
  Udp6Transmit,      /* +0x18 */
  Udp6Receive,       /* +0x20 */
  Udp6Cancel,        /* +0x28 */
  Udp6Poll           /* +0x30 */
};

/* Default Udp6 config data template (0x34 bytes) */
STATIC CONST EFI_UDP6_CONFIG_DATA gUdp6DefaultConfigData = {
  0,                    /* AcceptPromiscuous */
  0,                    /* AcceptAnyPort */
  0,                    /* AllowDuplicatePort */
  1,                    /* TrafficClass */
  0x11,                 /* HopLimit */
  {0, 0, 0},           /* FlowLabel */
  0,                    /* ReceiveTimeout */
  0,                    /* TransmitTimeout */
  0,                    /* StationPort */
  0,                    /* RemotePort */
  {{0}},                /* StationAddress */
  {{0}},                /* RemoteAddress */
  0,                    /* (pad) */
  0                     /* (pad) */
};

/*============================================================================*
 * Forward declarations of internal helpers from Udp6Impl.c
 *============================================================================*/

EFI_STATUS
Udp6CreateIp6Child (
  IN OUT UDP6_SERVICE  *Udp6Service
  );

VOID
Udp6DestroyIp6Child (
  IN UDP6_SERVICE  *Udp6Service
  );

/*============================================================================*
 * Component Name stubs
 *============================================================================*/

EFI_STATUS
EFIAPI
Udp6ComponentNameGetDriverName (
  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
  IN  UINT8                        Language,
  OUT CHAR16                       **DriverName
  )
{
  return EFI_UNSUPPORTED;
}

EFI_STATUS
EFIAPI
Udp6ComponentNameGetControllerName (
  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
  IN  EFI_HANDLE                   ControllerHandle,
  IN  EFI_HANDLE                   ChildHandle,
  IN  UINT8                        Language,
  OUT CHAR16                       **ControllerName
  )
{
  return EFI_UNSUPPORTED;
}

/*============================================================================*
 * Module Entry Point (0x698)
 *============================================================================*/

EFI_STATUS
EFIAPI
Udp6DriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  UINTN       VarSize;
  BOOLEAN     NetworkStackEnabled;

  //
  // Save global pointers
  //
  gImageHandle = ImageHandle;
  gSystemTable = SystemTable;
  gBS          = SystemTable->BootServices;
  gRT          = SystemTable->RuntimeServices;

  //
  // Query NetworkStackVar runtime variable to check if stack is enabled
  //
  VarSize = sizeof (BOOLEAN);
  Status = gRT->GetVariable (
                  L"NetworkStackVar",
                  &gNetworkStackVarGuid,
                  NULL,
                  &VarSize,
                  &NetworkStackEnabled
                  );
  if (EFI_ERROR (Status)) {
    return EFI_UNSUPPORTED;
  }

  if (!NetworkStackEnabled) {
    return EFI_UNSUPPORTED;
  }

  //
  // Save image handle for driver binding
  //
  gControllerHandle = ImageHandle;

  //
  // Install Driver Binding + Component Name protocols
  //
  gUdp6DriverBinding.ImageHandle         = ImageHandle;
  gUdp6DriverBinding.DriverBindingHandle = ImageHandle;

  Status = gBS->InstallMultipleProtocolInterfaces (
                  &ImageHandle,
                  &gEfiDriverBindingProtocolGuid,
                  &gUdp6DriverBinding,
                  &gEfiComponentNameProtocolGuid,
                  &gUdp6ComponentName,
                  &gEfiComponentName2ProtocolGuid,
                  &gUdp6ComponentName2,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
    return Status;
  }

  //
  // Compute random port base using timer-derived value
  //
  gUdp6RandomPort = (UINT16)((Udp6GetRandomValue () & 0x3FF) + 1024);

  return EFI_SUCCESS;
}

/*============================================================================*
 * Udp6Driver.c - Driver Binding Protocol + Service Binding
 *============================================================================*/

/*------------------------------------------------------------------------------
 * Udp6DriverBindingSupported (0x8F0)
 *------------------------------------------------------------------------------
 *
 * Tests whether the driver supports a given controller. Opens the
 * EFI_DRIVER_BINDING_PROTOCOL on the controller handle and checks if this
 * driver's handle matches any in the open protocol information.
 *
 * Source: Udp6Driver.c (line 220)
 *----------------------------------------------------------------------------*/

EFI_STATUS
EFIAPI
Udp6DriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  )
{
  EFI_OPEN_PROTOCOL_INFORMATION_ENTRY  *OpenInfoBuffer;
  UINTN                                OpenInfoCount;
  UINTN                                Index;
  EFI_STATUS                           Status;

  if (This == NULL || ControllerHandle == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Get open protocol information for the controller
  //
  Status = gBS->OpenProtocolInformation (
                  ControllerHandle,
                  &gEfiUdp6ServiceBindingProtocolGuid,
                  &OpenInfoBuffer,
                  &OpenInfoCount
                  );
  if (EFI_ERROR (Status) || OpenInfoCount == 0 || OpenInfoBuffer == NULL) {
    return EFI_UNSUPPORTED;
  }

  //
  // Walk the open info looking for our driver binding handle
  //
  for (Index = 0; Index < OpenInfoCount; Index++) {
    if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) &&
        OpenInfoBuffer[Index].AgentHandle == This->DriverBindingHandle) {
      gBS->FreePool (OpenInfoBuffer);
      return EFI_SUCCESS;
    }
  }

  gBS->FreePool (OpenInfoBuffer);
  return EFI_UNSUPPORTED;
}

/*------------------------------------------------------------------------------
 * Udp6DriverBindingStart (0x840 / 0x990 base)
 *------------------------------------------------------------------------------
 *
 * Starts the driver on a controller. Opens the Udp6ServiceBinding protocol
 * on the controller, checks the service signature, and either creates a new
 * child or handles the case where children already exist.
 * When no remaining device path, destroys all children and stops.
 *
 * Source: Udp6Driver.c (line 291)
 *----------------------------------------------------------------------------*/

EFI_STATUS
EFIAPI
Udp6DriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  )
{
  UDP6_SERVICE   *Udp6Service;
  EFI_HANDLE     ChildHandle;
  EFI_STATUS     Status;

  if (This == NULL || ControllerHandle == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Open the UDP6 service binding protocol on the controller
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiUdp6ServiceBindingProtocolGuid,
                  (VOID **)&Udp6Service,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  if (EFI_ERROR (Status)) {
    return EFI_UNSUPPORTED;
  }

  //
  // Verify service signature
  //
  ASSERT (Udp6Service->Signature == UDP6_SERVICE_SIGNATURE);

  if (RemainingDevicePath != NULL) {
    //
    // Create a new child instance via the service binding protocol
    //
    Status = Udp6Service->ServiceBinding.CreateChild (
                            &Udp6Service->ServiceBinding,
                            &ChildHandle
                            );
    return Status;
  }

  //
  // No remaining path - check if we have children to destroy
  //
  if (!IsListEmpty (&Udp6Service->InstanceList)) {
    return EFI_ALREADY_STARTED;
  }

  //
  // No instances - tear down the service
  //
  gBS->CloseProtocol (
         ControllerHandle,
         &gEfiUdp6ProtocolGuid,
         This->DriverBindingHandle,
         ControllerHandle
         );
  IpIoDestroy (Udp6Service->IpIo);
  Udp6Service->IpIo = NULL;
  ZeroMem (Udp6Service, sizeof (UDP6_SERVICE));
  gBS->FreePool (Udp6Service);

  return EFI_SUCCESS;
}

/*------------------------------------------------------------------------------
 * Udp6DriverBindingStop (0xDF4)
 *------------------------------------------------------------------------------
 *
 * Stops the driver on a controller. Opens the service binding protocol,
 * destroys the instance (including uninstalling protocol, closing IP6,
 * freeing IpInfo, removing from instance list, cleaning token maps).
 *
 * Source: Udp6Driver.c (line 497)
 *----------------------------------------------------------------------------*/

EFI_STATUS
EFIAPI
Udp6DriverBindingStop (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN UINTN                        NumberOfChildren,
  IN EFI_HANDLE                   *ChildHandleBuffer
  )
{
  UDP6_SERVICE   *Udp6Service;
  UDP6_INSTANCE  *Instance;
  EFI_STATUS     Status;

  if (This == NULL || ControllerHandle == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Locate the service binding protocol on the controller
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiUdp6ServiceBindingProtocolGuid,
                  (VOID **)&Udp6Service,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  if (EFI_ERROR (Status)) {
    return EFI_UNSUPPORTED;
  }

  //
  // CR: get UDP6_SERVICE from ServiceBinding pointer
  //
  Udp6Service = UDP6_SERVICE_FROM_PROTOCOL (Udp6Service);

  //
  // Already being destroyed?
  //
  if (Udp6Service->InDestroy) {
    return EFI_SUCCESS;
  }

  Udp6Service->InDestroy = TRUE;

  //
  // Close the IP6 protocol on the IpIo child handle
  //
  gBS->CloseProtocol (
         Udp6Service->IpIo->ChildHandle,
         &gEfiIp6ProtocolGuid,
         gImageHandle,
         Udp6Service->ControllerHandle
         );

  //
  // Destroy the IpInfo context via IpIo library
  //
  Udp6DestroyIp6Child (Udp6Service);

  //
  // Remove from instance list
  //
  RemoveEntryList (&Udp6Service->InstanceList.ForwardLink);

  //
  // Decrement instance count
  //
  Udp6Service->InstanceCount--;

  //
  // Clean up token maps (Tx, Rx, Mcast)
  //
  NetMapClean (&Udp6Service->TxTokens);
  NetMapClean (&Udp6Service->RxTokens);
  NetMapClean (&Udp6Service->McastIps);

  //
  // Free the service structure
  //
  gBS->FreePool (Udp6Service);

  return EFI_SUCCESS;
}

/*------------------------------------------------------------------------------
 * Udp6ServiceBindingCreateChild (via sub_B50)
 *------------------------------------------------------------------------------
 *
 * Creates a new UDP6 child instance. Allocates a UDP6_INSTANCE (0x158 bytes),
 * initializes its fields, installs the EFI_UDP6_PROTOCOL interface, and
 * opens the IP6 protocol as a child controller.
 *
 * Source: Udp6Driver.c (line 361)
 *----------------------------------------------------------------------------*/

EFI_STATUS
EFIAPI
Udp6ServiceBindingCreateChild (
  IN  EFI_SERVICE_BINDING_PROTOCOL  *This,
  OUT EFI_HANDLE                    *ChildHandle
  )
{
  UDP6_SERVICE   *Udp6Service;
  UDP6_INSTANCE  *Instance;
  VOID           *IpInfo;
  EFI_STATUS     Status;

  if (This == NULL || ChildHandle == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Udp6Service = UDP6_SERVICE_FROM_PROTOCOL (This);

  //
  // Allocate zero-filled instance (0x158 bytes)
  //
  Instance = AllocateZeroPool (sizeof (UDP6_INSTANCE));
  if (Instance == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  Instance->Signature = UDP6_INSTANCE_SIGNATURE;
  Instance->Service   = Udp6Service;
  InitListHead (&Instance->ServiceLink);
  InitListHead (&Instance->DeliveredDgramQue);
  NetMapInit (&Instance->TxTokens);
  NetMapInit (&Instance->RxTokens);
  NetMapInit (&Instance->McastIps);

  //
  // Copy the protocol function table template
  //
  CopyMem (&Instance->Protocol, &gUdp6ProtocolTemplate, sizeof (EFI_UDP6_PROTOCOL));

  //
  // Initialize remaining fields
  //
  Instance->IsConfigured = FALSE;
  Instance->IsNoReceive  = FALSE;
  Instance->InDestroy    = FALSE;

  //
  // Create IP info context via IpIo library
  //
  IpInfo = IpIoAddIpInfo (Udp6Service->IpIo);
  if (IpInfo == NULL) {
    goto ERROR;
  }
  Instance->IpInfo = IpInfo;

  //
  // Install the UDP6 protocol interface on the child handle
  //
  Status = gBS->InstallProtocolInterface (
                  ChildHandle,
                  &gEfiUdp6ProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  &Instance->Protocol
                  );
  if (EFI_ERROR (Status)) {
    goto ERROR;
  }

  //
  // Open IP6 protocol on the child handle (by child controller)
  //
  Status = gBS->OpenProtocol (
                  IpIoGetChildHandle (Udp6Service->IpIo),
                  &gEfiIp6ProtocolGuid,
                  &Instance->Ip6,
                  gImageHandle,
                  *ChildHandle,
                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
                  );
  if (EFI_ERROR (Status)) {
    goto ERROR;
  }

  //
  // Link into service's instance list
  //
  InsertTailList (&Udp6Service->InstanceList, &Instance->ServiceLink);
  Udp6Service->InstanceCount++;

  return EFI_SUCCESS;

ERROR:
  if (Instance->IpInfo != NULL) {
    IpIoRemoveIpInfo (Udp6Service->IpIo, Instance->IpInfo);
  }
  gBS->FreePool (Instance);
  return EFI_OUT_OF_RESOURCES;
}

/*------------------------------------------------------------------------------
 * Udp6ServiceBindingDestroyChild (0x990)
 *------------------------------------------------------------------------------
 *
 * Destroys a UDP6 child instance. Validates via signature, removes from
 * service list, closes protocols, destroys IpInfo, cleans maps, frees.
 *
 * Source: Udp6Driver.c (line 291 - actually DestroyChild)
 *----------------------------------------------------------------------------*/

EFI_STATUS
EFIAPI
Udp6ServiceBindingDestroyChild (
  IN  EFI_SERVICE_BINDING_PROTOCOL  *This,
  IN  EFI_HANDLE                    ChildHandle
  )
{
  UDP6_INSTANCE  *Instance;
  VOID           *ProtocolInterface;
  EFI_STATUS     Status;

  if (This == NULL || ChildHandle == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Get the protocol interface on the child handle
  //
  Status = gBS->OpenProtocol (
                  ChildHandle,
                  &gEfiUdp6ProtocolGuid,
                  &ProtocolInterface,
                  gImageHandle,
                  ChildHandle,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  if (EFI_ERROR (Status)) {
    return EFI_DEVICE_ERROR;
  }

  //
  // CR: get UDP6_INSTANCE from protocol interface pointer
  // Instance = CONTAINER_OF (ProtocolInterface, UDP6_INSTANCE, Protocol)
  //
  Instance = UDP6_INSTANCE_FROM_PROTOCOL (ProtocolInterface);
  if (Instance->Signature != UDP6_INSTANCE_SIGNATURE) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Uninstall protocol interface
  //
  gBS->UninstallProtocolInterface (
         ChildHandle,
         &gEfiUdp6ProtocolGuid,
         ProtocolInterface
         );

  //
  // Close IP6 protocol opened as child controller
  //
  Status = gBS->CloseProtocol (
                  IpIoGetChildHandle (Instance->Service->IpIo),
                  &gEfiIp6ProtocolGuid,
                  gImageHandle,
                  ChildHandle
                  );
  if (EFI_ERROR (Status)) {
    // Continue cleanup even if close fails
  }

  //
  // Destroy IpInfo
  //
  IpIoRemoveIpInfo (Instance->Service->IpIo, Instance->IpInfo);

  //
  // Remove from instance list
  //
  RemoveEntryList (&Instance->ServiceLink);
  Instance->Service->InstanceCount--;

  //
  // Clean token maps
  //
  NetMapClean (&Instance->TxTokens);
  NetMapClean (&Instance->RxTokens);
  NetMapClean (&Instance->McastIps);

  //
  // Free instance
  //
  gBS->FreePool (Instance);

  return EFI_SUCCESS;
}

/*============================================================================*
 * Udp6Impl.c - Internal Implementation Helpers
 *============================================================================*/

/*------------------------------------------------------------------------------
 * Udp6CreateIp6Child (0xFE8)
 *------------------------------------------------------------------------------
 *
 * Creates the IP6 child and IpIo interface for a UDP6_SERVICE. Allocates
 * the IP IO context (0xA0 bytes), creates IP IO with version 6, registers
 * datagram receive and ICMP error callbacks, creates a periodic 500ms timer.
 *
 * Source: Udp6Impl.c
 *----------------------------------------------------------------------------*/

EFI_STATUS
Udp6CreateIp6Child (
  IN OUT UDP6_SERVICE  *Udp6Service
  )
{
  EFI_STATUS  Status;

  //
  // Zero-initialize the service structure
  //
  ZeroMem (Udp6Service, sizeof (UDP6_SERVICE));
  Udp6Service->Signature       = UDP6_SERVICE_SIGNATURE;
  Udp6Service->ImageHandle     = gImageHandle;
  gControllerHandle             = Udp6Service->ControllerHandle;
  InitListHead (&Udp6Service->InstanceList);
  Udp6Service->InstanceCount   = 0;

  //
  // Create the IP IO context
  //
  Udp6Service->IpIo = IpIoCreate (gImageHandle, Udp6Service->ControllerHandle, 6);
  if (Udp6Service->IpIo == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Register datagram received callback
  //
  Status = IpIoSetReceiveHandler (
             Udp6Service->IpIo,
             Udp6DgramRcvd,
             Udp6Service,
             0
             );
  if (EFI_ERROR (Status)) {
    goto ERROR;
  }

  //
  // Register ICMP error received callback
  //
  Status = IpIoSetIcmpErrorHandler (
             Udp6Service->IpIo,
             Udp6DgramRcvdIcmpError,
             Udp6Service
             );
  if (EFI_ERROR (Status)) {
    goto ERROR;
  }

  //
  // Configure the IpIo with default UDP6 config
  //
  Status = IpIoConfigure (
             Udp6Service->IpIo,
             &gUdp6DefaultConfigData,
             Udp6DgramSent,
             Udp6Service
             );
  if (EFI_ERROR (Status)) {
    goto ERROR;
  }

  //
  // Create periodic 500ms timer
  //
  Status = gBS->CreateEvent (
                  EVT_TIMER | EVT_NOTIFY_SIGNAL,
                  TPL_CALLBACK,
                  Udp6TimerHandler,
                  Udp6Service,
                  &Udp6Service->TimerEvent
                  );
  if (EFI_ERROR (Status)) {
    goto ERROR;
  }

  Status = gBS->SetTimer (
                  Udp6Service->TimerEvent,
                  TimerPeriodic,
                  500000     // 500ms
                  );
  if (EFI_ERROR (Status)) {
    gBS->CloseEvent (Udp6Service->TimerEvent);
    goto ERROR;
  }

  return EFI_SUCCESS;

ERROR:
  if (Udp6Service->TimerEvent != NULL) {
    gBS->CloseEvent (Udp6Service->TimerEvent);
  }
  if (Udp6Service->IpIo != NULL) {
    IpIoDestroy (Udp6Service->IpIo);
    Udp6Service->IpIo = NULL;
  }
  return Status;
}

/*------------------------------------------------------------------------------
 * Udp6DestroyIp6Child
 *------------------------------------------------------------------------------
 *
 * Destroys the IP6 child and IpIo interface for a UDP6_SERVICE.
 *----------------------------------------------------------------------------*/

VOID
Udp6DestroyIp6Child (
  IN UDP6_SERVICE  *Udp6Service
  )
{
  if (Udp6Service->IpIo != NULL) {
    gBS->CloseProtocol (
           Udp6Service->IpIo->ChildHandle,
           &gEfiIp6ProtocolGuid,
           gImageHandle,
           Udp6Service->ControllerHandle
           );
    IpIoDestroy (Udp6Service->IpIo);
    Udp6Service->IpIo = NULL;
  }
}

/*------------------------------------------------------------------------------
 * Udp6TimerHandler (0x120C)
 *------------------------------------------------------------------------------
 *
 * Periodic timer callback (500ms). Walks all UDP6 instances on the service
 * and decrements the Timeout counter on delivered datagrams by 50000 (50ms
 * worth of ticks). If a datagram's Timeout field drops below zero, recycles
 * the RxData.
 *
 * Source: Udp6Impl.c (line 437)
 *----------------------------------------------------------------------------*/

VOID
EFIAPI
Udp6TimerHandler (
  IN EFI_EVENT    Event,
  IN VOID         *Context
  )
{
  UDP6_SERVICE   *Udp6Service;
  LIST_ENTRY     *Entry;
  UDP6_INSTANCE  *Instance;
  LIST_ENTRY     *DgramEntry;
  UINT32         Timeout;

  Udp6Service = (UDP6_SERVICE *)Context;
  ASSERT (Udp6Service != NULL);
  ASSERT (Udp6Service->Signature == UDP6_SERVICE_SIGNATURE);

  //
  // Walk the instance list
  //
  for (Entry = Udp6Service->InstanceList.ForwardLink;
       Entry != &Udp6Service->InstanceList;
       Entry = Entry->ForwardLink) {

    Instance = CR (Entry, UDP6_INSTANCE, ServiceLink, UDP6_INSTANCE_SIGNATURE);
    ASSERT (Instance != NULL);
    ASSERT (Instance->Signature == UDP6_INSTANCE_SIGNATURE);

    //
    // Process delivered datagram queue timeouts
    // Each entry has a UINT32 timeout field at offset +0x18 in the queue item
    //
    if (Instance->IsConfigured && !IsListEmpty (&Instance->DeliveredDgramQue)) {
      DgramEntry = Instance->DeliveredDgramQue.ForwardLink;
      while (DgramEntry != &Instance->DeliveredDgramQue) {
        //
        // Read the timeout field from the RxData structure
        //
        Timeout = ((UINT32 *)((UINT8 *)DgramEntry - sizeof (LIST_ENTRY)))[6];
        if (Timeout >= 50000) {
          Timeout -= 50000;
          ((UINT32 *)((UINT8 *)DgramEntry - sizeof (LIST_ENTRY)))[6] = Timeout;
          DgramEntry = DgramEntry->ForwardLink;
        } else {
          //
          // Timeout expired: remove entry and recycle RxData
          //
          DgramEntry = DgramEntry->ForwardLink;
          RemoveEntryList (DgramEntry->Blink);
          Udp6RecycleRxData (Instance, DgramEntry->Blink);
        }
      }
    }
  }
}

/*------------------------------------------------------------------------------
 * Udp6DgramSent (0x171C)
 *------------------------------------------------------------------------------
 *
 * Callback invoked by IpIo when a UDP datagram is sent. If the callback
 * context (Udp6Service) is non-NULL, dispatches delivery to the receive
 * path for local echo. Otherwise dispatches to the delivered queue.
 *
 * Source: Udp6Impl.c (line 1047)
 *----------------------------------------------------------------------------*/

VOID
EFIAPI
Udp6DgramSent (
  IN VOID    *Context,
  IN BOOLEAN IsMulticast,
  IN VOID    *Packet,
  IN VOID    *Session,
  IN UINT8   IpProtocol
  )
{
  UDP6_SERVICE  *Udp6Service;

  Udp6Service = (UDP6_SERVICE *)Context;

  if (Udp6Service != NULL) {
    //
    // Dispatch as received datagram
    //
    Udp6DgramRcvd (Context, IsMulticast, Packet, Session, IpProtocol);
  } else {
    //
    // No service context - deliver directly
    //
    Udp6DeliverDgram (NULL, Packet, 0, NULL, 0, NULL);
  }
}

/*------------------------------------------------------------------------------
 * Udp6InstanceMatchDgram (0x18E8)
 *------------------------------------------------------------------------------
 *
 * Tests whether a received datagram matches an instance's configuration.
 * Checks AcceptPromiscuous, AcceptAnyPort, source/dest addresses, and
 * multicast group membership.
 *
 * Source: Udp6Impl.c (line 1989)
 *----------------------------------------------------------------------------*/

BOOLEAN
Udp6InstanceMatchDgram (
  IN UDP6_INSTANCE   *Instance,
  IN NET_BUF         *Packet,
  IN UINT16          SrcPort,
  IN EFI_IPv6_ADDRESS *SrcAddr,
  IN UINT16          DstPort,
  IN EFI_IPv6_ADDRESS *DstAddr
  )
{
  UINT16  InstanceRemotePort;
  UINT8   Index;

  //
  // Promiscuous mode: accept everything
  //
  if (Instance->ConfigData.AcceptPromiscuous) {
    return TRUE;
  }

  //
  // AcceptAnyPort: skip port matching
  //
  if (!Instance->ConfigData.AcceptAnyPort) {
    InstanceRemotePort = Instance->ConfigData.RemotePort;
  } else {
    InstanceRemotePort = 0;
  }

  //
  // Check remote port (if instance has one configured)
  //
  InstanceRemotePort = NTOHS (Instance->ConfigData.RemotePort);
  if (InstanceRemotePort != 0 && InstanceRemotePort != SrcPort) {
    return FALSE;
  }

  //
  // Check remote address (if not unspecified)
  //
  for (Index = 0; Index < 16; Index++) {
    if (Instance->ConfigData.RemoteAddress.Addr[Index] != 0) {
      break;
    }
  }
  if (Index < 16) {
    //
    // Remote address is specified - must match
    //
    if (CompareMem (
          &Instance->ConfigData.RemoteAddress,
          SrcAddr,
          sizeof (EFI_IPv6_ADDRESS)
          ) != 0) {
      return FALSE;
    }
  }

  //
  // Check local address (if not unspecified)
  //
  for (Index = 0; Index < 16; Index++) {
    if (Instance->ConfigData.StationAddress.Addr[Index] != 0) {
      break;
    }
  }
  if (Index < 16) {
    //
    // Station address is specified - must match destination
    //
    if (CompareMem (
          &Instance->ConfigData.StationAddress,
          DstAddr,
          sizeof (EFI_IPv6_ADDRESS)
          ) != 0) {
      return FALSE;
    }
  }

  //
  // If destination is multicast, check membership
  //
  if (DstAddr->Addr[0] == 0xFF) {
    return Udp6IsInMcastList (Instance, DstAddr);
  }

  return TRUE;
}

/*------------------------------------------------------------------------------
 * Udp6DeliverDgram (0x1CF8)
 *------------------------------------------------------------------------------
 *
 * Delivers a received datagram. Parses the UDP header, checks checksum,
 * strips the UDP header, and delivers to matching instances via the
 * internal delivery function.
 *
 * Source: Udp6Impl.c (line 1624)
 *----------------------------------------------------------------------------*/

EFI_STATUS
Udp6DeliverDgram (
  IN UDP6_INSTANCE   *Instance,
  IN NET_BUF         *Packet,
  IN UINT16          SrcPort,
  IN EFI_IPv6_ADDRESS *SrcAddr,
  IN UINT16          DstPort,
  IN EFI_IPv6_ADDRESS *DstAddr
  )
{
  EFI_UDP6_HEADER  *Udp6Header;
  UINT16           Checksum;

  if (Packet->TotalSize < sizeof (EFI_UDP6_HEADER)) {
    return NetbufFree (Packet);
  }

  Udp6Header = (EFI_UDP6_HEADER *)NetbufGetHeader (Packet, 0);
  if (Udp6Header == NULL) {
    return NetbufFree (Packet);
  }

  //
  // Verify checksum if present
  //
  if (Udp6Header->Checksum != 0) {
    Checksum = Udp6CalculateChecksum (
                 (UINT16 *)SrcAddr,
                 (UINT16 *)DstAddr,
                 Packet->TotalSize,
                 Udp6Header
                 );
    if (Checksum != 0 && Checksum != 0xFFFF) {
      //
      // Checksum failure - drop packet
      //
      return NetbufFree (Packet);
    }
  }

  //
  // Extract ports from header
  //
  SrcPort = NTOHS (Udp6Header->SrcPort);
  DstPort = NTOHS (Udp6Header->DstPort);

  //
  // Strip the UDP header from the packet
  //
  NetbufTrim (Packet, sizeof (EFI_UDP6_HEADER), NET_BUF_HEAD);

  //
  // Deliver to the matching instance
  //
  return Udp6InternalDeliver (Instance, Packet, &SrcAddr, SrcPort, &DstAddr, DstPort);
}

/*------------------------------------------------------------------------------
 * Udp6DgramRcvd (0x2018)
 *------------------------------------------------------------------------------
 *
 * Callback from the IpIo layer when a UDP datagram is received.
 * Parses the UDP header, extracts source/dest info, and walks
 * the instance list delivering to matching instances.
 *
 * Source: Udp6Impl.c (line 1872)
 *----------------------------------------------------------------------------*/

VOID
EFIAPI
Udp6DgramRcvd (
  IN VOID    *Context,
  IN BOOLEAN IsMulticast,
  IN VOID    *Packet,
  IN VOID    *Session,
  IN UINT8   IpProtocol
  )
{
  UDP6_SERVICE     *Udp6Service;
  LIST_ENTRY       *Entry;
  UDP6_INSTANCE    *Instance;
  NET_BUF          *Nbuf;
  EFI_UDP6_HEADER  *Udp6Header;

  Udp6Service = (UDP6_SERVICE *)Context;
  Nbuf = (NET_BUF *)Packet;

  if (Nbuf->TotalSize < sizeof (EFI_UDP6_HEADER)) {
    NetbufFree (Nbuf);
    return;
  }

  Udp6Header = (EFI_UDP6_HEADER *)NetbufGetHeader (Nbuf, 0);
  if (Udp6Header == NULL) {
    DEBUG ((EFI_D_ERROR, "Udp6DgramRcvd: Udp6Header != ((void *) 0)\n"));
    NetbufFree (Nbuf);
    return;
  }

  //
  // Walk all UDP6 instances on this service
  //
  for (Entry = Udp6Service->InstanceList.ForwardLink;
       Entry != &Udp6Service->InstanceList;
       Entry = Entry->ForwardLink) {

    Instance = CR (Entry, UDP6_INSTANCE, ServiceLink, UDP6_INSTANCE_SIGNATURE);
    ASSERT (Instance != NULL);

    if (Instance->IsConfigured) {
      //
      // Check if this instance wants this datagram
      //
      if (Udp6InstanceMatchDgram (
            Instance,
            Nbuf,
            NTOHS (Udp6Header->SrcPort),
            (EFI_IPv6_ADDRESS *)Session,   // Session contains IP6 addresses
            NTOHS (Udp6Header->DstPort),
            (EFI_IPv6_ADDRESS *)((UINT8 *)Session + sizeof (EFI_IPv6_ADDRESS))
            )) {
        //
        // Deliver the datagram to this instance
        //
        Udp6DeliverDgram (
          Instance,
          Nbuf,
          NTOHS (Udp6Header->SrcPort),
          (EFI_IPv6_ADDRESS *)Session,
          NTOHS (Udp6Header->DstPort),
          (EFI_IPv6_ADDRESS *)((UINT8 *)Session + sizeof (EFI_IPv6_ADDRESS))
          );
      }
    }
  }

  //
  // Free the NET_BUF
  //
  NetbufFree (Nbuf);
}

/*------------------------------------------------------------------------------
 * Udp6DgramRcvdIcmpError (0x1E60)
 *------------------------------------------------------------------------------
 *
 * Callback from the IpIo layer when an ICMPv6 error is received for a UDP
 * datagram. Constructs an ICMP error notification and sends it back to
 * the matching instance. Follows RFC 4443 section 3.1 (ICMPv6 errors).
 *
 * Source: Udp6Impl.c (line 1743)
 *----------------------------------------------------------------------------*/

VOID
EFIAPI
Udp6DgramRcvdIcmpError (
  IN VOID    *Context,
  IN BOOLEAN IsMulticast,
  IN VOID    *Packet,
  IN VOID    *Session,
  IN UINT8   IpProtocol
  )
{
  UDP6_SERVICE     *Udp6Service;
  NET_BUF          *Nbuf;
  VOID             *Ip6ModeData;
  UINT32            AllocSize;
  UINT32            Mtu;
  VOID             *IcmpErrHdr;
  VOID             *IcmpErrBuf;
  EFI_IP6_PROTOCOL *Ip6;

  Udp6Service = (UDP6_SERVICE *)Context;
  Nbuf = (NET_BUF *)Packet;

  //
  // Only handle ICMPv6 protocol errors (Protocol == 6 = ICMPv6)
  //
  if (IpProtocol != 6) {
    NetbufFree (Nbuf);
    return;
  }

  //
  // Allocate IP6 mode data to determine MTU
  //
  Ip6ModeData = AllocateZeroPool (sizeof (EFI_IP6_MODE_DATA));
  if (Ip6ModeData == NULL) {
    NetbufFree (Nbuf);
    return;
  }

  //
  // Get IP6 mode data (prefer instance-specific, fall back to service)
  //
  Ip6 = (EFI_IP6_PROTOCOL *)IpIoGetIp6Protocol (Udp6Service->IpIo);
  if (Ip6 != NULL) {
    Ip6->GetModeData (Ip6, Ip6ModeData, NULL, NULL, NULL);
  } else {
    CopyMem (Ip6ModeData, Udp6Service->IpIo->Ip6->ModeData, sizeof (EFI_IP6_MODE_DATA));
  }

  Mtu = ((EFI_IP6_MODE_DATA *)Ip6ModeData)->MaxPacketSize;

  //
  // Calculate allocation size: ICMP header + UDP header + data
  //
  AllocSize = sizeof (EFI_IP6_ICMP_ERROR_HEADER) + Nbuf->TotalSize;
  if (AllocSize > Mtu) {
    AllocSize = Mtu;
  }

  //
  // Build ICMP error notification
  //
  IcmpErrBuf = AllocatePool (AllocSize);
  if (IcmpErrBuf == NULL) {
    gBS->FreePool (Ip6ModeData);
    NetbufFree (Nbuf);
    return;
  }

  IcmpErrHdr = AllocatePool (AllocSize);
  if (IcmpErrHdr == NULL) {
    gBS->FreePool (IcmpErrBuf);
    gBS->FreePool (Ip6ModeData);
    NetbufFree (Nbuf);
    return;
  }

  //
  // Build the ICMP error header
  //
  *(UINT16 *)IcmpErrHdr = 0x0401;   // Type=4 (Time Exceeded), Code=1 (Fragment reassembly time exceeded)
  *((UINT16 *)IcmpErrHdr + 1) = 0; // Reserved / checksum (set to 0)

  //
  // Copy the original UDP datagram (payload portion)
  //
  CopyMem ((UINT8 *)IcmpErrHdr + 8, Nbuf->Data, Nbuf->TotalSize);

  //
  // Send the ICMP error via IpIo
  //
  IpIoSend (Udp6Service->IpIo, IcmpErrBuf, AllocSize, IcmpErrHdr);

  NetbufFree (Nbuf);
  gBS->FreePool (IcmpErrHdr);
  gBS->FreePool (IcmpErrBuf);
  gBS->FreePool (Ip6ModeData);
}

/*============================================================================*
 * Udp6Main.c - Protocol API Layer
 *============================================================================*/

/*------------------------------------------------------------------------------
 * Udp6GetModeData (at binary 0x244C, line 73)
 *------------------------------------------------------------------------------
 *
 * Returns the current mode data of the UDP6 instance. Copies the instance's
 * config data to the caller, then delegates to Ip6->GetModeData on the
 * instance's IP6 child protocol, forwarding the remaining output parameters.
 *
 * Source: Udp6Main.c (line 73)
 *----------------------------------------------------------------------------*/

EFI_STATUS
EFIAPI
Udp6GetModeData (
  IN  EFI_UDP6_PROTOCOL        *This,
  OUT EFI_UDP6_CONFIG_DATA     *Udp6ConfigData,
  OUT EFI_IP6_MODE_DATA        *Ip6ModeData,
  OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData,
  OUT EFI_SIMPLE_NETWORK_MODE  *SnpModeData
  )
{
  UDP6_INSTANCE  *Instance;

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

  Instance = UDP6_INSTANCE_FROM_PROTOCOL (This);

  if (!Instance->IsConfigured && Udp6ConfigData != NULL) {
    return EFI_NOT_STARTED;
  }

  //
  // Copy the instance's config data to the caller
  //
  if (Udp6ConfigData != NULL) {
    CopyMem (Udp6ConfigData, &Instance->ConfigData, sizeof (EFI_UDP6_CONFIG_DATA));
  }

  //
  // Delegate to IP6->GetModeData (the first function in EFI_IP6_PROTOCOL)
  //
  if (Instance->Ip6 != NULL) {
    return Instance->Ip6->GetModeData (
                            Instance->Ip6,
                            Ip6ModeData,
                            MnpConfigData,
                            SnpModeData
                            );
  }

  return EFI_SUCCESS;
}

/*------------------------------------------------------------------------------
 * Udp6Configure (at binary 0x253C, line 158)
 *------------------------------------------------------------------------------
 *
 * Configures or resets a UDP6 child instance. When UdpConfigData is
 * provided, validates the address/port combination, configures the
 * underlying IP6 layer, and stores config in the instance. When NULL,
 * resets to default state.
 *
 * Source: Udp6Main.c (line 158)
 *----------------------------------------------------------------------------*/

EFI_STATUS
EFIAPI
Udp6Configure (
  IN EFI_UDP6_PROTOCOL        *This,
  IN EFI_UDP6_CONFIG_DATA     *UdpConfigData OPTIONAL
  )
{
  UDP6_INSTANCE       *Instance;
  UDP6_SERVICE        *Udp6Service;
  EFI_STATUS          Status;
  EFI_IPv6_ADDRESS    LocalAddr;
  EFI_IPv6_ADDRESS    RemoteAddr;
  EFI_UDP6_CONFIG_DATA DefaultConfig;
  UINTN               Index;

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

  Instance = UDP6_INSTANCE_FROM_PROTOCOL (This);

  //
  // If not configured and no config data, just return success
  //
  if (!Instance->IsConfigured && UdpConfigData == NULL) {
    return EFI_SUCCESS;
  }

  Udp6Service = Instance->Service;
  ASSERT (Udp6Service != NULL);

  if (UdpConfigData != NULL) {
    //
    // Validate the config data
    //
    CopyMem (&LocalAddr, &UdpConfigData->StationAddress, sizeof (EFI_IPv6_ADDRESS));
    CopyMem (&RemoteAddr, &UdpConfigData->RemoteAddress, sizeof (EFI_IPv6_ADDRESS));

    //
    // Check for invalid addresses (multicast source = 0xFF)
    //
    for (Index = 0; Index < 16; Index++) {
      if (LocalAddr.Addr[Index] != 0) {
        break;
      }
    }
    if (Index < 16 && LocalAddr.Addr[0] == 0xFF) {
      return EFI_INVALID_PARAMETER;
    }

    for (Index = 0; Index < 16; Index++) {
      if (RemoteAddr.Addr[Index] != 0) {
        break;
      }
    }
    if (Index < 16 && RemoteAddr.Addr[0] == 0xFF) {
      return EFI_INVALID_PARAMETER;
    }

    //
    // If already configured with same data, just acknowledge
    //
    if (Instance->IsConfigured &&
        CompareMem (&Instance->ConfigData, UdpConfigData, sizeof (EFI_UDP6_CONFIG_DATA)) == 0) {
      Instance->IsNoReceive = FALSE;
      return EFI_ALREADY_STARTED;
    }

    //
    // If already configured, re-configure the IP6 layer
    //
    if (Instance->IsConfigured) {
      //
      // Update IP6 configuration
      //
      CopyMem (&DefaultConfig, &gUdp6DefaultConfigData, sizeof (EFI_UDP6_CONFIG_DATA));
      DefaultConfig.StationPort    = UdpConfigData->StationPort;
      DefaultConfig.RemotePort     = UdpConfigData->RemotePort;
      DefaultConfig.StationAddress = UdpConfigData->StationAddress;
      DefaultConfig.RemoteAddress  = UdpConfigData->RemoteAddress;

      Status = Instance->Ip6->Configure (Instance->Ip6, &DefaultConfig);
      if (EFI_ERROR (Status)) {
        if (Status == EFI_ALREADY_STARTED) {
          Instance->IsNoReceive = TRUE;
        }
        return Status;
      }
    }

    //
    // Copy config to instance
    //
    CopyMem (&Instance->ConfigData, UdpConfigData, sizeof (EFI_UDP6_CONFIG_DATA));

    //
    // Add instance to multicast group (if configured)
    //
    if (Instance->ConfigData.StationAddress.Addr[0] != 0) {
      Status = Udp6Groups (This, TRUE, &Instance->ConfigData.StationAddress);
      if (EFI_ERROR (Status)) {
        // Clean up IP6 config
        Instance->Ip6->Configure (Instance->Ip6, NULL);
        return Status;
      }
    }

    //
    // Compute checksum seed for this connection
    //
    Instance->ChecksumSeed = Udp6ComputeChecksumSeed (
                               &Instance->ConfigData.RemoteAddress,
                               &Instance->ConfigData.StationAddress
                               );
    Instance->IsConfigured = TRUE;
    Instance->IsNoReceive  = FALSE;

  } else {
    //
    // Reset: clear configuration
    //
    Instance->IsConfigured = FALSE;
    Instance->IsNoReceive  = FALSE;
    ZeroMem (&Instance->ConfigData, sizeof (EFI_UDP6_CONFIG_DATA));

    //
    // Tell IP6 layer to reset
    //
    if (Instance->Ip6 != NULL) {
      Instance->Ip6->Configure (Instance->Ip6, NULL);
    }

    //
    // Flush delivered datagram queue
    //
    while (!IsListEmpty (&Instance->DeliveredDgramQue)) {
      Udp6RecycleRxData (Instance, Instance->DeliveredDgramQue.ForwardLink);
    }
  }

  return EFI_SUCCESS;
}

/*------------------------------------------------------------------------------
 * Udp6Groups (at binary 0x290C, line 352)
 *------------------------------------------------------------------------------
 *
 * Joins (JoinFlag=TRUE) or leaves (JoinFlag=FALSE) a multicast group on
 * an instance. When joining, allocates a copy of the multicast address
 * and inserts into the instance's McastIps map. On leave, removes from
 * map. Delegates to Ip6->Groups for the actual IP6 multicast join/leave.
 *
 * Source: Udp6Main.c (line 352)
 *----------------------------------------------------------------------------*/

EFI_STATUS
EFIAPI
Udp6Groups (
  IN EFI_UDP6_PROTOCOL        *This,
  IN BOOLEAN                  JoinFlag,
  IN EFI_IPv6_ADDRESS         *MulticastAddress OPTIONAL
  )
{
  UDP6_INSTANCE   *Instance;
  EFI_IPv6_ADDRESS *McastAddrCopy;
  EFI_STATUS       Status;

  if (This == NULL || (JoinFlag && MulticastAddress == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  Instance = UDP6_INSTANCE_FROM_PROTOCOL (This);

  if (!Instance->IsConfigured) {
    return EFI_NOT_STARTED;
  }

  if (JoinFlag) {
    //
    // Validate: only all-nodes multicast join (addr[0] == 0xFF)
    //
    if (MulticastAddress->Addr[0] != 0xFF) {
      return EFI_INVALID_PARAMETER;
    }

    //
    // Allocate and insert multicast address into McastIps map
    //
    McastAddrCopy = AllocateCopyPool (sizeof (EFI_IPv6_ADDRESS), MulticastAddress);
    if (McastAddrCopy == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    Status = NetMapInsertTail (&Instance->McastIps, McastAddrCopy, NULL);
    if (EFI_ERROR (Status)) {
      gBS->FreePool (McastAddrCopy);
      return Status;
    }
  }

  //
  // Delegate multicast to IP6 layer
  //
  Status = Instance->Ip6->Groups (
                            Instance->Ip6,
                            JoinFlag,
                            MulticastAddress
                            );
  if (EFI_ERROR (Status)) {
    //
    // Clean up map entry if IP6 failed
    //
    if (JoinFlag) {
      NetMapRemoveTail (&Instance->McastIps, NULL);
    }
    return Status;
  }

  if (!JoinFlag) {
    //
    // Leaving all groups - clean up the map
    //
    NetMapRemoveTail (&Instance->McastIps, NULL);
  }

  return EFI_SUCCESS;
}

/*------------------------------------------------------------------------------
 * Udp6Transmit (at binary 0x2A58, line 475)
 *------------------------------------------------------------------------------
 *
 * Transmits a UDP datagram. Validates the token, builds the UDP header
 * with source/dest ports, computes the checksum, allocates a NET_BUF,
 * and sends via the IpIo->Send path.
 *
 * Source: Udp6Main.c (line 475)
 *----------------------------------------------------------------------------*/

EFI_STATUS
EFIAPI
Udp6Transmit (
  IN EFI_UDP6_PROTOCOL          *This,
  IN OUT EFI_UDP6_COMPLETION_TOKEN *Token
  )
{
  UDP6_INSTANCE       *Instance;
  EFI_UDP6_TX_DATA    *TxData;
  EFI_STATUS          Status;
  NET_BUF             *Nbuf;
  EFI_UDP6_HEADER     *Udp6Header;

  if (This == NULL || Token == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Instance = UDP6_INSTANCE_FROM_PROTOCOL (This);

  if (!Instance->IsConfigured) {
    return EFI_NOT_STARTED;
  }

  //
  // Validate the token
  //
  Status = Udp6ValidateTxToken (Instance, Token);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Check for duplicate token (already in TX or RX map)
  //
  if (NetMapFind (&Instance->TxTokens, Token) != NULL ||
      NetMapFind (&Instance->RxTokens, Token) != NULL) {
    return EFI_ACCESS_DENIED;
  }

  //
  // Get TX data from token
  //
  TxData = Token->Packet.TxData;

  //
  // Allocate NET_BUF with UDP header space + fragment data
  //
  Nbuf = NetbufAllocWithSize (
           sizeof (EFI_UDP6_HEADER) + TxData->DataLength,
           0,
           0
           );
  if (Nbuf == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Allocate UDP header fragment
  //
  Udp6Header = (EFI_UDP6_HEADER *)NetbufAllocFragment (Nbuf, sizeof (EFI_UDP6_HEADER), 1);
  if (Udp6Header == NULL) {
    NetbufFree (Nbuf);
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Build UDP header with ports in network byte order
  //
  Udp6Header->SrcPort  = HTONS (Instance->ConfigData.StationPort);
  Udp6Header->DstPort  = HTONS (Instance->ConfigData.RemotePort);
  Udp6Header->Length   = HTONS (sizeof (EFI_UDP6_HEADER) + TxData->DataLength);
  Udp6Header->Checksum = 0;

  //
  // Insert token into TX map
  //
  Status = NetMapInsertTail (&Instance->TxTokens, Token, Nbuf);
  if (EFI_ERROR (Status)) {
    NetbufFree (Nbuf);
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Send via IpIo
  //
  Status = IpIoSend (
             Instance->Service->IpIo,
             Nbuf,
             (VOID *)&Instance->ConfigData.RemoteAddress,
             (VOID *)&Instance->ConfigData.StationAddress,
             Token
             );
  if (EFI_ERROR (Status)) {
    NetMapRemoveTail (&Instance->TxTokens, NULL);
    NetbufFree (Nbuf);
    return Status;
  }

  //
  // Signal completion (I/O is synchronous in this implementation)
  //
  Token->Status = EFI_SUCCESS;
  gBS->SignalEvent (Token->Event);

  return EFI_SUCCESS;
}

/*------------------------------------------------------------------------------
 * Udp6Receive (at binary 0x2EC0, line 796)
 *------------------------------------------------------------------------------
 *
 * Receives a UDP datagram. Checks for duplicate tokens, inserts token
 * into the RxTokens map, recycles delivered datagrams, and polls the
 * IP IO layer for pending data. The actual data delivery happens
 * through the IpIo callback (Udp6DgramRcvd).
 *
 * Source: Udp6Main.c (line 796)
 *----------------------------------------------------------------------------*/

EFI_STATUS
EFIAPI
Udp6Receive (
  IN EFI_UDP6_PROTOCOL          *This,
  IN OUT EFI_UDP6_COMPLETION_TOKEN *Token
  )
{
  UDP6_INSTANCE  *Instance;
  EFI_STATUS     Status;

  if (This == NULL || Token == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Instance = UDP6_INSTANCE_FROM_PROTOCOL (This);

  if (!Instance->IsConfigured) {
    return EFI_NOT_STARTED;
  }

  //
  // Check for duplicate token in TxTokens or RxTokens maps
  //
  if (NetMapFind (&Instance->TxTokens, Token) != EFI_NOT_FOUND ||
      NetMapFind (&Instance->RxTokens, Token) != EFI_NOT_FOUND) {
    return EFI_ACCESS_DENIED;
  }

  //
  // Insert the token into RxTokens map
  //
  Status = NetMapInsertTail (&Instance->RxTokens, Token, NULL);
  if (EFI_ERROR (Status)) {
    return EFI_OUT_OF_RESOURCES;
  }

  return EFI_SUCCESS;
}

/*------------------------------------------------------------------------------
 * Udp6Cancel (at binary 0x2D7C, line 705)
 *------------------------------------------------------------------------------
 *
 * Cancels a pending receive token. Searches the RxTokens map for the
 * token, removes it, recycles delivered datagrams, and polls the
 * IP IO layer. Sets Token->Status to EFI_SUCCESS on successful cancel.
 *
 * Source: Udp6Main.c (line 705)
 *----------------------------------------------------------------------------*/

EFI_STATUS
EFIAPI
Udp6Cancel (
  IN EFI_UDP6_PROTOCOL          *This,
  IN EFI_UDP6_COMPLETION_TOKEN  *Token OPTIONAL
  )
{
  UDP6_INSTANCE  *Instance;
  EFI_STATUS     Status;

  if (This == NULL || Token == NULL || Token->Event == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Instance = UDP6_INSTANCE_FROM_PROTOCOL (This);

  if (!Instance->IsConfigured) {
    return EFI_NOT_STARTED;
  }

  //
  // Find the token in RxTokens map; if not found, return error
  //
  if (NetMapFind (&Instance->RxTokens, Token) == EFI_NOT_FOUND ||
      NetMapFind (&Instance->TxTokens, Token) == EFI_NOT_FOUND) {
    return EFI_NOT_FOUND;
  }

  //
  // Remove the token from RxTokens map
  //
  Status = NetMapRemoveItem (&Instance->RxTokens, Token, NULL);
  if (EFI_ERROR (Status)) {
    return EFI_ACCESS_DENIED;
  }

  //
  // Recycle delivered datagrams and poll the IP IO layer
  //
  Udp6RecycleDeliveredDgram (Instance);
  Udp6Poll (This);

  return EFI_SUCCESS;
}

/*------------------------------------------------------------------------------
 * Udp6Poll (at binary 0x2FF4, line 846)
 *------------------------------------------------------------------------------
 *
 * Polls for incoming packets. Validates the instance, then delegates to
 * Ip6->GetModeData on the instance's IP6 child protocol.
 *
 * Note: The binary's Poll function calls Ip6->GetModeData rather than
 * Ip6->Poll, which triggers the IP6 layer to process any pending data.
 *
 * Source: Udp6Main.c (line 846)
 *----------------------------------------------------------------------------*/

EFI_STATUS
EFIAPI
Udp6Poll (
  IN EFI_UDP6_PROTOCOL  *This
  )
{
  UDP6_INSTANCE  *Instance;

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

  Instance = UDP6_INSTANCE_FROM_PROTOCOL (This);

  //
  // Delegate to IP6->GetModeData to trigger packet processing
  //
  if (Instance->Ip6 != NULL) {
    return Instance->Ip6->GetModeData (Instance->Ip6);
  }

  return EFI_SUCCESS;
}