/*++
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;
}