From: vanjeff Date: Tue, 24 Jul 2007 08:06:37 +0000 (+0000) Subject: Import SnpDxe, Tcp4Dxe, Udp4Dxe and MnpDxe. X-Git-Tag: edk2-stable201903~22545 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=8a67d61da4d5a8f08a656cbeea2d902d0ad9042a Import SnpDxe, Tcp4Dxe, Udp4Dxe and MnpDxe. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3416 6f19259b-4bc3-4df7-8a09-765794883524 --- diff --git a/MdeModulePkg/Include/Library/IpIoLib.h b/MdeModulePkg/Include/Library/IpIoLib.h index 15cfbf8f43..894e07bed4 100644 --- a/MdeModulePkg/Include/Library/IpIoLib.h +++ b/MdeModulePkg/Include/Library/IpIoLib.h @@ -171,7 +171,7 @@ typedef struct _IP_IO_SEND_ENTRY { EFI_IP4_COMPLETION_TOKEN *SndToken; } IP_IO_SEND_ENTRY; -typedef struct _EFI_IP4_OVERRIDE_DATA IP_IO_OVERRIDE; +typedef EFI_IP4_OVERRIDE_DATA IP_IO_OVERRIDE; typedef struct _IP_IO_IP_INFO { IP4_ADDR Addr; diff --git a/MdeModulePkg/Include/Library/NetLib.h b/MdeModulePkg/Include/Library/NetLib.h index 215a2fb094..6bc3431855 100644 --- a/MdeModulePkg/Include/Library/NetLib.h +++ b/MdeModulePkg/Include/Library/NetLib.h @@ -809,4 +809,79 @@ NetPseudoHeadChecksum ( IN UINT8 Proto, IN UINT16 Len ); + +// +// The debug level definition. This value is also used as the +// syslog's servity level. Don't change it. +// +enum { + NETDEBUG_LEVEL_TRACE = 5, + NETDEBUG_LEVEL_WARNING = 4, + NETDEBUG_LEVEL_ERROR = 3, +}; + +#ifdef EFI_NETWORK_STACK_DEBUG + +// +// The debug output expects the ASCII format string, Use %a to print ASCII +// string, and %s to print UNICODE string. PrintArg must be enclosed in (). +// For example: NET_DEBUG_TRACE ("Tcp", ("State transit to %a\n", Name)); +// +#define NET_DEBUG_TRACE(Module, PrintArg) \ + NetDebugOutput ( \ + NETDEBUG_LEVEL_TRACE, \ + Module, \ + __FILE__, \ + __LINE__, \ + NetDebugASPrint PrintArg \ + ) + +#define NET_DEBUG_WARNING(Module, PrintArg) \ + NetDebugOutput ( \ + NETDEBUG_LEVEL_WARNING, \ + Module, \ + __FILE__, \ + __LINE__, \ + NetDebugASPrint PrintArg \ + ) + +#define NET_DEBUG_ERROR(Module, PrintArg) \ + NetDebugOutput ( \ + NETDEBUG_LEVEL_ERROR, \ + Module, \ + __FILE__, \ + __LINE__, \ + NetDebugASPrint PrintArg \ + ) + +#else +#define NET_DEBUG_TRACE(Module, PrintString) +#define NET_DEBUG_WARNING(Module, PrintString) +#define NET_DEBUG_ERROR(Module, PrintString) +#endif + +UINT8 * +NetDebugASPrint ( + UINT8 *Format, + ... + ); + +EFI_STATUS +NetDebugOutput ( + UINT32 Level, + UINT8 *Module, + UINT8 *File, + UINT32 Line, + UINT8 *Message + ); + +// +// Network debug message is sent out as syslog. +// +enum { + NET_SYSLOG_FACILITY = 16, // Syslog local facility local use + NET_SYSLOG_PACKET_LEN = 512, + NET_DEBUG_MSG_LEN = 470, // 512 - (ether+ip+udp head length) + NET_SYSLOG_TX_TIMEOUT = 500 *1000 *10, // 500ms +}; #endif diff --git a/MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf b/MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf index 9430d8abba..f7aa52a0bf 100644 --- a/MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf +++ b/MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf @@ -41,25 +41,12 @@ [LibraryClasses] - MemoryAllocationLib - UefiLib + IpIoLib BaseLib - UefiBootServicesTableLib - UefiRuntimeServicesTableLib - BaseMemoryLib DebugLib - PrintLib - + UefiBootServicesTableLib + MemoryAllocationLib [Protocols] gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiDriverDiagnosticsProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiUdp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiLoadedImageProtocolGuid # PROTOCOL ALWAYS_CONSUMED gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiDriverConfigurationProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiDriverBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiUdp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiComponentNameProtocolGuid # PROTOCOL ALWAYS_CONSUMED - diff --git a/MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf b/MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf index be9ff1b379..3ae0a04ee4 100644 --- a/MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf +++ b/MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf @@ -35,6 +35,7 @@ [Sources.common] DxeNetLib.c NetBuffer.c + NetDebug.c [Packages] MdePkg/MdePkg.dec @@ -42,25 +43,15 @@ [LibraryClasses] - MemoryAllocationLib - UefiLib - BaseLib - UefiBootServicesTableLib - UefiRuntimeServicesTableLib - BaseMemoryLib - DebugLib - PrintLib - + NetLib + BaseLib + DebugLib + BaseMemoryLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + UefiLib + MemoryAllocationLib [Protocols] - gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiDriverDiagnosticsProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiUdp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiLoadedImageProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiDriverConfigurationProtocolGuid # PROTOCOL ALWAYS_CONSUMED gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiDriverBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiUdp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiComponentNameProtocolGuid # PROTOCOL ALWAYS_CONSUMED diff --git a/MdeModulePkg/Library/DxeNetLib/NetDebug.c b/MdeModulePkg/Library/DxeNetLib/NetDebug.c new file mode 100644 index 0000000000..afcc1b3b42 --- /dev/null +++ b/MdeModulePkg/Library/DxeNetLib/NetDebug.c @@ -0,0 +1,536 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + NetDebug.c + +Abstract: + + Network debug facility. The debug information is wrapped in + SYSLOG packets, then sent over SNP. This debug facility can't + be used by SNP. Apply caution when used in MNP and non-network + module because SNP is most likely not "thread safe". We assume + that the SNP supports the EHTERNET. + + +**/ + + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + + +// +// Any error level digitally larger than mNetDebugLevelMax +// will be silently discarded. +// +UINTN mNetDebugLevelMax = NETDEBUG_LEVEL_ERROR; +UINT32 mSyslogPacketSeq = 0xDEADBEEF; + +// +// You can change mSyslogDstMac mSyslogDstIp and mSyslogSrcIp +// here to direct the syslog packets to the syslog deamon. The +// default is broadcast to both the ethernet and IP. +// +UINT8 mSyslogDstMac [NET_ETHER_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +UINT32 mSyslogDstIp = 0xffffffff; +UINT32 mSyslogSrcIp = 0; + +UINT8 * +MonthName[] = { + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" +}; + + +/** + Locate the handles that support SNP, then open one of them + to send the syslog packets. The caller isn't required to close + the SNP after use because the SNP is opened by HandleProtocol. + + None + + @return The point to SNP if one is properly openned. Otherwise NULL + +**/ +EFI_SIMPLE_NETWORK_PROTOCOL * +SyslogLocateSnp ( + VOID + ) +{ + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_STATUS Status; + EFI_HANDLE *Handles; + UINTN HandleCount; + UINTN Index; + + // + // Locate the handles which has SNP installed. + // + Handles = NULL; + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleNetworkProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + + if (EFI_ERROR (Status) || (HandleCount == 0)) { + return NULL; + } + + // + // Try to open one of the ethernet SNP protocol to send packet + // + Snp = NULL; + + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + Handles[Index], + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &Snp + ); + + if ((Status == EFI_SUCCESS) && (Snp != NULL) && + (Snp->Mode->IfType == NET_IFTYPE_ETHERNET) && + (Snp->Mode->MaxPacketSize >= NET_SYSLOG_PACKET_LEN)) { + + break; + } + + Snp = NULL; + } + + gBS->FreePool (Handles); + return Snp; +} + + +/** + Transmit a syslog packet synchronously through SNP. The Packet + already has the ethernet header prepended. This function should + fill in the source MAC because it will try to locate a SNP each + time it is called to avoid the problem if SNP is unloaded. + This code snip is copied from MNP. + + @param Packet The Syslog packet + @param Length The length of the packet + + @retval EFI_DEVICE_ERROR Failed to locate a usable SNP protocol + @retval EFI_TIMEOUT Timeout happened to send the packet. + @retval EFI_SUCCESS Packet is sent. + +**/ +EFI_STATUS +SyslogSendPacket ( + IN UINT8 *Packet, + IN UINT32 Length + ) +{ + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + ETHER_HEAD *Ether; + EFI_STATUS Status; + EFI_EVENT TimeoutEvent; + UINT8 *TxBuf; + + Snp = SyslogLocateSnp (); + + if (Snp == NULL) { + return EFI_DEVICE_ERROR; + } + + Ether = (ETHER_HEAD *) Packet; + CopyMem (Ether->SrcMac, Snp->Mode->CurrentAddress.Addr, NET_ETHER_ADDR_LEN); + + // + // Start the timeout event. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_NOTIFY, + NULL, + NULL, + &TimeoutEvent + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + for (;;) { + // + // Transmit the packet through SNP. + // + Status = Snp->Transmit (Snp, 0, Length, Packet, NULL, NULL, NULL); + + if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) { + Status = EFI_DEVICE_ERROR; + break; + } + + // + // If Status is EFI_SUCCESS, the packet is put in the transmit queue. + // if Status is EFI_NOT_READY, the transmit engine of the network + // interface is busy. Both need to sync SNP. + // + TxBuf = NULL; + + do { + // + // Get the recycled transmit buffer status. + // + Snp->GetStatus (Snp, NULL, &TxBuf); + + if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + Status = EFI_TIMEOUT; + break; + } + + } while (TxBuf == NULL); + + if ((Status == EFI_SUCCESS) || (Status == EFI_TIMEOUT)) { + break; + } + + // + // Status is EFI_NOT_READY. Restart the timer event and + // call Snp->Transmit again. + // + gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT); + } + + gBS->SetTimer (TimeoutEvent, TimerCancel, 0); + +ON_EXIT: + gBS->CloseEvent (TimeoutEvent); + return Status; +} + + +/** + Compute checksum for a bulk of data. This code is copied from the + Netbuffer library. + + @param Bulk Pointer to the data. + @param Len Length of the data, in bytes. + + @retval UINT16 The computed checksum. + +**/ +UINT16 +SyslogChecksum ( + IN UINT8 *Bulk, + IN UINT32 Len + ) +{ + register UINT32 Sum; + + Sum = 0; + + while (Len > 1) { + Sum += *(UINT16 *) Bulk; + Bulk += 2; + Len -= 2; + } + + // + // Add left-over byte, if any + // + if (Len > 0) { + Sum += *(UINT8 *) Bulk; + } + + // + // Fold 32-bit sum to 16 bits + // + while (Sum >> 16) { + Sum = (Sum & 0xffff) + (Sum >> 16); + } + + return (UINT16) ~Sum; +} + + +/** + Build a syslog packet, including the Ethernet/Ip/Udp headers + and user's message. + + @param Buf The buffer to put the packet data + @param BufLen The lenght of the Buf + @param Level Syslog servity level + @param Module The module that generates the log + @param File The file that contains the current log + @param Line The line of code in the File that contains the + current log + @param Message The log message + + @return The length of the syslog packet built. + +**/ +UINT32 +SyslogBuildPacket ( + UINT8 *Buf, + UINT32 BufLen, + UINT32 Level, + UINT8 *Module, + UINT8 *File, + UINT32 Line, + UINT8 *Message + ) +{ + ETHER_HEAD *Ether; + IP4_HEAD *Ip4; + EFI_UDP4_HEADER *Udp4; + EFI_TIME Time; + UINT32 Pri; + UINT32 Len; + + // + // Fill in the Ethernet header. Leave alone the source MAC. + // SyslogSendPacket will fill in the address for us. + // + Ether = (ETHER_HEAD *) Buf; + CopyMem (Ether->DstMac, mSyslogDstMac, NET_ETHER_ADDR_LEN); + ZeroMem (Ether->SrcMac, NET_ETHER_ADDR_LEN); + + Ether->EtherType = HTONS (0x0800); // IP protocol + + Buf += sizeof (ETHER_HEAD); + BufLen -= sizeof (ETHER_HEAD); + + // + // Fill in the IP header + // + Ip4 = (IP4_HEAD *) Buf; + Ip4->HeadLen = 5; + Ip4->Ver = 4; + Ip4->Tos = 0; + Ip4->TotalLen = 0; + Ip4->Id = (UINT16) mSyslogPacketSeq; + Ip4->Fragment = 0; + Ip4->Ttl = 16; + Ip4->Protocol = 0x11; + Ip4->Checksum = 0; + Ip4->Src = mSyslogSrcIp; + Ip4->Dst = mSyslogDstIp; + + Buf += sizeof (IP4_HEAD); + BufLen -= sizeof (IP4_HEAD); + + // + // Fill in the UDP header, Udp checksum is optional. Leave it zero. + // + Udp4 = (EFI_UDP4_HEADER*) Buf; + Udp4->SrcPort = HTONS (514); + Udp4->DstPort = HTONS (514); + Udp4->Length = 0; + Udp4->Checksum = 0; + + Buf += sizeof (EFI_UDP4_HEADER); + BufLen -= sizeof (EFI_UDP4_HEADER); + + // + // Build the syslog message body with Timestamp machine module Message + // + Pri = ((NET_SYSLOG_FACILITY & 31) << 3) | (Level & 7); + gRT->GetTime (&Time, NULL); + + // + // Use %a to format the ASCII strings, %s to format UNICODE strings + // + Len = 0; + Len += (UINT32) AsciiSPrint ( + Buf, + BufLen, + "<%d> %a %d %d:%d:%d ", + Pri, + MonthName [Time.Month-1], + Time.Day, + Time.Hour, + Time.Minute, + Time.Second + ); + Len--; + + Len += (UINT32) AsciiSPrint ( + Buf + Len, + BufLen - Len, + "Tiano %a: %a (Line: %d File: %a)", + Module, + Message, + Line, + File + ); + Len--; + + // + // OK, patch the IP length/checksum and UDP length fields. + // + Len += sizeof (EFI_UDP4_HEADER); + Udp4->Length = HTONS ((UINT16) Len); + + Len += sizeof (IP4_HEAD); + Ip4->TotalLen = HTONS ((UINT16) Len); + Ip4->Checksum = SyslogChecksum ((UINT8 *) Ip4, sizeof (IP4_HEAD)); + + return Len + sizeof (ETHER_HEAD); +} + + +/** + Allocate a buffer, then format the message to it. This is a + help function for the NET_DEBUG_XXX macros. The PrintArg of + these macros treats the variable length print parameters as a + single parameter, and pass it to the NetDebugASPrint. For + example, NET_DEBUG_TRACE ("Tcp", ("State transit to %a\n", Name)) + if extracted to: + NetDebugOutput ( + NETDEBUG_LEVEL_TRACE, + "Tcp", + __FILE__, + __LINE__, + NetDebugASPrint ("State transit to %a\n", Name) + ) + This is exactly what we want. + + @param Format The ASCII format string. + @param ... The variable length parameter whose format is + determined by the Format string. + + @return The buffer containing the formatted message, or NULL if failed to + @return allocate memory. + +**/ +UINT8 * +NetDebugASPrint ( + UINT8 *Format, + ... + ) +{ + VA_LIST Marker; + UINT8 *Buf; + + Buf = AllocatePool (NET_DEBUG_MSG_LEN); + + if (Buf == NULL) { + return NULL; + } + + VA_START (Marker, Format); + AsciiVSPrint (Buf, NET_DEBUG_MSG_LEN, Format, Marker); + VA_END (Marker); + + return Buf; +} + + +/** + Output a debug message to syslog. This function will locate a + instance of SNP then send the message through it. Because it + isn't open the SNP BY_DRIVER, apply caution when using it. + + @param Level The servity level of the message. + @param Module The Moudle that generates the log. + @param File The file that contains the log. + @param Line The exact line that contains the log. + @param Message The user message to log. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet + @retval EFI_SUCCESS The log is discard because that it is more verbose + than the mNetDebugLevelMax. Or, it has been sent + out. + +**/ +EFI_STATUS +NetDebugOutput ( + UINT32 Level, + UINT8 *Module, + UINT8 *File, + UINT32 Line, + UINT8 *Message + ) +{ + UINT8 *Packet; + UINT32 Len; + EFI_STATUS Status; + + // + // Check whether the message should be sent out + // + if (Message == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (Level > mNetDebugLevelMax) { + Status = EFI_SUCCESS; + goto ON_EXIT; + } + + // + // Allocate a maxium of 1024 bytes, the caller should ensure + // that the message plus the ethernet/ip/udp header is shorter + // than this + // + Packet = AllocatePool (NET_SYSLOG_PACKET_LEN); + + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Build the message: Ethernet header + IP header + Udp Header + user data + // + Len = SyslogBuildPacket ( + Packet, + NET_SYSLOG_PACKET_LEN, + Level, + Module, + File, + Line, + Message + ); + + mSyslogPacketSeq++; + Status = SyslogSendPacket (Packet, Len); + gBS->FreePool (Packet); + +ON_EXIT: + gBS->FreePool (Message); + return Status; +} diff --git a/MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf b/MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf index 421eaa011a..3b286980f6 100644 --- a/MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf +++ b/MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf @@ -41,25 +41,12 @@ [LibraryClasses] - MemoryAllocationLib - UefiLib + UdpIoLib BaseLib - UefiBootServicesTableLib - UefiRuntimeServicesTableLib - BaseMemoryLib DebugLib - PrintLib - + UefiBootServicesTableLib + MemoryAllocationLib [Protocols] - gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiDriverDiagnosticsProtocolGuid # PROTOCOL ALWAYS_CONSUMED gEfiUdp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiLoadedImageProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiDriverConfigurationProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiDriverBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiUdp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED - gEfiComponentNameProtocolGuid # PROTOCOL ALWAYS_CONSUMED - + gEfiUdp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED \ No newline at end of file diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index b461ecc56b..4947db5851 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -401,6 +401,10 @@ $(WORKSPACE)/MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf $(WORKSPACE)/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDxe.inf + $(WORKSPACE)/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf + $(WORKSPACE)/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf + $(WORKSPACE)/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf + $(WORKSPACE)/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf $(WORKSPACE)/MdeModulePkg/Application/HelloWorld/HelloWorld.inf diff --git a/MdeModulePkg/Universal/Network/MnpDxe/ComponentName.c b/MdeModulePkg/Universal/Network/MnpDxe/ComponentName.c new file mode 100644 index 0000000000..209861c775 --- /dev/null +++ b/MdeModulePkg/Universal/Network/MnpDxe/ComponentName.c @@ -0,0 +1,162 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + ComponentName.c + +Abstract: + + +**/ + + +#include "MnpDriver.h" + +// +// EFI Component Name Functions +// +EFI_STATUS +EFIAPI +MnpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +MnpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +EFI_COMPONENT_NAME_PROTOCOL gMnpComponentName = { + MnpComponentNameGetDriverName, + MnpComponentNameGetControllerName, + "eng" +}; + +STATIC EFI_UNICODE_STRING_TABLE mMnpDriverNameTable[] = { + { + "eng", + L"MNP Network Service Driver" + }, + { + NULL, + NULL + } +}; + +EFI_STATUS +EFIAPI +MnpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + Language - A pointer to a three character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + DriverName - A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + Returns: + EFI_SUCCES - The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - DriverName is NULL. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return LookupUnicodeString ( + Language, + gMnpComponentName.SupportedLanguages, + mMnpDriverNameTable, + DriverName + ); +} + +EFI_STATUS +EFIAPI +MnpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + ControllerHandle - The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + ChildHandle - The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + Language - A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + ControllerName - A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language from the point of view of the driver specified + by This. + + Returns: + EFI_SUCCESS - The Unicode string for the user readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - ControllerName is NULL. + EFI_UNSUPPORTED - The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return EFI_UNSUPPORTED; +} diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c b/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c new file mode 100644 index 0000000000..c842991f30 --- /dev/null +++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c @@ -0,0 +1,1466 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + MnpConfig.c + +Abstract: + + Implementation of Managed Network Protocol private services. + + +**/ + + +#include "MnpImpl.h" + +EFI_SERVICE_BINDING_PROTOCOL mMnpServiceBindingProtocol = { + MnpServiceBindingCreateChild, + MnpServiceBindingDestroyChild +}; + +EFI_MANAGED_NETWORK_PROTOCOL mMnpProtocolTemplate = { + MnpGetModeData, + MnpConfigure, + MnpMcastIpToMac, + MnpGroups, + MnpTransmit, + MnpReceive, + MnpCancel, + MnpPoll +}; + +EFI_MANAGED_NETWORK_CONFIG_DATA mMnpDefaultConfigData = { + 10000, + 10000, + 0, + FALSE, + FALSE, + FALSE, + FALSE, + FALSE, + FALSE, + FALSE +}; + +STATIC +EFI_STATUS +MnpAddFreeNbuf ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN UINTN Count + ); + +STATIC +EFI_STATUS +MnpStartSnp ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp + ); + +STATIC +EFI_STATUS +MnpStopSnp ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp + ); + +STATIC +EFI_STATUS +MnpStart ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN BOOLEAN IsConfigUpdate, + IN BOOLEAN EnableSystemPoll + ); + +STATIC +EFI_STATUS +MnpStop ( + IN MNP_SERVICE_DATA *MnpServiceData + ); + +STATIC +EFI_STATUS +MnpConfigReceiveFilters ( + IN MNP_SERVICE_DATA *MnpServiceData + ); + +STATIC +EFI_STATUS +MnpGroupOpAddCtrlBlk ( + IN MNP_INSTANCE_DATA *Instance, + IN MNP_GROUP_CONTROL_BLOCK *CtrlBlk, + IN MNP_GROUP_ADDRESS *GroupAddress OPTIONAL, + IN EFI_MAC_ADDRESS *MacAddress, + IN UINT32 HwAddressSize + ); + +STATIC +BOOLEAN +MnpGroupOpDelCtrlBlk ( + IN MNP_INSTANCE_DATA *Instance, + IN MNP_GROUP_CONTROL_BLOCK *CtrlBlk + ); + + +/** + Add some NET_BUF into MnpServiceData->FreeNbufQue. The buffer length of + the NET_BUF is specified by MnpServiceData->BufferLength. + + @param MnpServiceData Pointer to the MNP_SERVICE_DATA. + @param Count Number of NET_BUFFERs to add. + + @retval EFI_SUCCESS The specified amount of NET_BUFs are allocated and + added into MnpServiceData->FreeNbufQue. + @retval EFI_OUT_OF_RESOURCES Failed to allocate a NET_BUF structure. + +**/ +STATIC +EFI_STATUS +MnpAddFreeNbuf ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN UINTN Count + ) +{ + EFI_STATUS Status; + UINTN Index; + NET_BUF *Nbuf; + + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + ASSERT ((Count > 0) && (MnpServiceData->BufferLength > 0)); + + Status = EFI_SUCCESS; + + for (Index = 0; Index < Count; Index++) { + + Nbuf = NetbufAlloc (MnpServiceData->BufferLength); + if (Nbuf == NULL) { + + MNP_DEBUG_ERROR (("MnpAddFreeNbuf: NetBufAlloc failed.\n")); + Status = EFI_OUT_OF_RESOURCES; + break; + } + + NetbufQueAppend (&MnpServiceData->FreeNbufQue, Nbuf); + } + + MnpServiceData->NbufCnt += Index; + + return Status; +} + + +/** + Allocate a free NET_BUF from MnpServiceData->FreeNbufQue. If there is none + in the queue, first try to allocate some and add them into the queue, then + fetch the NET_BUF from the updated FreeNbufQue. + + @param MnpServiceData Pointer to the MNP_SERVICE_DATA. + + @return Pointer to the allocated free NET_BUF structure, if NULL the operation is failed. + +**/ +NET_BUF * +MnpAllocNbuf ( + IN MNP_SERVICE_DATA *MnpServiceData + ) +{ + EFI_STATUS Status; + NET_BUF_QUEUE *FreeNbufQue; + NET_BUF *Nbuf; + EFI_TPL OldTpl; + + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + + FreeNbufQue = &MnpServiceData->FreeNbufQue; + + OldTpl = NET_RAISE_TPL (NET_TPL_RECYCLE); + + // + // Check whether there are available buffers, or else try to add some. + // + if (FreeNbufQue->BufNum == 0) { + + if ((MnpServiceData->NbufCnt + MNP_NET_BUFFER_INCREASEMENT) > MNP_MAX_NET_BUFFER_NUM) { + + MNP_DEBUG_ERROR ( + ("MnpAllocNbuf: The maximum NET_BUF size is reached for MNP driver instance %p.\n", + MnpServiceData) + ); + + Nbuf = NULL; + goto ON_EXIT; + } + + Status = MnpAddFreeNbuf (MnpServiceData, MNP_NET_BUFFER_INCREASEMENT); + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR ( + ("MnpAllocNbuf: Failed to add NET_BUFs into the FreeNbufQue, %r.\n", + Status) + ); + // + // Don't return NULL, perhaps MnpAddFreeNbuf does add some NET_BUFs but + // the amount is less than MNP_NET_BUFFER_INCREASEMENT. + // + } + } + + Nbuf = NetbufQueRemove (FreeNbufQue); + + // + // Increase the RefCnt. + // + if (Nbuf != NULL) { + NET_GET_REF (Nbuf); + } + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + + return Nbuf; +} + + +/** + Try to reclaim the Nbuf into the buffer pool. + + @param MnpServiceData Pointer to the mnp service context data. + @param Nbuf Pointer to the NET_BUF to free. + + @return None. + +**/ +VOID +MnpFreeNbuf ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN NET_BUF *Nbuf + ) +{ + EFI_TPL OldTpl; + + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + ASSERT (Nbuf->RefCnt > 1); + + OldTpl = NET_RAISE_TPL (NET_TPL_RECYCLE); + + NET_PUT_REF (Nbuf); + + if (Nbuf->RefCnt == 1) { + // + // Trim all buffer contained in the Nbuf, then append it to the NbufQue. + // + NetbufTrim (Nbuf, Nbuf->TotalSize, NET_BUF_TAIL); + NetbufQueAppend (&MnpServiceData->FreeNbufQue, Nbuf); + } + + NET_RESTORE_TPL (OldTpl); +} + + +/** + Initialize the mnp service context data. + + @param MnpServiceData Pointer to the mnp service context data. + @param Snp Pointer to the simple network protocol. + + @retval EFI_SUCCESS The mnp service context is initialized. + @retval Other Some error occurs. + +**/ +EFI_STATUS +MnpInitializeServiceData ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN EFI_HANDLE ImageHandle, + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + + MnpServiceData->Signature = MNP_SERVICE_DATA_SIGNATURE; + + MnpServiceData->ControllerHandle = ControllerHandle; + + // + // Copy the ServiceBinding structure. + // + MnpServiceData->ServiceBinding = mMnpServiceBindingProtocol; + + // + // Open the Simple Network protocol. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &Snp, + ImageHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + // + // Get MTU from Snp. + // + SnpMode = Snp->Mode; + MnpServiceData->Snp = Snp; + MnpServiceData->Mtu = SnpMode->MaxPacketSize; + + // + // Initialize the lists. + // + NetListInit (&MnpServiceData->GroupAddressList); + NetListInit (&MnpServiceData->ChildrenList); + + // + // Get the buffer length used to allocate NET_BUF to hold data received + // from SNP. Do this before fill the FreeNetBufQue. + // + MnpServiceData->BufferLength = MnpServiceData->Mtu + SnpMode->MediaHeaderSize + NET_ETHER_FCS_SIZE; + + // + // Initialize the FreeNetBufQue and pre-allocate some NET_BUFs. + // + NetbufQueInit (&MnpServiceData->FreeNbufQue); + Status = MnpAddFreeNbuf (MnpServiceData, MNP_INIT_NET_BUFFER_NUM); + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR (("MnpInitializeServiceData: MnpAddFreeNbuf failed, %r.\n", Status)); + goto ERROR; + } + // + // Get one NET_BUF from the FreeNbufQue for rx cache. + // + MnpServiceData->RxNbufCache = MnpAllocNbuf (MnpServiceData); + NetbufAllocSpace ( + MnpServiceData->RxNbufCache, + MnpServiceData->BufferLength, + NET_BUF_TAIL + ); + + // + // Allocate buffer pool for tx. + // + MnpServiceData->TxBuf = NetAllocatePool (MnpServiceData->Mtu + SnpMode->MediaHeaderSize); + if (MnpServiceData->TxBuf == NULL) { + + MNP_DEBUG_ERROR (("MnpInitializeServiceData: NetAllocatePool failed.\n")); + Status = EFI_OUT_OF_RESOURCES; + + goto ERROR; + } + + // + // Create the system poll timer. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + NET_TPL_LOCK, + MnpSystemPoll, + MnpServiceData, + &MnpServiceData->PollTimer + ); + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR (("MnpInitializeServiceData: CreateEvent for poll timer failed.\n")); + goto ERROR; + } + + // + // Create the timer for packet timeout check. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + NET_TPL_EVENT, + MnpCheckPacketTimeout, + MnpServiceData, + &MnpServiceData->TimeoutCheckTimer + ); + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR (("MnpInitializeServiceData: CreateEvent for packet timeout check failed.\n")); + goto ERROR; + } + + // + // Create the timer for tx timeout check. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + NET_TPL_SLOW_TIMER, + NULL, + NULL, + &MnpServiceData->TxTimeoutEvent + ); + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR (("MnpInitializeServiceData: CreateEvent for tx timeout event failed.\n")); + } + +ERROR: + + if (EFI_ERROR (Status)) { + // + // Free the dynamic allocated resources if necessary. + // + if (MnpServiceData->TimeoutCheckTimer != NULL) { + + gBS->CloseEvent (MnpServiceData->TimeoutCheckTimer); + } + + if (MnpServiceData->PollTimer != NULL) { + + gBS->CloseEvent (MnpServiceData->PollTimer); + } + + if (MnpServiceData->TxBuf != NULL) { + + NetFreePool (MnpServiceData->TxBuf); + } + + if (MnpServiceData->RxNbufCache != NULL) { + + MnpFreeNbuf (MnpServiceData, MnpServiceData->RxNbufCache); + } + + if (MnpServiceData->FreeNbufQue.BufNum != 0) { + + NetbufQueFlush (&MnpServiceData->FreeNbufQue); + } + } + + return Status; +} + + +/** + Flush the mnp service context data. + + @param MnpServiceData Pointer to the mnp service context data. + + @return None. + +**/ +VOID +MnpFlushServiceData ( + MNP_SERVICE_DATA *MnpServiceData + ) +{ + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + + // + // The GroupAddressList must be empty. + // + ASSERT (NetListIsEmpty (&MnpServiceData->GroupAddressList)); + + // + // Close the event. + // + gBS->CloseEvent (&MnpServiceData->TxTimeoutEvent); + gBS->CloseEvent (&MnpServiceData->TimeoutCheckTimer); + gBS->CloseEvent (&MnpServiceData->PollTimer); + + // + // Free the tx buffer. + // + NetFreePool (MnpServiceData->TxBuf); + + // + // Free the RxNbufCache. + // + MnpFreeNbuf (MnpServiceData, MnpServiceData->RxNbufCache); + + // + // Flush the FreeNbufQue. + // + MnpServiceData->NbufCnt -= MnpServiceData->FreeNbufQue.BufNum; + NetbufQueFlush (&MnpServiceData->FreeNbufQue); + + DEBUG_CODE ( + + if (MnpServiceData->NbufCnt != 0) { + + MNP_DEBUG_WARN (("MnpFlushServiceData: Memory leak, MnpServiceData->NbufCnt != 0.\n")); + } + ); +} + + +/** + Initialize the mnp instance context data. + + @param MnpServiceData Pointer to the mnp service context data. + @param Instance Pointer to the mnp instance context data to + initialize. + + @return None. + +**/ +VOID +MnpInitializeInstanceData ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN MNP_INSTANCE_DATA *Instance + ) +{ + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + ASSERT (Instance != NULL); + + // + // Set the signature. + // + Instance->Signature = MNP_INSTANCE_DATA_SIGNATURE; + + // + // Copy the MNP Protocol interfaces from the template. + // + Instance->ManagedNetwork = mMnpProtocolTemplate; + + // + // Copy the default config data. + // + Instance->ConfigData = mMnpDefaultConfigData; + + // + // Initialize the lists. + // + NetListInit (&Instance->GroupCtrlBlkList); + NetListInit (&Instance->RcvdPacketQueue); + NetListInit (&Instance->RxDeliveredPacketQueue); + + // + // Initialize the RxToken Map. + // + NetMapInit (&Instance->RxTokenMap); + + // + // Save the MnpServiceData info. + // + Instance->MnpServiceData = MnpServiceData; +} + + +/** + Check whether the token specified by Arg maches the token in Item. + + @param Map Pointer to the NET_MAP. + @param Item Pointer to the NET_MAP_ITEM + @param Arg Pointer to the Arg, it's a pointer to the token to + check. + + @retval EFI_SUCCESS The token specified by Arg is different from the + token in Item. + @retval EFI_ACCESS_DENIED The token specified by Arg is the same as that in + Item. + +**/ +EFI_STATUS +MnpTokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg + ) +{ + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token; + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *TokenInItem; + + Token = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *) Arg; + TokenInItem = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *) Item->Key; + + if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) { + // + // The token is the same either the two tokens equals or the Events in + // the two tokens are the same. + // + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} + + +/** + Cancel the token specified by Arg if it matches the token in Item. + + @param Map Pointer to the NET_MAP. + @param Item Pointer to the NET_MAP_ITEM + @param Arg Pointer to the Arg, it's a pointer to the token to + cancel. + + @retval EFI_SUCCESS The Arg is NULL, and the token in Item is + cancelled, or the Arg isn't NULL, and the token in + Item is different from the Arg. + @retval EFI_ABORTED The Arg isn't NULL, the token in Item mathces the + Arg, and the token is cancelled. + +**/ +EFI_STATUS +MnpCancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg + ) +{ + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *TokenToCancel; + + if ((Arg != NULL) && (Item->Key != Arg)) { + // + // The token in Item is not the token specified by Arg. + // + return EFI_SUCCESS; + } + + TokenToCancel = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *) Item->Key; + + // + // Cancel this token with status set to EFI_ABORTED. + // + TokenToCancel->Status = EFI_ABORTED; + gBS->SignalEvent (TokenToCancel->Event); + + // + // Remove the item from the map. + // + NetMapRemoveItem (Map, Item, NULL); + + if (Arg != NULL) { + // + // Only abort the token specified by Arg if Arg isn't NULL. + // + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + Start and initialize the simple network. + + @param Snp Pointer to the simple network protocol. + + @retval EFI_SUCCESS The simple network protocol is started. + @retval Other Some error occurs. + +**/ +STATIC +EFI_STATUS +MnpStartSnp ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp + ) +{ + EFI_STATUS Status; + + ASSERT (Snp != NULL); + + // + // Start the simple network. + // + Status = Snp->Start (Snp); + + if (!EFI_ERROR (Status)) { + // + // Initialize the simple network. + // + Status = Snp->Initialize (Snp, 0, 0); + } + + return Status; +} + + +/** + Stop the simple network. + + @param Snp Pointer to the simple network protocol. + + @retval EFI_SUCCESS The simple network is stopped. + @retval Other Some error occurs. + +**/ +STATIC +EFI_STATUS +MnpStopSnp ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp + ) +{ + EFI_STATUS Status; + + ASSERT (Snp != NULL); + + // + // Shut down the simple network. + // + Status = Snp->Shutdown (Snp); + + if (!EFI_ERROR (Status)) { + // + // Stop the simple network. + // + Status = Snp->Stop (Snp); + } + + return Status; +} + + +/** + Start the managed network, this function is called when one instance is configured + or reconfigured. + + @param MnpServiceData Pointer to the mnp service context data. + @param IsConfigUpdate The instance is reconfigured or it's the first time + the instanced is configured. + @param EnableSystemPoll Enable the system polling or not. + + @retval EFI_SUCCESS The managed network is started and some + configuration is updated. + @retval Other Some error occurs. + +**/ +STATIC +EFI_STATUS +MnpStart ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN BOOLEAN IsConfigUpdate, + IN BOOLEAN EnableSystemPoll + ) +{ + EFI_STATUS Status; + EFI_TIMER_DELAY TimerOpType; + + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + + Status = EFI_SUCCESS; + + if (!IsConfigUpdate) { + // + // If it's not a configuration update, increase the configured children number. + // + MnpServiceData->ConfiguredChildrenNumber++; + + if (MnpServiceData->ConfiguredChildrenNumber == 1) { + // + // It's the first configured child, start the simple network. + // + Status = MnpStartSnp (MnpServiceData->Snp); + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR (("MnpStart: MnpStartSnp failed, %r.\n", Status)); + goto ErrorExit; + } + + // + // Start the timeout timer. + // + Status = gBS->SetTimer ( + MnpServiceData->TimeoutCheckTimer, + TimerPeriodic, + MNP_TIMEOUT_CHECK_INTERVAL + ); + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR ( + ("MnpStart, gBS->SetTimer for TimeoutCheckTimer %r.\n", + Status) + ); + goto ErrorExit; + } + } + } + + if (MnpServiceData->EnableSystemPoll ^ EnableSystemPoll) { + // + // The EnableSystemPoll differs with the current state, disable or enable + // the system poll. + // + TimerOpType = EnableSystemPoll ? TimerPeriodic : TimerCancel; + + Status = gBS->SetTimer (MnpServiceData->PollTimer, TimerOpType, MNP_SYS_POLL_INTERVAL); + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR (("MnpStart: gBS->SetTimer for PollTimer failed, %r.\n", Status)); + goto ErrorExit; + } + + MnpServiceData->EnableSystemPoll = EnableSystemPoll; + } + + // + // Change the receive filters if need. + // + Status = MnpConfigReceiveFilters (MnpServiceData); + +ErrorExit: + + return Status; +} + + +/** + Stop the managed network. + + @param MnpServiceData Pointer to the mnp service context data. + + @retval EFI_SUCCESS The managed network is stopped. + @retval Other Some error occurs. + +**/ +STATIC +EFI_STATUS +MnpStop ( + IN MNP_SERVICE_DATA *MnpServiceData + ) +{ + EFI_STATUS Status; + + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + ASSERT (MnpServiceData->ConfiguredChildrenNumber > 0); + + // + // Configure the receive filters. + // + MnpConfigReceiveFilters (MnpServiceData); + + // + // Decrease the children number. + // + MnpServiceData->ConfiguredChildrenNumber--; + + if (MnpServiceData->ConfiguredChildrenNumber > 0) { + // + // If there are other configured chilren, return and keep the timers and + // simple network unchanged. + // + return EFI_SUCCESS; + } + + // + // No configured children now. + // + + if (MnpServiceData->EnableSystemPoll) { + // + // The system poll in on, cancel the poll timer. + // + Status = gBS->SetTimer (MnpServiceData->PollTimer, TimerCancel, 0); + MnpServiceData->EnableSystemPoll = FALSE; + } + + // + // Cancel the timeout timer. + // + Status = gBS->SetTimer (MnpServiceData->TimeoutCheckTimer, TimerCancel, 0); + + // + // Stop the simple network. + // + Status = MnpStopSnp (MnpServiceData->Snp); + + return Status; +} + + +/** + Flush the instance's received data. + + @param Instance Pointer to the mnp instance context data. + + @return None. + +**/ +VOID +MnpFlushRcvdDataQueue ( + IN MNP_INSTANCE_DATA *Instance + ) +{ + EFI_TPL OldTpl; + MNP_RXDATA_WRAP *RxDataWrap; + + NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE); + + OldTpl = NET_RAISE_TPL (NET_TPL_RECYCLE); + + while (!NetListIsEmpty (&Instance->RcvdPacketQueue)) { + // + // Remove all the Wraps. + // + RxDataWrap = NET_LIST_HEAD (&Instance->RcvdPacketQueue, MNP_RXDATA_WRAP, WrapEntry); + + // + // Recycle the RxDataWrap. + // + MnpRecycleRxData (NULL, (VOID *) RxDataWrap); + Instance->RcvdPacketQueueSize--; + } + + ASSERT (Instance->RcvdPacketQueueSize == 0); + + NET_RESTORE_TPL (OldTpl); +} + + +/** + Configure the Instance using ConfigData. + + @param Instance Pointer to the mnp instance context data. + @param ConfigData Pointer to the configuration data used to configure + the isntance. + + @retval EFI_SUCCESS The Instance is configured. + @retval EFI_UNSUPPORTED EnableReceiveTimestamps is on and the + implementation doesn't support it. + @retval Other Some error occurs. + +**/ +EFI_STATUS +MnpConfigureInstance ( + IN MNP_INSTANCE_DATA *Instance, + IN EFI_MANAGED_NETWORK_CONFIG_DATA *ConfigData OPTIONAL + ) +{ + EFI_STATUS Status; + MNP_SERVICE_DATA *MnpServiceData; + EFI_MANAGED_NETWORK_CONFIG_DATA *OldConfigData; + EFI_MANAGED_NETWORK_CONFIG_DATA *NewConfigData; + BOOLEAN IsConfigUpdate; + + NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE); + + if ((ConfigData != NULL) && ConfigData->EnableReceiveTimestamps) { + // + // Don't support timestamp. + // + return EFI_UNSUPPORTED; + } + + Status = EFI_SUCCESS; + + MnpServiceData = Instance->MnpServiceData; + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + + IsConfigUpdate = (BOOLEAN) ((Instance->Configured) && (ConfigData != NULL)); + + OldConfigData = &Instance->ConfigData; + NewConfigData = ConfigData; + if (NewConfigData == NULL) { + // + // Restore back the default config data if a reset of this instance + // is required. + // + NewConfigData = &mMnpDefaultConfigData; + } + + // + // Reset the instance's receive filter. + // + Instance->ReceiveFilter = 0; + + // + // Clear the receive counters according to the old ConfigData. + // + if (OldConfigData->EnableUnicastReceive) { + MnpServiceData->UnicastCount--; + } + + if (OldConfigData->EnableMulticastReceive) { + MnpServiceData->MulticastCount--; + } + + if (OldConfigData->EnableBroadcastReceive) { + MnpServiceData->BroadcastCount--; + } + + if (OldConfigData->EnablePromiscuousReceive) { + MnpServiceData->PromiscuousCount--; + } + + // + // Set the receive filter counters and the receive filter of the + // instance according to the new ConfigData. + // + if (NewConfigData->EnableUnicastReceive) { + MnpServiceData->UnicastCount++; + Instance->ReceiveFilter |= MNP_RECEIVE_UNICAST; + } + + if (NewConfigData->EnableMulticastReceive) { + MnpServiceData->MulticastCount++; + } + + if (NewConfigData->EnableBroadcastReceive) { + MnpServiceData->BroadcastCount++; + Instance->ReceiveFilter |= MNP_RECEIVE_BROADCAST; + } + + if (NewConfigData->EnablePromiscuousReceive) { + MnpServiceData->PromiscuousCount++; + } + + if (OldConfigData->FlushQueuesOnReset) { + + MnpFlushRcvdDataQueue (Instance); + } + + if (ConfigData == NULL) { + + NetMapIterate (&Instance->RxTokenMap, MnpCancelTokens, NULL); + } + + if (!NewConfigData->EnableMulticastReceive) { + + MnpGroupOp (Instance, FALSE, NULL, NULL); + } + + // + // Save the new configuration data. + // + *OldConfigData = *NewConfigData; + + Instance->Configured = (BOOLEAN) (ConfigData != NULL); + + if (Instance->Configured) { + // + // The instance is configured, start the Mnp. + // + Status = MnpStart ( + MnpServiceData, + IsConfigUpdate, + !NewConfigData->DisableBackgroundPolling + ); + } else { + // + // The instance is changed to the unconfigured state, stop the Mnp. + // + Status = MnpStop (MnpServiceData); + } + + return Status; +} + + +/** + Configure the Snp receive filters according to the instances' receive filter + settings. + + @param MnpServiceData Pointer to the mnp service context data. + + @retval EFI_SUCCESS The receive filters is configured. + @retval EFI_OUT_OF_RESOURCES The receive filters can't be configured due to lack + of memory resource. + +**/ +STATIC +EFI_STATUS +MnpConfigReceiveFilters ( + IN MNP_SERVICE_DATA *MnpServiceData + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_MAC_ADDRESS *MCastFilter; + UINT32 MCastFilterCnt; + UINT32 EnableFilterBits; + UINT32 DisableFilterBits; + BOOLEAN ResetMCastFilters; + NET_LIST_ENTRY *Entry; + UINT32 Index; + MNP_GROUP_ADDRESS *GroupAddress; + + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + + Snp = MnpServiceData->Snp; + + // + // Initialize the enable filter and disable filter. + // + EnableFilterBits = 0; + DisableFilterBits = Snp->Mode->ReceiveFilterMask; + + if (MnpServiceData->UnicastCount != 0) { + // + // Enable unicast if any instance wants to receive unicast. + // + EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_UNICAST; + } + + if (MnpServiceData->BroadcastCount != 0) { + // + // Enable broadcast if any instance wants to receive broadcast. + // + EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST; + } + + MCastFilter = NULL; + MCastFilterCnt = 0; + ResetMCastFilters = TRUE; + + if ((MnpServiceData->MulticastCount != 0) && (MnpServiceData->GroupAddressCount != 0)) { + // + // There are instances configured to receive multicast and already some group + // addresses are joined. + // + + ResetMCastFilters = FALSE; + + if (MnpServiceData->GroupAddressCount <= Snp->Mode->MaxMCastFilterCount) { + // + // The joind group address is less than simple network's maximum count. + // Just configure the snp to do the multicast filtering. + // + + EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST; + + // + // Allocate pool for the mulicast addresses. + // + MCastFilterCnt = MnpServiceData->GroupAddressCount; + MCastFilter = NetAllocatePool (sizeof (EFI_MAC_ADDRESS) * MCastFilterCnt); + if (MCastFilter == NULL) { + + MNP_DEBUG_ERROR (("MnpConfigReceiveFilters: Failed to allocate memory resource for MCastFilter.\n")); + return EFI_OUT_OF_RESOURCES; + } + + // + // Fill the multicast HW address buffer. + // + Index = 0; + NET_LIST_FOR_EACH (Entry, &MnpServiceData->GroupAddressList) { + + GroupAddress = NET_LIST_USER_STRUCT (Entry, MNP_GROUP_ADDRESS, AddrEntry); + *(MCastFilter + Index) = GroupAddress->Address; + Index++; + + ASSERT (Index <= MCastFilterCnt); + } + } else { + // + // The maximum multicast is reached, set the filter to be promiscuous + // multicast. + // + + if (Snp->Mode->ReceiveFilterMask & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) { + EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + } else { + // + // Either MULTICAST or PROMISCUOUS_MULTICAST is not supported by Snp, + // set the NIC to be promiscuous although this will tremendously degrade + // the performance. + // + EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS; + } + } + } + + if (MnpServiceData->PromiscuousCount != 0) { + // + // Enable promiscuous if any instance wants to receive promiscuous. + // + EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS; + } + + // + // Set the disable filter. + // + DisableFilterBits ^= EnableFilterBits; + + // + // Configure the receive filters of SNP. + // + Status = Snp->ReceiveFilters ( + Snp, + EnableFilterBits, + DisableFilterBits, + ResetMCastFilters, + MCastFilterCnt, + MCastFilter + ); + DEBUG_CODE ( + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR ( + ("MnpConfigReceiveFilters: Snp->ReceiveFilters failed, %r.\n", + Status) + ); + } + ); + + if (MCastFilter != NULL) { + // + // Free the buffer used to hold the group addresses. + // + NetFreePool (MCastFilter); + } + + return Status; +} + + +/** + Add a group address control block which controls the MacAddress for + this instance. + + @param Instance Pointer to the mnp instance context data. + @param CtrlBlk Pointer to the group address control block. + @param GroupAddress Pointer to the group adress. + @param MacAddress Pointer to the mac address. + @param HwAddressSize The hardware address size. + + @retval EFI_SUCCESS The group address control block is added. + @retval EFI_OUT_OF_RESOURCE Failed due to lack of memory resources. + +**/ +STATIC +EFI_STATUS +MnpGroupOpAddCtrlBlk ( + IN MNP_INSTANCE_DATA *Instance, + IN MNP_GROUP_CONTROL_BLOCK *CtrlBlk, + IN MNP_GROUP_ADDRESS *GroupAddress OPTIONAL, + IN EFI_MAC_ADDRESS *MacAddress, + IN UINT32 HwAddressSize + ) +{ + MNP_SERVICE_DATA *MnpServiceData; + + NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE); + + MnpServiceData = Instance->MnpServiceData; + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + + if (GroupAddress == NULL) { + + ASSERT (MacAddress != NULL); + + // + // Allocate a new GroupAddress to be added into MNP's GroupAddressList. + // + GroupAddress = NetAllocatePool (sizeof (MNP_GROUP_ADDRESS)); + if (GroupAddress == NULL) { + + MNP_DEBUG_ERROR (("MnpGroupOpFormCtrlBlk: Failed to allocate memory resource.\n")); + + return EFI_OUT_OF_RESOURCES; + } + + GroupAddress->Address = *MacAddress; + GroupAddress->RefCnt = 0; + NetListInsertTail ( + &MnpServiceData->GroupAddressList, + &GroupAddress->AddrEntry + ); + MnpServiceData->GroupAddressCount++; + } + + // + // Increase the RefCnt. + // + GroupAddress->RefCnt++; + + // + // Add the CtrlBlk into the instance's GroupCtrlBlkList. + // + CtrlBlk->GroupAddress = GroupAddress; + NetListInsertTail (&Instance->GroupCtrlBlkList, &CtrlBlk->CtrlBlkEntry); + + return EFI_SUCCESS; +} + + +/** + Delete a group control block from the instance. If the controlled group address's + reference count reaches zero, the group address is removed too. + + @param Instance Pointer to the instance context data. + @param CtrlBlk Pointer to the group control block to delete. + + @return The group address controlled by the control block is no longer used or not. + +**/ +STATIC +BOOLEAN +MnpGroupOpDelCtrlBlk ( + IN MNP_INSTANCE_DATA *Instance, + IN MNP_GROUP_CONTROL_BLOCK *CtrlBlk + ) +{ + MNP_SERVICE_DATA *MnpServiceData; + MNP_GROUP_ADDRESS *GroupAddress; + + NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE); + + MnpServiceData = Instance->MnpServiceData; + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + + // + // Remove and free the CtrlBlk. + // + GroupAddress = CtrlBlk->GroupAddress; + NetListRemoveEntry (&CtrlBlk->CtrlBlkEntry); + NetFreePool (CtrlBlk); + + ASSERT (GroupAddress->RefCnt > 0); + + // + // Count down the RefCnt. + // + GroupAddress->RefCnt--; + + if (GroupAddress->RefCnt == 0) { + // + // Free this GroupAddress entry if no instance uses it. + // + MnpServiceData->GroupAddressCount--; + NetListRemoveEntry (&GroupAddress->AddrEntry); + NetFreePool (GroupAddress); + + return TRUE; + } + + return FALSE; +} + + +/** + Do the group operations for this instance. + + @param Instance Pointer to the instance context data. + @param JoinFlag Set to TRUE to join a group. Set to TRUE to leave a + group/groups. + @param MacAddress Pointer to the group address to join or leave. + @param CtrlBlk Pointer to the group control block if JoinFlag if + FALSE. + + @retval EFI_SUCCESS The group operation finished. + @retval Other Some error occurs. + +**/ +EFI_STATUS +MnpGroupOp ( + IN MNP_INSTANCE_DATA *Instance, + IN BOOLEAN JoinFlag, + IN EFI_MAC_ADDRESS *MacAddress OPTIONAL, + IN MNP_GROUP_CONTROL_BLOCK *CtrlBlk OPTIONAL + ) +{ + MNP_SERVICE_DATA *MnpServiceData; + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *NextEntry; + MNP_GROUP_ADDRESS *GroupAddress; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + MNP_GROUP_CONTROL_BLOCK *NewCtrlBlk; + EFI_STATUS Status; + BOOLEAN AddressExist; + BOOLEAN NeedUpdate; + + NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE); + + MnpServiceData = Instance->MnpServiceData; + SnpMode = MnpServiceData->Snp->Mode; + + if (JoinFlag) { + // + // A new gropu address is to be added. + // + + GroupAddress = NULL; + AddressExist = FALSE; + + // + // Allocate memory for the control block. + // + NewCtrlBlk = NetAllocatePool (sizeof (MNP_GROUP_CONTROL_BLOCK)); + if (NewCtrlBlk == NULL) { + + MNP_DEBUG_ERROR (("MnpGroupOp: Failed to allocate memory resource.\n")); + return EFI_OUT_OF_RESOURCES; + } + + NET_LIST_FOR_EACH (Entry, &MnpServiceData->GroupAddressList) { + // + // Check whether the MacAddress is already joined by other instances. + // + GroupAddress = NET_LIST_USER_STRUCT (Entry, MNP_GROUP_ADDRESS, AddrEntry); + if (0 == NetCompareMem ( + MacAddress, + &GroupAddress->Address, + SnpMode->HwAddressSize + )) { + + AddressExist = TRUE; + break; + } + } + + if (!AddressExist) { + GroupAddress = NULL; + } + + // + // Add the GroupAddress for this instance. + // + Status = MnpGroupOpAddCtrlBlk ( + Instance, + NewCtrlBlk, + GroupAddress, + MacAddress, + SnpMode->HwAddressSize + ); + if (EFI_ERROR (Status)) { + + return Status; + } + + NeedUpdate = TRUE; + } else { + + if (MacAddress != NULL) { + + ASSERT (CtrlBlk != NULL); + + // + // Leave the specific multicast mac address. + // + NeedUpdate = MnpGroupOpDelCtrlBlk (Instance, CtrlBlk); + } else { + // + // Leave all multicast mac addresses. + // + NeedUpdate = FALSE; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->GroupCtrlBlkList) { + + NewCtrlBlk = NET_LIST_USER_STRUCT ( + Entry, + MNP_GROUP_CONTROL_BLOCK, + CtrlBlkEntry + ); + // + // Update is required if the group address left is no longer used + // by other instances. + // + NeedUpdate = MnpGroupOpDelCtrlBlk (Instance, NewCtrlBlk); + } + } + } + + Status = EFI_SUCCESS; + + if (NeedUpdate) { + // + // Reconfigure the receive filters if necessary. + // + Status = MnpConfigReceiveFilters (MnpServiceData); + } + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpDebug.h b/MdeModulePkg/Universal/Network/MnpDxe/MnpDebug.h new file mode 100644 index 0000000000..72eb59b9b4 --- /dev/null +++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpDebug.h @@ -0,0 +1,28 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + MnpDebug.h + +Abstract: + + +**/ + +#ifndef _MNP_DEBUG_H_ +#define _MNP_DEBUG_H_ + +#define MNP_DEBUG_TRACE(PrintArg) NET_DEBUG_TRACE ("Mnp", PrintArg) +#define MNP_DEBUG_WARN(PrintArg) NET_DEBUG_WARNING ("Mnp", PrintArg) +#define MNP_DEBUG_ERROR(PrintArg) NET_DEBUG_ERROR ("Mnp", PrintArg) + +#endif diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c new file mode 100644 index 0000000000..07be21dced --- /dev/null +++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c @@ -0,0 +1,560 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + MnpDriver.c + +Abstract: + + +**/ + +#include "MnpDriver.h" +#include "MnpDebug.h" +#include "MnpImpl.h" + + +EFI_DRIVER_BINDING_PROTOCOL gMnpDriverBinding = { + MnpDriverBindingSupported, + MnpDriverBindingStart, + MnpDriverBindingStop, + 0xa, + NULL, + NULL +}; + + +/** + Test to see if this driver supports ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCES This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +MnpDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Test to see if MNP is already installed. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + + return EFI_ALREADY_STARTED; + } + + // + // Test to see if SNP is installed. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSimpleNetworkProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + + +/** + Start this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on + ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +MnpDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + MNP_SERVICE_DATA *MnpServiceData; + BOOLEAN MnpInitialized; + + MnpInitialized = FALSE; + + MnpServiceData = NetAllocateZeroPool (sizeof (MNP_SERVICE_DATA)); + if (MnpServiceData == NULL) { + MNP_DEBUG_ERROR (("MnpDriverBindingStart(): Failed to allocate the " + L"Mnp Service Data.\n")); + + return EFI_OUT_OF_RESOURCES; + } + + // + // Initialize the Mnp Service Data. + // + Status = MnpInitializeServiceData (MnpServiceData, This->DriverBindingHandle, ControllerHandle); + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR (("MnpDriverBindingStart: MnpInitializeServiceData " + L"failed, %r.\n",Status)); + goto ErrorExit; + } + + MnpInitialized = TRUE; + + // + // Install the MNP Service Binding Protocol. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + &MnpServiceData->ServiceBinding, + NULL + ); + +ErrorExit: + + if (EFI_ERROR (Status)) { + + if (MnpInitialized) { + // + // Flush the Mnp Service Data. + // + MnpFlushServiceData (MnpServiceData); + } + + // + // Close the Simple Network Protocol. + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiSimpleNetworkProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + NetFreePool (MnpServiceData); + } + + return Status; +} + + +/** + Stop this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on. + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCES This driver is removed ControllerHandle. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +MnpDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + MNP_SERVICE_DATA *MnpServiceData; + MNP_INSTANCE_DATA *Instance; + + // + // Retrieve the MNP service binding protocol from the ControllerHandle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR ( + ("MnpDriverBindingStop: Locate MNP Service Binding Protocol failed, %r.\n", + Status) + ); + goto EXIT; + } + + MnpServiceData = MNP_SERVICE_DATA_FROM_THIS (ServiceBinding); + + while (!NetListIsEmpty (&MnpServiceData->ChildrenList)) { + // + // Don't use NetListRemoveHead here, the remove opreration will be done + // in ServiceBindingDestroyChild. + // + Instance = NET_LIST_HEAD ( + &MnpServiceData->ChildrenList, + MNP_INSTANCE_DATA, + InstEntry + ); + + ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle); + } + + // + // Uninstall the MNP Service Binding Protocol. + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + ControllerHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + ServiceBinding, + NULL + ); + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR (("MnpDriverBindingStop: Uninstall MNP Service Binding Protocol failed, %r.\n")); + goto EXIT; + } + + // + // Close the openned Snp protocol. + // + Status = gBS->CloseProtocol ( + ControllerHandle, + &gEfiSimpleNetworkProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR (("MnpDriverBindingStop: Close SNP Protocol failed, %r.\n", Status)); + goto EXIT; + } + + // + // Flush the Mnp service data. + // + MnpFlushServiceData (MnpServiceData); + + NetFreePool (MnpServiceData); + +EXIT: + + return Status; +} + + +/** + Creates a child handle with a set of I/O services. + + @param This Protocol instance pointer. + @param ChildHandle Pointer to the handle of the child to create. If + it is NULL, then a new handle is created. If it is + not NULL, then the I/O services are added to the + existing child handle. + + @retval EFI_SUCCES The child handle was created with the I/O + services. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +MnpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ) +{ + EFI_STATUS Status; + MNP_SERVICE_DATA *MnpServiceData; + MNP_INSTANCE_DATA *Instance; + VOID *Snp; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + + return EFI_INVALID_PARAMETER; + } + + MnpServiceData = MNP_SERVICE_DATA_FROM_THIS (This); + + // + // Allocate buffer for the new instance. + // + Instance = NetAllocateZeroPool (sizeof (MNP_INSTANCE_DATA)); + if (Instance == NULL) { + + MNP_DEBUG_ERROR (("MnpServiceBindingCreateChild: Faild to allocate memory for the new instance.\n")); + return EFI_OUT_OF_RESOURCES; + } + + // + // Init the instance data. + // + MnpInitializeInstanceData (MnpServiceData, Instance); + + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiManagedNetworkProtocolGuid, + &Instance->ManagedNetwork, + NULL + ); + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR ( + ("MnpServiceBindingCreateChild: Failed to install the MNP protocol, %r.\n", + Status) + ); + goto ErrorExit; + } + + // + // Save the instance's childhandle. + // + Instance->Handle = *ChildHandle; + + Status = gBS->OpenProtocol ( + MnpServiceData->ControllerHandle, + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &Snp, + gMnpDriverBinding.DriverBindingHandle, + Instance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ErrorExit; + } + + // + // Add the child instance into ChildrenList. + // + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + NetListInsertTail (&MnpServiceData->ChildrenList, &Instance->InstEntry); + MnpServiceData->ChildrenNumber++; + + NET_RESTORE_TPL (OldTpl); + +ErrorExit: + + if (EFI_ERROR (Status)) { + + if (Instance->Handle != NULL) { + + gBS->UninstallMultipleProtocolInterfaces ( + &gEfiManagedNetworkProtocolGuid, + &Instance->ManagedNetwork, + NULL + ); + } + + NetFreePool (Instance); + } + + return Status; +} + + +/** + Destroys a child handle with a set of I/O services. + + @param This Protocol instance pointer. + @param ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The I/O services were removed from the child + handle. + @retval EFI_UNSUPPORTED The child handle does not support the I/O services + that are being removed. + @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle. + @retval EFI_ACCESS_DENIED The child handle could not be destroyed because + its I/O services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +MnpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + MNP_SERVICE_DATA *MnpServiceData; + EFI_MANAGED_NETWORK_PROTOCOL *ManagedNetwork; + MNP_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + + return EFI_INVALID_PARAMETER; + } + + MnpServiceData = MNP_SERVICE_DATA_FROM_THIS (This); + + // + // Try to retrieve ManagedNetwork Protocol from ChildHandle. + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiManagedNetworkProtocolGuid, + (VOID **) &ManagedNetwork, + gMnpDriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + + return EFI_UNSUPPORTED; + } + + Instance = MNP_INSTANCE_DATA_FROM_THIS (ManagedNetwork); + + // + // MnpServiceBindingDestroyChild may be called twice: first called by + // MnpServiceBindingStop, second called by uninstalling the MNP protocol + // in this ChildHandle. Use destroyed to make sure the resource clean code + // will only excecute once. + // + if (Instance->Destroyed) { + + return EFI_SUCCESS; + } + + Instance->Destroyed = TRUE; + + // + // Close the Simple Network protocol. + // + gBS->CloseProtocol ( + MnpServiceData->ControllerHandle, + &gEfiSimpleNetworkProtocolGuid, + gMnpDriverBinding.DriverBindingHandle, + ChildHandle + ); + + // + // Uninstall the ManagedNetwork protocol. + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiManagedNetworkProtocolGuid, + &Instance->ManagedNetwork, + NULL + ); + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR ( + ("MnpServiceBindingDestroyChild: Failed to uninstall the ManagedNetwork protocol, %r.\n", + Status) + ); + + Instance->Destroyed = FALSE; + return Status; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + // + // Reset the configuration. + // + ManagedNetwork->Configure (ManagedNetwork, NULL); + + // + // Try to flush the RcvdPacketQueue. + // + MnpFlushRcvdDataQueue (Instance); + + // + // Clean the RxTokenMap. + // + NetMapClean (&Instance->RxTokenMap); + + // + // Remove this instance from the ChildrenList. + // + NetListRemoveEntry (&Instance->InstEntry); + MnpServiceData->ChildrenNumber--; + + NET_RESTORE_TPL (OldTpl); + + NetFreePool (Instance); + + return Status; +} + +//@MT: EFI_DRIVER_ENTRY_POINT (MnpDriverEntryPoint) + +EFI_STATUS +EFIAPI +MnpDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +/*++ + +Routine Description: + + The entry point for Mnp driver which installs the driver binding and component name + protocol on its ImageHandle. + +Arguments: + + ImageHandle - The image handle of the driver. + SystemTable - The system table. + +Returns: + + EFI_SUCCESS - If the driver binding and component name protocols are successfully + installed, otherwise if failed. + +--*/ +{ + return NetLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &gMnpDriverBinding, + ImageHandle, + &gMnpComponentName, + NULL, + NULL + ); +} diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h new file mode 100644 index 0000000000..48ad0960cd --- /dev/null +++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h @@ -0,0 +1,136 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + MnpDriver.h + +Abstract: + + +**/ + +#ifndef _MNP_DRIVER_H_ +#define _MNP_DRIVER_H_ +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MnpDebug.h" + +// +// Required Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gMnpDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gMnpComponentName; + +#define MNP_SERVICE_DATA_SIGNATURE EFI_SIGNATURE_32 ('M', 'n', 'p', 'S') + +typedef struct _MNP_SERVICE_DATA { + UINT32 Signature; + + EFI_HANDLE ControllerHandle; + + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + + UINT32 Mtu; + + NET_LIST_ENTRY ChildrenList; + UINTN ChildrenNumber; + UINTN ConfiguredChildrenNumber; + + NET_LIST_ENTRY GroupAddressList; + UINT32 GroupAddressCount; + + EFI_EVENT TxTimeoutEvent; + + NET_BUF_QUEUE FreeNbufQue; + INTN NbufCnt; + + EFI_EVENT PollTimer; + BOOLEAN EnableSystemPoll; + + EFI_EVENT TimeoutCheckTimer; + + UINT32 UnicastCount; + UINT32 BroadcastCount; + UINT32 MulticastCount; + UINT32 PromiscuousCount; + + // + // The size of the data buffer in the MNP_PACKET_BUFFER used to + // store a packet. + // + UINT32 BufferLength; + NET_BUF *RxNbufCache; + UINT8 *TxBuf; +} MNP_SERVICE_DATA; + +#define MNP_SERVICE_DATA_FROM_THIS(a) \ + CR ( \ + (a), \ + MNP_SERVICE_DATA, \ + ServiceBinding, \ + MNP_SERVICE_DATA_SIGNATURE \ + ) + +EFI_STATUS +EFIAPI +MnpDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +MnpDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +MnpDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +EFI_STATUS +EFIAPI +MnpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ); + +EFI_STATUS +EFIAPI +MnpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf b/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf new file mode 100644 index 0000000000..26512cd1da --- /dev/null +++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf @@ -0,0 +1,63 @@ +#/** @file +# Component name for module Mnp +# +# FIX ME! +# Copyright (c) 2006, Intel Corporation. All right reserved. +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = MnpDxe + FILE_GUID = 025BBFC7-E6A9-4b8b-82AD-6815A1AEAF4A + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = MnpDriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + MnpDebug.h + MnpMain.c + MnpIo.c + MnpDriver.h + ComponentName.c + MnpDriver.c + MnpConfig.c + MnpImpl.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + DebugLib + NetLib + + +[Protocols] + gEfiManagedNetworkServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiManagedNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED + diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.msa b/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.msa new file mode 100644 index 0000000000..3ef22ddc03 --- /dev/null +++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.msa @@ -0,0 +1,70 @@ + + + MnpDxe + DXE_DRIVER + 025BBFC7-E6A9-4b8b-82AD-6815A1AEAF4A + 1.0 + Component name for module Mnp + FIX ME! + Copyright (c) 2006, Intel Corporation. All right reserved. + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF EBC + false + MnpDxe + + + + DebugLib + + + UefiDriverEntryPoint + + + UefiBootServicesTableLib + + + UefiLib + + + + MnpImpl.h + MnpConfig.c + MnpDriver.c + ComponentName.c + MnpDriver.h + MnpIo.c + MnpMain.c + MnpDebug.h + + + + + + + + gEfiManagedNetworkProtocolGuid + + + gEfiSimpleNetworkProtocolGuid + + + gEfiManagedNetworkServiceBindingProtocolGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + MnpDriverEntryPoint + + + \ No newline at end of file diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpImpl.h b/MdeModulePkg/Universal/Network/MnpDxe/MnpImpl.h new file mode 100644 index 0000000000..cdb081d2b4 --- /dev/null +++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpImpl.h @@ -0,0 +1,274 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + MnpImpl.h + +Abstract: + + +**/ + +#ifndef _MNP_IMPL_H_ +#define _MNP_IMPL_H_ + +#include "MnpDriver.h" +#include "MnpDebug.h" + +#define NET_ETHER_FCS_SIZE 4 + +#define MNP_SYS_POLL_INTERVAL (2 * TICKS_PER_MS) // 2 milliseconds +#define MNP_TIMEOUT_CHECK_INTERVAL (50 * TICKS_PER_MS) // 50 milliseconds +#define MNP_TX_TIMEOUT_TIME (500 * TICKS_PER_MS) // 500 milliseconds +#define MNP_INIT_NET_BUFFER_NUM 512 +#define MNP_NET_BUFFER_INCREASEMENT 64 +#define MNP_MAX_NET_BUFFER_NUM 65536 + +#define MNP_MAX_RCVD_PACKET_QUE_SIZE 256 + +#define MNP_RECEIVE_UNICAST 0x01 +#define MNP_RECEIVE_BROADCAST 0x02 + +#define UNICAST_PACKET MNP_RECEIVE_UNICAST +#define BROADCAST_PACKET MNP_RECEIVE_BROADCAST + +#define MNP_INSTANCE_DATA_SIGNATURE EFI_SIGNATURE_32 ('M', 'n', 'p', 'I') + +#define MNP_INSTANCE_DATA_FROM_THIS(a) \ + CR ( \ + (a), \ + MNP_INSTANCE_DATA, \ + ManagedNetwork, \ + MNP_INSTANCE_DATA_SIGNATURE \ + ) + +typedef struct _MNP_INSTANCE_DATA { + UINT32 Signature; + + MNP_SERVICE_DATA *MnpServiceData; + + EFI_HANDLE Handle; + + NET_LIST_ENTRY InstEntry; + + EFI_MANAGED_NETWORK_PROTOCOL ManagedNetwork; + + BOOLEAN Configured; + BOOLEAN Destroyed; + + NET_LIST_ENTRY GroupCtrlBlkList; + + NET_MAP RxTokenMap; + + NET_LIST_ENTRY RxDeliveredPacketQueue; + NET_LIST_ENTRY RcvdPacketQueue; + UINTN RcvdPacketQueueSize; + + EFI_MANAGED_NETWORK_CONFIG_DATA ConfigData; + + UINT8 ReceiveFilter; +} MNP_INSTANCE_DATA; + +typedef struct _MNP_GROUP_ADDRESS { + NET_LIST_ENTRY AddrEntry; + EFI_MAC_ADDRESS Address; + INTN RefCnt; +} MNP_GROUP_ADDRESS; + +typedef struct _MNP_GROUP_CONTROL_BLOCK { + NET_LIST_ENTRY CtrlBlkEntry; + MNP_GROUP_ADDRESS *GroupAddress; +} MNP_GROUP_CONTROL_BLOCK; + +typedef struct _MNP_RXDATA_WRAP { + NET_LIST_ENTRY WrapEntry; + MNP_INSTANCE_DATA *Instance; + EFI_MANAGED_NETWORK_RECEIVE_DATA RxData; + NET_BUF *Nbuf; + UINT64 TimeoutTick; +} MNP_RXDATA_WRAP; + +EFI_STATUS +MnpInitializeServiceData ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN EFI_HANDLE ImageHandle, + IN EFI_HANDLE ControllerHandle + ); + +VOID +MnpFlushServiceData ( + MNP_SERVICE_DATA *MnpServiceData + ); + +VOID +MnpInitializeInstanceData ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN MNP_INSTANCE_DATA *Instance + ); + +EFI_STATUS +MnpTokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg + ); + +EFI_STATUS +MnpCancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg + ); + +VOID +MnpFlushRcvdDataQueue ( + IN MNP_INSTANCE_DATA *Instance + ); + +EFI_STATUS +MnpConfigureInstance ( + IN MNP_INSTANCE_DATA *Instance, + IN EFI_MANAGED_NETWORK_CONFIG_DATA *ConfigData OPTIONAL + ); + +EFI_STATUS +MnpGroupOp ( + IN MNP_INSTANCE_DATA *Instance, + IN BOOLEAN JoinFlag, + IN EFI_MAC_ADDRESS *MacAddr OPTIONAL, + IN MNP_GROUP_CONTROL_BLOCK *CtrlBlk OPTIONAL + ); + +BOOLEAN +MnpIsValidTxToken ( + IN MNP_INSTANCE_DATA *Instance, + IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token + ); + +VOID +MnpBuildTxPacket ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData, + OUT UINT8 **PktBuf, + OUT UINT32 *PktLen + ); + +EFI_STATUS +MnpSyncSendPacket ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN UINT8 *Packet, + IN UINT32 Length, + IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token + ); + +EFI_STATUS +MnpInstanceDeliverPacket ( + IN MNP_INSTANCE_DATA *Instance + ); + +VOID +EFIAPI +MnpRecycleRxData ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +EFI_STATUS +MnpReceivePacket ( + IN MNP_SERVICE_DATA *MnpServiceData + ); + +NET_BUF * +MnpAllocNbuf ( + IN MNP_SERVICE_DATA *MnpServiceData + ); + +VOID +MnpFreeNbuf ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN NET_BUF *Nbuf + ); + +VOID +EFIAPI +MnpCheckPacketTimeout ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +VOID +EFIAPI +MnpSystemPoll ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +EFI_STATUS +EFIAPI +MnpGetModeData ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +EFI_STATUS +EFIAPI +MnpConfigure ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL + ); + +EFI_STATUS +EFIAPI +MnpMcastIpToMac ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN BOOLEAN Ipv6Flag, + IN EFI_IP_ADDRESS *IpAddress, + OUT EFI_MAC_ADDRESS *MacAddress + ); + +EFI_STATUS +EFIAPI +MnpGroups ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_MAC_ADDRESS *MacAddress OPTIONAL + ); + +EFI_STATUS +EFIAPI +MnpTransmit ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token + ); + +EFI_STATUS +EFIAPI +MnpCancel ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token OPTIONAL + ); + +EFI_STATUS +EFIAPI +MnpReceive ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token + ); + +EFI_STATUS +EFIAPI +MnpPoll ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c b/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c new file mode 100644 index 0000000000..cde235912f --- /dev/null +++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c @@ -0,0 +1,1087 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + MnpIo.c + +Abstract: + + Implementation of Managed Network Protocol I/O functions. + + +**/ + +#include +#include +#include +#include +#include "MnpImpl.h" + + +/** + Validates the Mnp transmit token. + + @param Instance Pointer to the Mnp instance context data. + @param Token Pointer to the transmit token to check. + + @return The Token is valid or not. + +**/ +BOOLEAN +MnpIsValidTxToken ( + IN MNP_INSTANCE_DATA *Instance, + IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token + ) +{ + MNP_SERVICE_DATA *MnpServiceData; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData; + UINT32 Index; + UINT32 TotalLength; + EFI_MANAGED_NETWORK_FRAGMENT_DATA *FragmentTable; + + MnpServiceData = Instance->MnpServiceData; + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + + SnpMode = MnpServiceData->Snp->Mode; + TxData = Token->Packet.TxData; + + if ((Token->Event == NULL) || (TxData == NULL) || (TxData->FragmentCount == 0)) { + // + // The token is invalid if the Event is NULL, or the TxData is NULL, or + // the fragment count is zero. + // + MNP_DEBUG_WARN (("MnpIsValidTxToken: Invalid Token.\n")); + return FALSE; + } + + if ((TxData->DestinationAddress != NULL) && (TxData->HeaderLength != 0)) { + // + // The token is invalid if the HeaderLength isn't zero while the DestinationAddress + // is NULL (The destination address is already put into the packet). + // + MNP_DEBUG_WARN (("MnpIsValidTxToken: DestinationAddress isn't NULL, HeaderLength must be 0.\n")); + return FALSE; + } + + TotalLength = 0; + FragmentTable = TxData->FragmentTable; + for (Index = 0; Index < TxData->FragmentCount; Index++) { + + if ((FragmentTable[Index].FragmentLength == 0) || (FragmentTable[Index].FragmentBuffer == NULL)) { + // + // The token is invalid if any FragmentLength is zero or any FragmentBuffer is NULL. + // + MNP_DEBUG_WARN (("MnpIsValidTxToken: Invalid FragmentLength or FragmentBuffer.\n")); + return FALSE; + } + + TotalLength += FragmentTable[Index].FragmentLength; + } + + if ((TxData->DestinationAddress == NULL) && (FragmentTable[0].FragmentLength < TxData->HeaderLength)) { + // + // Media header is split between fragments. + // + return FALSE; + } + + if (TotalLength != (TxData->DataLength + TxData->HeaderLength)) { + // + // The length calculated from the fragment information doesn't equal to the + // sum of the DataLength and the HeaderLength. + // + MNP_DEBUG_WARN (("MnpIsValidTxData: Invalid Datalength compared with the sum of fragment length.\n")); + return FALSE; + } + + if (TxData->DataLength > MnpServiceData->Mtu) { + // + // The total length is larger than the MTU. + // + MNP_DEBUG_WARN (("MnpIsValidTxData: TxData->DataLength exceeds Mtu.\n")); + return FALSE; + } + + return TRUE; +} + + +/** + Build the packet to transmit from the TxData passed in. + + @param MnpServiceData Pointer to the mnp service context data. + @param TxData Pointer to the transmit data containing the + information to build the packet. + @param PktBuf Pointer to record the address of the packet. + @param PktLen Pointer to a UINT32 variable used to record the + packet's length. + + @return None. + +**/ +VOID +MnpBuildTxPacket ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData, + OUT UINT8 **PktBuf, + OUT UINT32 *PktLen + ) +{ + EFI_SIMPLE_NETWORK_MODE *SnpMode; + UINT8 *DstPos; + UINT16 Index; + + if ((TxData->DestinationAddress == NULL) && (TxData->FragmentCount == 1)) { + // + // Media header is in FragmentTable and there is only one fragment, + // use fragment buffer directly. + // + *PktBuf = TxData->FragmentTable[0].FragmentBuffer; + *PktLen = TxData->FragmentTable[0].FragmentLength; + } else { + // + // Either media header isn't in FragmentTable or there is more than + // one fragment, copy the data into the packet buffer. Reserve the + // media header space if necessary. + // + SnpMode = MnpServiceData->Snp->Mode; + DstPos = MnpServiceData->TxBuf; + + *PktLen = 0; + if (TxData->DestinationAddress != NULL) { + // + // If dest address is not NULL, move DstPos to reserve space for the + // media header. Add the media header length to buflen. + // + DstPos += SnpMode->MediaHeaderSize; + *PktLen += SnpMode->MediaHeaderSize; + } + + for (Index = 0; Index < TxData->FragmentCount; Index++) { + // + // Copy the data. + // + NetCopyMem ( + DstPos, + TxData->FragmentTable[Index].FragmentBuffer, + TxData->FragmentTable[Index].FragmentLength + ); + DstPos += TxData->FragmentTable[Index].FragmentLength; + } + + // + // Set the buffer pointer and the buffer length. + // + *PktBuf = MnpServiceData->TxBuf; + *PktLen += TxData->DataLength + TxData->HeaderLength; + } +} + + +/** + Synchronously send out the packet. + + @param MnpServiceData Pointer to the mnp service context data. + @param Packet Pointer to the pakcet buffer. + @param Length The length of the packet. + @param Token Pointer to the token the packet generated from. + + @retval EFI_SUCCESS The packet is sent out. + @retval EFI_TIMEOUT Time out occurs, the packet isn't sent. + @retval EFI_DEVICE_ERROR An unexpected network error occurs. + +**/ +EFI_STATUS +MnpSyncSendPacket ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN UINT8 *Packet, + IN UINT32 Length, + IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData; + UINT32 HeaderSize; + UINT8 *TxBuf; + + Snp = MnpServiceData->Snp; + TxData = Token->Packet.TxData; + + HeaderSize = Snp->Mode->MediaHeaderSize - TxData->HeaderLength; + + // + // Start the timeout event. + // + Status = gBS->SetTimer ( + MnpServiceData->TxTimeoutEvent, + TimerRelative, + MNP_TX_TIMEOUT_TIME + ); + if (EFI_ERROR (Status)) { + + goto SIGNAL_TOKEN; + } + + for (;;) { + // + // Transmit the packet through SNP. + // + Status = Snp->Transmit ( + Snp, + HeaderSize, + Length, + Packet, + TxData->SourceAddress, + TxData->DestinationAddress, + &TxData->ProtocolType + ); + if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) { + + Status = EFI_DEVICE_ERROR; + break; + } + + // + // If Status is EFI_SUCCESS, the packet is put in the transmit queue. + // if Status is EFI_NOT_READY, the transmit engine of the network interface is busy. + // Both need to sync SNP. + // + TxBuf = NULL; + do { + // + // Get the recycled transmit buffer status. + // + Snp->GetStatus (Snp, NULL, &TxBuf); + + if (!EFI_ERROR (gBS->CheckEvent (MnpServiceData->TxTimeoutEvent))) { + + Status = EFI_TIMEOUT; + break; + } + } while (TxBuf == NULL); + + if ((Status == EFI_SUCCESS) || (Status == EFI_TIMEOUT)) { + + break; + } else { + // + // Status is EFI_NOT_READY. Restart the timer event and call Snp->Transmit again. + // + gBS->SetTimer ( + MnpServiceData->TxTimeoutEvent, + TimerRelative, + MNP_TX_TIMEOUT_TIME + ); + } + } + + // + // Cancel the timer event. + // + gBS->SetTimer (MnpServiceData->TxTimeoutEvent, TimerCancel, 0); + +SIGNAL_TOKEN: + + Token->Status = Status; + gBS->SignalEvent (Token->Event); + + return EFI_SUCCESS; +} + + +/** + Try to deliver the received packet to the instance. + + @param Instance Pointer to the mnp instance context data. + + @retval EFI_SUCCESS The received packet is delivered, or there is no + packet to deliver, or there is no available receive + token. + @retval EFI_OUT_OF_RESOURCES The deliver fails due to lack of memory resource. + +**/ +EFI_STATUS +MnpInstanceDeliverPacket ( + IN MNP_INSTANCE_DATA *Instance + ) +{ + MNP_SERVICE_DATA *MnpServiceData; + MNP_RXDATA_WRAP *RxDataWrap; + NET_BUF *DupNbuf; + EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *RxToken; + + MnpServiceData = Instance->MnpServiceData; + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + + if (NetMapIsEmpty (&Instance->RxTokenMap) || NetListIsEmpty (&Instance->RcvdPacketQueue)) { + // + // No pending received data or no available receive token, return. + // + return EFI_SUCCESS; + } + + ASSERT (Instance->RcvdPacketQueueSize != 0); + + RxDataWrap = NET_LIST_HEAD (&Instance->RcvdPacketQueue, MNP_RXDATA_WRAP, WrapEntry); + if (RxDataWrap->Nbuf->RefCnt > 2) { + // + // There are other instances share this Nbuf, duplicate to get a + // copy to allow the instance to do R/W operations. + // + DupNbuf = MnpAllocNbuf (MnpServiceData); + if (DupNbuf == NULL) { + MNP_DEBUG_WARN (("MnpDeliverPacket: Failed to allocate a free Nbuf.\n")); + + return EFI_OUT_OF_RESOURCES; + } + + // + // Duplicate the net buffer. + // + NetbufDuplicate (RxDataWrap->Nbuf, DupNbuf, 0); + MnpFreeNbuf (MnpServiceData, RxDataWrap->Nbuf); + RxDataWrap->Nbuf = DupNbuf; + } + + // + // All resources are OK, remove the packet from the queue. + // + NetListRemoveHead (&Instance->RcvdPacketQueue); + Instance->RcvdPacketQueueSize--; + + RxData = &RxDataWrap->RxData; + SnpMode = MnpServiceData->Snp->Mode; + + // + // Set all the buffer pointers. + // + RxData->MediaHeader = NetbufGetByte (RxDataWrap->Nbuf, 0, NULL); + RxData->DestinationAddress = RxData->MediaHeader; + RxData->SourceAddress = (UINT8 *) RxData->MediaHeader + SnpMode->HwAddressSize; + RxData->PacketData = (UINT8 *) RxData->MediaHeader + SnpMode->MediaHeaderSize; + + // + // Insert this RxDataWrap into the delivered queue. + // + NetListInsertTail (&Instance->RxDeliveredPacketQueue, &RxDataWrap->WrapEntry); + + // + // Get the receive token from the RxTokenMap. + // + RxToken = NetMapRemoveHead (&Instance->RxTokenMap, NULL); + + // + // Signal this token's event. + // + RxToken->Packet.RxData = &RxDataWrap->RxData; + RxToken->Status = EFI_SUCCESS; + gBS->SignalEvent (RxToken->Event); + + return EFI_SUCCESS; +} + + +/** + Deliver the received packet for the instances belonging to the MnpServiceData. + + @param MnpServiceData Pointer to the mnp service context data. + + @return None. + +**/ +STATIC +VOID +MnpDeliverPacket ( + IN MNP_SERVICE_DATA *MnpServiceData + ) +{ + NET_LIST_ENTRY *Entry; + MNP_INSTANCE_DATA *Instance; + + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + + NET_LIST_FOR_EACH (Entry, &MnpServiceData->ChildrenList) { + Instance = NET_LIST_USER_STRUCT (Entry, MNP_INSTANCE_DATA, InstEntry); + NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE); + + // + // Try to deliver packet for this instance. + // + MnpInstanceDeliverPacket (Instance); + } +} + + +/** + Recycle the RxData and other resources used to hold and deliver the received + packet. + + @param Event The event this notify function registered to. + @param Context Pointer to the context data registerd to the Event. + + @return None. + +**/ +VOID +EFIAPI +MnpRecycleRxData ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + MNP_RXDATA_WRAP *RxDataWrap; + MNP_SERVICE_DATA *MnpServiceData; + + ASSERT (Context != NULL); + + RxDataWrap = (MNP_RXDATA_WRAP *) Context; + NET_CHECK_SIGNATURE (RxDataWrap->Instance, MNP_INSTANCE_DATA_SIGNATURE); + + ASSERT (RxDataWrap->Nbuf != NULL); + + MnpServiceData = RxDataWrap->Instance->MnpServiceData; + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + + // + // Free this Nbuf. + // + MnpFreeNbuf (MnpServiceData, RxDataWrap->Nbuf); + RxDataWrap->Nbuf = NULL; + + // + // Close the recycle event. + // + gBS->CloseEvent (RxDataWrap->RxData.RecycleEvent); + + // + // Remove this Wrap entry from the list. + // + NetListRemoveEntry (&RxDataWrap->WrapEntry); + + NetFreePool (RxDataWrap); +} + + +/** + Queue the received packet into instance's receive queue. + + @param Instance Pointer to the mnp instance context data. + @param RxDataWrap Pointer to the Wrap structure containing the + received data and other information. + + @return None. + +**/ +STATIC +VOID +MnpQueueRcvdPacket ( + IN MNP_INSTANCE_DATA *Instance, + IN MNP_RXDATA_WRAP *RxDataWrap + ) +{ + MNP_RXDATA_WRAP *OldRxDataWrap; + + NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE); + + // + // Check the queue size. If it exceeds the limit, drop one packet + // from the head. + // + if (Instance->RcvdPacketQueueSize == MNP_MAX_RCVD_PACKET_QUE_SIZE) { + + MNP_DEBUG_WARN (("MnpQueueRcvdPacket: Drop one packet bcz queue size limit reached.\n")); + + // + // Get the oldest packet. + // + OldRxDataWrap = NET_LIST_HEAD ( + &Instance->RcvdPacketQueue, + MNP_RXDATA_WRAP, + WrapEntry + ); + + // + // Recycle this OldRxDataWrap, this entry will be removed by the callee. + // + MnpRecycleRxData (NULL, (VOID *) OldRxDataWrap); + Instance->RcvdPacketQueueSize--; + } + + // + // Update the timeout tick using the configured parameter. + // + RxDataWrap->TimeoutTick = Instance->ConfigData.ReceivedQueueTimeoutValue; + + // + // Insert this Wrap into the instance queue. + // + NetListInsertTail (&Instance->RcvdPacketQueue, &RxDataWrap->WrapEntry); + Instance->RcvdPacketQueueSize++; +} + + +/** + Match the received packet with the instance receive filters. + + @param Instance Pointer to the mnp instance context data. + @param RxData Pointer to the EFI_MANAGED_NETWORK_RECEIVE_DATA. + @param GroupAddress Pointer to the GroupAddress, the GroupAddress is + non-NULL and it contains the destination multicast + mac address of the received packet if the packet + destinated to a multicast mac address. + @param PktAttr The received packets attribute. + + @return The received packet matches the instance's receive filters or not. + +**/ +STATIC +BOOLEAN +MnpMatchPacket ( + IN MNP_INSTANCE_DATA *Instance, + IN EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData, + IN MNP_GROUP_ADDRESS *GroupAddress OPTIONAL, + IN UINT8 PktAttr + ) +{ + EFI_MANAGED_NETWORK_CONFIG_DATA *ConfigData; + NET_LIST_ENTRY *Entry; + MNP_GROUP_CONTROL_BLOCK *GroupCtrlBlk; + + NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE); + + ConfigData = &Instance->ConfigData; + + if (ConfigData->EnablePromiscuousReceive) { + // + // Always match if this instance is configured to be promiscuous. + // + return TRUE; + } + // + // Check the protocol type. + // + if ((ConfigData->ProtocolTypeFilter != 0) && (ConfigData->ProtocolTypeFilter != RxData->ProtocolType)) { + return FALSE; + } + + // + // The protocol type is matched, check receive filter, include unicast and broadcast. + // + if ((Instance->ReceiveFilter & PktAttr) != 0) { + return TRUE; + } + + // + // Check multicast addresses. + // + if (ConfigData->EnableMulticastReceive && RxData->MulticastFlag) { + + ASSERT (GroupAddress != NULL); + + NET_LIST_FOR_EACH (Entry, &Instance->GroupCtrlBlkList) { + + GroupCtrlBlk = NET_LIST_USER_STRUCT (Entry, MNP_GROUP_CONTROL_BLOCK, CtrlBlkEntry); + if (GroupCtrlBlk->GroupAddress == GroupAddress) { + // + // The instance is configured to receiveing packets destinated to this + // multicast address. + // + return TRUE; + } + } + } + + // + // No match. + // + return FALSE; +} + + +/** + Analyse the received packets. + + @param MnpServiceData Pointer to the mnp service context data. + @param Nbuf Pointer to the net buffer holding the received + packet. + @param RxData Pointer to the buffer used to save the analysed + result in EFI_MANAGED_NETWORK_RECEIVE_DATA. + @param GroupAddress Pointer to pointer to a MNP_GROUP_ADDRESS used to + pass out the address of the multicast address the + received packet destinated to. + @param PktAttr Pointer to the buffer used to save the analysed + packet attribute. + + @return None. + +**/ +STATIC +VOID +MnpAnalysePacket ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN NET_BUF *Nbuf, + IN EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData, + OUT MNP_GROUP_ADDRESS **GroupAddress, + OUT UINT8 *PktAttr + ) +{ + EFI_SIMPLE_NETWORK_MODE *SnpMode; + UINT8 *BufPtr; + NET_LIST_ENTRY *Entry; + + SnpMode = MnpServiceData->Snp->Mode; + + // + // Get the packet buffer. + // + BufPtr = NetbufGetByte (Nbuf, 0, NULL); + ASSERT (BufPtr != NULL); + + // + // Set the initial values. + // + RxData->BroadcastFlag = FALSE; + RxData->MulticastFlag = FALSE; + RxData->PromiscuousFlag = FALSE; + *PktAttr = UNICAST_PACKET; + + if (!NET_MAC_EQUAL (&SnpMode->CurrentAddress, BufPtr, SnpMode->HwAddressSize)) { + // + // This packet isn't destinated to our current mac address, it't not unicast. + // + *PktAttr = 0; + + if (NET_MAC_EQUAL (&SnpMode->BroadcastAddress, BufPtr, SnpMode->HwAddressSize)) { + // + // It's broadcast. + // + RxData->BroadcastFlag = TRUE; + *PktAttr = BROADCAST_PACKET; + } else if ((*BufPtr & 0x01) == 0x1) { + // + // It's multicast, try to match the multicast filters. + // + NET_LIST_FOR_EACH (Entry, &MnpServiceData->GroupAddressList) { + + *GroupAddress = NET_LIST_USER_STRUCT (Entry, MNP_GROUP_ADDRESS, AddrEntry); + if (NET_MAC_EQUAL (BufPtr, &((*GroupAddress)->Address), SnpMode->HwAddressSize)) { + RxData->MulticastFlag = TRUE; + break; + } + } + + if (!RxData->MulticastFlag) { + // + // No match, set GroupAddress to NULL. This multicast packet must + // be the result of PROMISUCOUS or PROMISUCOUS_MULTICAST flag is on. + // + *GroupAddress = NULL; + RxData->PromiscuousFlag = TRUE; + + if (MnpServiceData->PromiscuousCount == 0) { + // + // Skip the below code, there is no receiver of this packet. + // + return ; + } + } + } else { + RxData->PromiscuousFlag = TRUE; + } + } + + NetZeroMem (&RxData->Timestamp, sizeof (EFI_TIME)); + + // + // Fill the common parts of RxData. + // + RxData->PacketLength = Nbuf->TotalSize; + RxData->HeaderLength = SnpMode->MediaHeaderSize; + RxData->AddressLength = SnpMode->HwAddressSize; + RxData->DataLength = RxData->PacketLength - RxData->HeaderLength; + RxData->ProtocolType = NTOHS (*(UINT16 *) (BufPtr + 2 * SnpMode->HwAddressSize)); +} + + +/** + Wrap the RxData. + + @param Instance Pointer to the mnp instance context data. + @param RxData Pointer to the receive data to wrap. + + @return Pointer to a MNP_RXDATA_WRAP which wraps the RxData. + +**/ +STATIC +MNP_RXDATA_WRAP * +MnpWrapRxData ( + IN MNP_INSTANCE_DATA *Instance, + IN EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData + ) +{ + EFI_STATUS Status; + MNP_RXDATA_WRAP *RxDataWrap; + + // + // Allocate memory. + // + RxDataWrap = NetAllocatePool (sizeof (MNP_RXDATA_WRAP)); + if (RxDataWrap == NULL) { + MNP_DEBUG_ERROR (("MnpDispatchPacket: Failed to allocate a MNP_RXDATA_WRAP.\n")); + return NULL; + } + + RxDataWrap->Instance = Instance; + + // + // Fill the RxData in RxDataWrap, + // + RxDataWrap->RxData = *RxData; + + // + // Create the recycle event. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + NET_TPL_RECYCLE, + MnpRecycleRxData, + RxDataWrap, + &RxDataWrap->RxData.RecycleEvent + ); + if (EFI_ERROR (Status)) { + + MNP_DEBUG_ERROR (("MnpDispatchPacket: gBS->CreateEvent failed, %r.\n", Status)); + NetFreePool (RxDataWrap); + return NULL; + } + + return RxDataWrap; +} + + +/** + Enqueue the received the packets to the instances belonging to the + MnpServiceData. + + @param MnpServiceData Pointer to the mnp service context data. + @param Nbuf Pointer to the net buffer representing the received + packet. + + @return None. + +**/ +STATIC +VOID +MnpEnqueuePacket ( + IN MNP_SERVICE_DATA *MnpServiceData, + IN NET_BUF *Nbuf + ) +{ + NET_LIST_ENTRY *Entry; + MNP_INSTANCE_DATA *Instance; + EFI_MANAGED_NETWORK_RECEIVE_DATA RxData; + UINT8 PktAttr; + MNP_GROUP_ADDRESS *GroupAddress; + MNP_RXDATA_WRAP *RxDataWrap; + + // + // First, analyse the packet header. + // + MnpAnalysePacket (MnpServiceData, Nbuf, &RxData, &GroupAddress, &PktAttr); + + if (RxData.PromiscuousFlag && (MnpServiceData->PromiscuousCount == 0)) { + // + // No receivers, no more action need. + // + return ; + } + + // + // Iterate the children to find match. + // + NET_LIST_FOR_EACH (Entry, &MnpServiceData->ChildrenList) { + + Instance = NET_LIST_USER_STRUCT (Entry, MNP_INSTANCE_DATA, InstEntry); + NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE); + + if (!Instance->Configured) { + continue; + } + + // + // Check the packet against the instance receive filters. + // + if (MnpMatchPacket (Instance, &RxData, GroupAddress, PktAttr)) { + + // + // Wrap the RxData. + // + RxDataWrap = MnpWrapRxData (Instance, &RxData); + if (RxDataWrap == NULL) { + continue; + } + + // + // Associate RxDataWrap with Nbuf and increase the RefCnt. + // + RxDataWrap->Nbuf = Nbuf; + NET_GET_REF (RxDataWrap->Nbuf); + + // + // Queue the packet into the instance queue. + // + MnpQueueRcvdPacket (Instance, RxDataWrap); + } + } +} + + +/** + Try to receive a packet and deliver it. + + @param MnpServiceData Pointer to the mnp service context data. + + @retval EFI_SUCCESS add return value to function comment + @retval EFI_NOT_STARTED The simple network protocol is not started. + @retval EFI_NOT_READY No packet received. + @retval EFI_DEVICE_ERROR An unexpected error occurs. + +**/ +EFI_STATUS +MnpReceivePacket ( + IN MNP_SERVICE_DATA *MnpServiceData + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + NET_BUF *Nbuf; + UINT8 *BufPtr; + UINTN BufLen; + UINTN HeaderSize; + UINT32 Trimmed; + + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + + Snp = MnpServiceData->Snp; + if (Snp->Mode->State != EfiSimpleNetworkInitialized) { + // + // The simple network protocol is not started. + // + return EFI_NOT_STARTED; + } + + if (NetListIsEmpty (&MnpServiceData->ChildrenList)) { + // + // There is no child, no need to receive packets. + // + return EFI_SUCCESS; + } + + if (MnpServiceData->RxNbufCache == NULL) { + // + // Try to get a new buffer as there may be buffers recycled. + // + MnpServiceData->RxNbufCache = MnpAllocNbuf (MnpServiceData); + + if (MnpServiceData->RxNbufCache == NULL) { + // + // No availabe buffer in the buffer pool. + // + return EFI_DEVICE_ERROR; + } + + NetbufAllocSpace ( + MnpServiceData->RxNbufCache, + MnpServiceData->BufferLength, + NET_BUF_TAIL + ); + } + + Nbuf = MnpServiceData->RxNbufCache; + BufLen = Nbuf->TotalSize; + BufPtr = NetbufGetByte (Nbuf, 0, NULL); + ASSERT (BufPtr != NULL); + + // + // Receive packet through Snp. + // + Status = Snp->Receive (Snp, &HeaderSize, &BufLen, BufPtr, NULL, NULL, NULL); + if (EFI_ERROR (Status)) { + + DEBUG_CODE ( + if (Status != EFI_NOT_READY) { + MNP_DEBUG_ERROR (("MnpReceivePacket: Snp->Receive() = %r.\n", Status)); + } + ); + + return Status; + } + + // + // Sanity check. + // + if ((HeaderSize != Snp->Mode->MediaHeaderSize) || (BufLen < HeaderSize)) { + + MNP_DEBUG_WARN ( + ("MnpReceivePacket: Size error, HL:TL = %d:%d.\n", + HeaderSize, + BufLen) + ); + return EFI_DEVICE_ERROR; + } + + Trimmed = 0; + if (Nbuf->TotalSize != BufLen) { + // + // Trim the packet from tail. + // + Trimmed = NetbufTrim (Nbuf, Nbuf->TotalSize - (UINT32) BufLen, NET_BUF_TAIL); + ASSERT (Nbuf->TotalSize == BufLen); + } + + // + // Enqueue the packet to the matched instances. + // + MnpEnqueuePacket (MnpServiceData, Nbuf); + + if (Nbuf->RefCnt > 2) { + // + // RefCnt > 2 indicates there is at least one receiver of this packet. + // Free the current RxNbufCache and allocate a new one. + // + MnpFreeNbuf (MnpServiceData, Nbuf); + + Nbuf = MnpAllocNbuf (MnpServiceData); + MnpServiceData->RxNbufCache = Nbuf; + if (Nbuf == NULL) { + MNP_DEBUG_ERROR (("MnpReceivePacket: Alloc packet for receiving cache failed.\n")); + return EFI_DEVICE_ERROR; + } + + NetbufAllocSpace (Nbuf, MnpServiceData->BufferLength, NET_BUF_TAIL); + } else { + // + // No receiver for this packet. + // + NetbufAllocSpace (Nbuf, Trimmed, NET_BUF_TAIL); + goto EXIT; + } + // + // Deliver the queued packets. + // + MnpDeliverPacket (MnpServiceData); + +EXIT: + + ASSERT (Nbuf->TotalSize == MnpServiceData->BufferLength); + + return Status; +} + + +/** + Remove the received packets if timeout occurs. + + @param Event The event this notify function registered to. + @param Context Pointer to the context data registered to the + event. + + @return None. + +**/ +VOID +EFIAPI +MnpCheckPacketTimeout ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + MNP_SERVICE_DATA *MnpServiceData; + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *RxEntry; + NET_LIST_ENTRY *NextEntry; + MNP_INSTANCE_DATA *Instance; + MNP_RXDATA_WRAP *RxDataWrap; + EFI_TPL OldTpl; + + MnpServiceData = (MNP_SERVICE_DATA *) Context; + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + + NET_LIST_FOR_EACH (Entry, &MnpServiceData->ChildrenList) { + + Instance = NET_LIST_USER_STRUCT (Entry, MNP_INSTANCE_DATA, InstEntry); + NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE); + + if (!Instance->Configured || (Instance->ConfigData.ReceivedQueueTimeoutValue == 0)) { + // + // This instance is not configured or there is no receive time out, + // just skip to the next instance. + // + continue; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_RECYCLE); + + NET_LIST_FOR_EACH_SAFE (RxEntry, NextEntry, &Instance->RcvdPacketQueue) { + + RxDataWrap = NET_LIST_USER_STRUCT (RxEntry, MNP_RXDATA_WRAP, WrapEntry); + + if (RxDataWrap->TimeoutTick >= MNP_TIMEOUT_CHECK_INTERVAL) { + + RxDataWrap->TimeoutTick -= MNP_TIMEOUT_CHECK_INTERVAL; + } else { + // + // Drop the timeout packet. + // + MNP_DEBUG_WARN (("MnpCheckPacketTimeout: Received packet timeout.\n")); + MnpRecycleRxData (NULL, RxDataWrap); + Instance->RcvdPacketQueueSize--; + } + } + + NET_RESTORE_TPL (OldTpl); + } +} + + +/** + Poll to receive the packets from Snp. This function is either called by upperlayer + protocols/applications or the system poll timer notify mechanism. + + @param Event The event this notify function registered to. + @param Context Pointer to the context data registered to the + event. + + @return None. + +**/ +VOID +EFIAPI +MnpSystemPoll ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + MNP_SERVICE_DATA *MnpServiceData; + + MnpServiceData = (MNP_SERVICE_DATA *) Context; + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + + // + // Try to receive packets from Snp. + // + MnpReceivePacket (MnpServiceData); +} diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c b/MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c new file mode 100644 index 0000000000..02c0065c34 --- /dev/null +++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c @@ -0,0 +1,655 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + MnpMain.c + +Abstract: + + Implementation of Managed Network Protocol public services. + + +**/ + +#include +#include + +#include "MnpImpl.h" + + +/** + Get configuration data of this instance. + + @param This Pointer to the Managed Network Protocol. + @param MnpConfigData Pointer to strorage for MNP operational + parameters. + @param SnpModeData Pointer to strorage for SNP operational + parameters. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This MNP child driver instance has not been + configured The default values are returned in + MnpConfigData if it is not NULL. + +**/ +EFI_STATUS +EFIAPI +MnpGetModeData ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ) +{ + MNP_INSTANCE_DATA *Instance; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_TPL OldTpl; + EFI_STATUS Status; + + if (This == NULL) { + + return EFI_INVALID_PARAMETER; + } + + Instance = MNP_INSTANCE_DATA_FROM_THIS (This); + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (MnpConfigData != NULL) { + // + // Copy the instance configuration data. + // + *MnpConfigData = Instance->ConfigData; + } + + if (SnpModeData != NULL) { + // + // Copy the underlayer Snp mode data. + // + Snp = Instance->MnpServiceData->Snp; + *SnpModeData = *(Snp->Mode); + } + + if (!Instance->Configured) { + Status = EFI_NOT_STARTED; + } else { + Status = EFI_SUCCESS; + } + + NET_RESTORE_TPL (OldTpl); + + return Status; +} + + +/** + Set or clear the operational parameters for the MNP child driver. + + @param This Pointer to the Managed Network Protocol. + @param MnpConfigData Pointer to the configuration data that will be + assigned to the MNP child driver instance. If + NULL, the MNP child driver instance is reset to + startup defaults and all pending transmit and + receive requests are flushed. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Required system resources (usually memory) could + not be allocated. + @retval EFI_UNSUPPORTED EnableReceiveTimestamps is TRUE, this + implementation doesn't support it. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval Other The MNP child driver instance has been reset to + startup defaults. + +**/ +EFI_STATUS +EFIAPI +MnpConfigure ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL + ) +{ + MNP_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + EFI_STATUS Status; + + if ((This == NULL) || + ((MnpConfigData != NULL) && + (MnpConfigData->ProtocolTypeFilter > 0) && + (MnpConfigData->ProtocolTypeFilter <= 1500))) { + + return EFI_INVALID_PARAMETER; + } + + Instance = MNP_INSTANCE_DATA_FROM_THIS (This); + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if ((MnpConfigData == NULL) && (!Instance->Configured)) { + // + // If the instance is not configured and a reset is requested, just return. + // + Status = EFI_SUCCESS; + goto ON_EXIT; + } + + // + // Configure the instance. + // + Status = MnpConfigureInstance (Instance, MnpConfigData); + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + + return Status; +} + + +/** + Translate a multicast IP address to a multicast hardware (MAC) address. + + @param This Pointer to the Managed Network Protocol. + @param Ipv6Flag Set to TRUE if IpAddress is an IPv6 multicast + address. Set to FALSE if IpAddress is an IPv4 + multicast address. + @param IpAddress Pointer to the multicast IP address to convert. + @param MacAddress Pointer to the resulting multicast MAC address. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more parameter is invalid. + @retval EFI_NOT_STARTED This MNP child driver instance has not been + configured. + @retval EFI_UNSUPPORTED Ipv6Flag is TRUE, this implementation doesn't + supported it. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval Other The address could not be converted. + +**/ +EFI_STATUS +EFIAPI +MnpMcastIpToMac ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN BOOLEAN Ipv6Flag, + IN EFI_IP_ADDRESS *IpAddress, + OUT EFI_MAC_ADDRESS *MacAddress + ) +{ + EFI_STATUS Status; + MNP_INSTANCE_DATA *Instance; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_TPL OldTpl; + + if ((This == NULL) || (IpAddress == NULL) || (MacAddress == NULL)) { + + return EFI_INVALID_PARAMETER; + } + + if (Ipv6Flag) { + // + // Currently IPv6 isn't supported. + // + return EFI_UNSUPPORTED; + } + + if (!IP4_IS_MULTICAST (EFI_NTOHL (*IpAddress))) { + // + // The IPv4 address passed in is not a multicast address. + // + return EFI_INVALID_PARAMETER; + } + + Instance = MNP_INSTANCE_DATA_FROM_THIS (This); + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (!Instance->Configured) { + + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + Snp = Instance->MnpServiceData->Snp; + ASSERT (Snp != NULL); + + if (Snp->Mode->IfType == NET_IFTYPE_ETHERNET) { + // + // Translate the IPv4 address into a multicast MAC address if the NIC is an + // ethernet NIC. + // + MacAddress->Addr[0] = 0x01; + MacAddress->Addr[1] = 0x00; + MacAddress->Addr[2] = 0x5E; + MacAddress->Addr[3] = IpAddress->v4.Addr[1] & 0x7F; + MacAddress->Addr[4] = IpAddress->v4.Addr[2]; + MacAddress->Addr[5] = IpAddress->v4.Addr[3]; + + Status = EFI_SUCCESS; + } else { + // + // Invoke Snp to translate the multicast IP address. + // + Status = Snp->MCastIpToMac ( + Snp, + Ipv6Flag, + IpAddress, + MacAddress + ); + } + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + + return Status; +} + + +/** + Enable or disable receie filters for multicast address. + + @param This Pointer to the Managed Network Protocol. + @param JoinFlag Set to TRUE to join this multicast group. Set to + FALSE to leave this multicast group. + @param MacAddress Pointer to the multicast MAC group (address) to + join or leave. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more parameter is invalid + @retval EFI_NOT_STARTED This MNP child driver instance has not been + configured. + @retval EFI_ALREADY_STARTED The supplied multicast group is already joined. + @retval EFI_NOT_FOUND The supplied multicast group is not joined. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval Other The requested operation could not be completed. + The MNP multicast group settings are unchanged. + +**/ +EFI_STATUS +EFIAPI +MnpGroups ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_MAC_ADDRESS *MacAddress OPTIONAL + ) +{ + MNP_INSTANCE_DATA *Instance; + EFI_SIMPLE_NETWORK_MODE *SnpMode; + MNP_GROUP_CONTROL_BLOCK *GroupCtrlBlk; + MNP_GROUP_ADDRESS *GroupAddress; + NET_LIST_ENTRY *ListEntry; + BOOLEAN AddressExist; + EFI_TPL OldTpl; + EFI_STATUS Status; + + if (This == NULL || (JoinFlag && (MacAddress == NULL))) { + // + // This is NULL, or it's a join operation but MacAddress is NULL. + // + return EFI_INVALID_PARAMETER; + } + + Instance = MNP_INSTANCE_DATA_FROM_THIS (This); + SnpMode = Instance->MnpServiceData->Snp->Mode; + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (!Instance->Configured) { + + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + if ((!Instance->ConfigData.EnableMulticastReceive) || + ((MacAddress != NULL) && !NET_MAC_IS_MULTICAST (MacAddress, &SnpMode->BroadcastAddress, SnpMode->HwAddressSize))) { + // + // The instance isn't configured to do mulitcast receive. OR + // the passed in MacAddress is not a mutlticast mac address. + // + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Status = EFI_SUCCESS; + AddressExist = FALSE; + GroupCtrlBlk = NULL; + + if (MacAddress != NULL) { + // + // Search the instance's GroupCtrlBlkList to find the specific address. + // + NET_LIST_FOR_EACH (ListEntry, &Instance->GroupCtrlBlkList) { + + GroupCtrlBlk = NET_LIST_USER_STRUCT ( + ListEntry, + MNP_GROUP_CONTROL_BLOCK, + CtrlBlkEntry + ); + GroupAddress = GroupCtrlBlk->GroupAddress; + if (0 == NetCompareMem ( + MacAddress, + &GroupAddress->Address, + SnpMode->HwAddressSize + )) { + // + // There is already the same multicast mac address configured. + // + AddressExist = TRUE; + break; + } + } + + if (JoinFlag && AddressExist) { + // + // The multicast mac address to join already exists. + // + Status = EFI_ALREADY_STARTED; + } + + if (!JoinFlag && !AddressExist) { + // + // The multicast mac address to leave doesn't exist in this instance. + // + Status = EFI_NOT_FOUND; + } + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } else if (NetListIsEmpty (&Instance->GroupCtrlBlkList)) { + // + // The MacAddress is NULL and there is no configured multicast mac address, + // just return. + // + goto ON_EXIT; + } + + // + // OK, it is time to take action. + // + Status = MnpGroupOp (Instance, JoinFlag, MacAddress, GroupCtrlBlk); + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + + return Status; +} + + +/** + Place an outgoing packet into the transmit queue. + + @param This Pointer to the Managed Network Protocol. + @param Token Pointer to a token associated with the transmit + data descriptor. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more parameter is invalid + @retval EFI_NOT_STARTED This MNP child driver instance has not been + configured. + @retval EFI_ACCESS_DENIED The transmit completion token is already in the + transmit queue. + @retval EFI_OUT_OF_RESOURCES The transmit data could not be queued due to a + lack of system resources (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The MNP child driver instance has been reset to + startup defaults. + @retval EFI_NOT_READY The transmit request could not be queued because + the transmit queue is full. + +**/ +EFI_STATUS +EFIAPI +MnpTransmit ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + MNP_INSTANCE_DATA *Instance; + MNP_SERVICE_DATA *MnpServiceData; + UINT8 *PktBuf; + UINT32 PktLen; + EFI_TPL OldTpl; + + if ((This == NULL) || (Token == NULL)) { + + return EFI_INVALID_PARAMETER; + } + + Instance = MNP_INSTANCE_DATA_FROM_THIS (This); + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (!Instance->Configured) { + + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + if (!MnpIsValidTxToken (Instance, Token)) { + // + // The Token is invalid. + // + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + MnpServiceData = Instance->MnpServiceData; + NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE); + + // + // Build the tx packet + // + MnpBuildTxPacket (MnpServiceData, Token->Packet.TxData, &PktBuf, &PktLen); + + // + // OK, send the packet synchronously. + // + Status = MnpSyncSendPacket (MnpServiceData, PktBuf, PktLen, Token); + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + + return Status; +} + + +/** + Place an asynchronous receiving request into the receiving queue. + + @param This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL + instance. + @param Token Pointer to a token associated with the receive + data descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED This MNP child driver instance has not been + configured. + @retval EFI_INVALID_PARAMETER One or more parameter is invalid. + @retval EFI_OUT_OF_RESOURCES The transmit data could not be queued due to a + lack of system resources (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The MNP child driver instance has been reset to + startup defaults. + @retval EFI_ACCESS_DENIED The receive completion token was already in the + receive queue. + @retval EFI_NOT_READY The receive request could not be queued because + the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +MnpReceive ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + MNP_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + + if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) { + + return EFI_INVALID_PARAMETER; + } + + Instance = MNP_INSTANCE_DATA_FROM_THIS (This); + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (!Instance->Configured) { + + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + // + // Check whether this token(event) is already in the rx token queue. + // + Status = NetMapIterate (&Instance->RxTokenMap, MnpTokenExist, (VOID *) Token); + if (EFI_ERROR (Status)) { + + goto ON_EXIT; + } + + // + // Insert the Token into the RxTokenMap. + // + Status = NetMapInsertTail (&Instance->RxTokenMap, (VOID *) Token, NULL); + + if (!EFI_ERROR (Status)) { + // + // Try to deliver any buffered packets. + // + Status = MnpInstanceDeliverPacket (Instance); + } + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + + return Status; +} + + +/** + Abort a pending transmit or receive request. + + @param This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL + instance. + @param Token Pointer to a token that has been issued by + EFI_MANAGED_NETWORK_PROTOCOL.Transmit() or + EFI_MANAGED_NETWORK_PROTOCOL.Receive(). If NULL, + all pending tokens are aborted. + + @retval EFI_SUCCESS The asynchronous I/O request was aborted and + Token->Event was signaled. + @retval EFI_NOT_STARTED This MNP child driver instance has not been + configured. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_FOUND The asynchronous I/O request was not found in the + transmit or receive queue. It has either completed + or was not issued by Transmit() and Receive(). + +**/ +EFI_STATUS +EFIAPI +MnpCancel ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + EFI_STATUS Status; + MNP_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + + if (This == NULL) { + + return EFI_INVALID_PARAMETER; + } + + Instance = MNP_INSTANCE_DATA_FROM_THIS (This); + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (!Instance->Configured) { + + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + // + // Iterate the RxTokenMap to cancel the specified Token. + // + Status = NetMapIterate (&Instance->RxTokenMap, MnpCancelTokens, (VOID *) Token); + + if (Token != NULL) { + + Status = (Status == EFI_ABORTED) ? EFI_SUCCESS : EFI_NOT_FOUND; + } + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + + return Status; +} + + +/** + Poll the network interface to do transmit/receive work. + + @param This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL + instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This MNP child driver instance has not been + configured. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The MNP child driver instance has been reset to + startup defaults. + @retval EFI_NOT_READY No incoming or outgoing data was processed. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or + receive queue. + +**/ +EFI_STATUS +EFIAPI +MnpPoll ( + IN EFI_MANAGED_NETWORK_PROTOCOL *This + ) +{ + EFI_STATUS Status; + MNP_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = MNP_INSTANCE_DATA_FROM_THIS (This); + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (!Instance->Configured) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + // + // Try to receive packets. + // + Status = MnpReceivePacket (Instance->MnpServiceData); + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/ComponentName.c b/MdeModulePkg/Universal/Network/SnpDxe/ComponentName.c new file mode 100644 index 0000000000..6da17a3633 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/ComponentName.c @@ -0,0 +1,163 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + ComponentName.c + +Abstract: + + +**/ + + + +#include "Snp.h" + +// +// EFI Component Name Functions +// +EFI_STATUS +EFIAPI +SimpleNetworkComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +SimpleNetworkComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +EFI_COMPONENT_NAME_PROTOCOL gSimpleNetworkComponentName = { + SimpleNetworkComponentNameGetDriverName, + SimpleNetworkComponentNameGetControllerName, + "eng" +}; + +static EFI_UNICODE_STRING_TABLE mSimpleNetworkDriverNameTable[] = { + { + "eng", + L"Simple Network Protocol Driver" + }, + { + NULL, + NULL + } +}; + +EFI_STATUS +EFIAPI +SimpleNetworkComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + Language - A pointer to a three character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + DriverName - A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + Returns: + EFI_SUCCESS - The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - DriverName is NULL. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return LookupUnicodeString ( + Language, + gSimpleNetworkComponentName.SupportedLanguages, + mSimpleNetworkDriverNameTable, + DriverName + ); +} + +EFI_STATUS +EFIAPI +SimpleNetworkComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +/*++ + + Routine Description: + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + Arguments: + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + ControllerHandle - The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + ChildHandle - The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + Language - A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + ControllerName - A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language from the point of view of the driver specified + by This. + + Returns: + EFI_SUCCESS - The Unicode string for the user readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - ControllerName is NULL. + EFI_UNSUPPORTED - The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return EFI_UNSUPPORTED; +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf b/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf new file mode 100644 index 0000000000..34623da4c7 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf @@ -0,0 +1,75 @@ +#/** @file +# Component name for module SNP +# +# FIX ME! +# Copyright (c) 2006, Intel Corporation. All right reserved. +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SnpDxe + FILE_GUID = A2f436EA-A127-4EF8-957C-8048606FF670 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = InitializeSnpNiiDriver + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + receive.c + snp.h + nvdata.c + get_status.c + start.c + snp.c + stop.c + statistics.c + reset.c + shutdown.c + mcast_ip_to_mac.c + transmit.c + WaitForPacket.c + receive_filters.c + initialize.c + ComponentName.c + callback.c + station_address.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + UefiLib + BaseLib + UefiBootServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + DebugLib + + +[Protocols] + gEfiPciIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiNetworkInterfaceIdentifierProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiNetworkInterfaceIdentifierProtocolGuid_31 # PROTOCOL ALWAYS_CONSUMED diff --git a/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.msa b/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.msa new file mode 100644 index 0000000000..748b5c5383 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.msa @@ -0,0 +1,90 @@ + + + SnpDxe + DXE_DRIVER + A2f436EA-A127-4EF8-957C-8048606FF670 + 1.0 + Component name for module SNP + FIX ME! + Copyright (c) 2006, Intel Corporation. All right reserved. + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF EBC + false + SnpDxe + + + + DebugLib + + + BaseMemoryLib + + + UefiDriverEntryPoint + + + UefiBootServicesTableLib + + + BaseLib + + + UefiLib + + + + station_address.c + SNPEntry.c + callback.c + ComponentName.c + initialize.c + receive_filters.c + WaitForPacket.c + transmit.c + mcast_ip_to_mac.c + shutdown.c + reset.c + statistics.c + stop.c + snp.c + start.c + get_status.c + nvdata.c + snp.h + receive.c + + + + + + + + gEfiNetworkInterfaceIdentifierProtocolGuid + + + gEfiDevicePathProtocolGuid + + + gEfiSimpleNetworkProtocolGuid + + + gEfiPciIoProtocolGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + InitializeSnpNiiDriver + + + \ No newline at end of file diff --git a/MdeModulePkg/Universal/Network/SnpDxe/WaitForPacket.c b/MdeModulePkg/Universal/Network/SnpDxe/WaitForPacket.c new file mode 100644 index 0000000000..57d82ea160 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/WaitForPacket.c @@ -0,0 +1,97 @@ +/** @file +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + WaitForPacket.c + +Abstract: + Event handler to check for available packet. + + +**/ + +#include "Snp.h" + + +/** + + + +**/ +VOID +EFIAPI +SnpWaitForPacketNotify ( + EFI_EVENT Event, + VOID *SnpPtr + ) +{ + PXE_DB_GET_STATUS PxeDbGetStatus; + + // + // Do nothing if either parameter is a NULL pointer. + // + if (Event == NULL || SnpPtr == NULL) { + return ; + } + // + // Do nothing if the SNP interface is not initialized. + // + switch (((SNP_DRIVER *) SnpPtr)->mode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + case EfiSimpleNetworkStarted: + default: + return ; + } + // + // Fill in CDB for UNDI GetStatus(). + // + ((SNP_DRIVER *) SnpPtr)->cdb.OpCode = PXE_OPCODE_GET_STATUS; + ((SNP_DRIVER *) SnpPtr)->cdb.OpFlags = 0; + ((SNP_DRIVER *) SnpPtr)->cdb.CPBsize = PXE_CPBSIZE_NOT_USED; + ((SNP_DRIVER *) SnpPtr)->cdb.CPBaddr = PXE_CPBADDR_NOT_USED; + ((SNP_DRIVER *) SnpPtr)->cdb.DBsize = sizeof (UINT32) * 2; + ((SNP_DRIVER *) SnpPtr)->cdb.DBaddr = (UINT64)(UINTN) (((SNP_DRIVER *) SnpPtr)->db); + ((SNP_DRIVER *) SnpPtr)->cdb.StatCode = PXE_STATCODE_INITIALIZE; + ((SNP_DRIVER *) SnpPtr)->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + ((SNP_DRIVER *) SnpPtr)->cdb.IFnum = ((SNP_DRIVER *) SnpPtr)->if_num; + ((SNP_DRIVER *) SnpPtr)->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + // + // Clear contents of DB buffer. + // + ZeroMem (((SNP_DRIVER *) SnpPtr)->db, sizeof (UINT32) * 2); + + // + // Issue UNDI command and check result. + // + (*((SNP_DRIVER *) SnpPtr)->issue_undi32_command) ((UINT64)(UINTN) &((SNP_DRIVER *) SnpPtr)->cdb); + + if (((SNP_DRIVER *) SnpPtr)->cdb.StatCode != EFI_SUCCESS) { + return ; + } + // + // We might have a packet. Check the receive length and signal + // the event if the length is not zero. + // + CopyMem ( + &PxeDbGetStatus, + ((SNP_DRIVER *) SnpPtr)->db, + sizeof (UINT32) * 2 + ); + + if (PxeDbGetStatus.RxFrameLen != 0) { + gBS->SignalEvent (Event); + } +} + +/* eof - WaitForPacket.c */ diff --git a/MdeModulePkg/Universal/Network/SnpDxe/callback.c b/MdeModulePkg/Universal/Network/SnpDxe/callback.c new file mode 100644 index 0000000000..c246874917 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/callback.c @@ -0,0 +1,611 @@ +/*++ +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + callback.c + +Abstract: + This file contains two sets of callback routines for undi3.0 and undi3.1. + the callback routines for Undi3.1 have an extra parameter UniqueId which + stores the interface context for the NIC that snp is trying to talk.. + +--*/ + + +#include "Snp.h" + +// +// Global variables +// these 2 global variables are used only for 3.0 undi. we could not place +// them in the snp structure because we will not know which snp structure +// in the callback context! +// +STATIC BOOLEAN mInitializeLock = TRUE; +STATIC EFI_LOCK mLock; + +// +// End Global variables +// +extern EFI_PCI_IO_PROTOCOL *mPciIoFncs; + +VOID +snp_undi32_callback_v2p_30 ( + IN UINT64 CpuAddr, + IN OUT UINT64 DeviceAddrPtr + ) +/*++ + +Routine Description: + This is a callback routine supplied to UNDI at undi_start time. + UNDI call this routine with a virtual or CPU address that SNP provided + to convert it to a physical or device address. Since EFI uses the identical + mapping, this routine returns the physical address same as the virtual address + for most of the addresses. an address above 4GB cannot generally be used as a + device address, it needs to be mapped to a lower physical address. This routine + does not call the map routine itself, but it assumes that the mapping was done + at the time of providing the address to UNDI. This routine just looks up the + address in a map table (which is the v2p structure chain) + +Arguments: + CpuAddr - virtual address of a buffer + DeviceAddrPtr - pointer to the physical address + +Returns: + void - The DeviceAddrPtr will contain 0 in case of any error + +--*/ +{ + struct s_v2p *v2p; + // + // Do nothing if virtual address is zero or physical pointer is NULL. + // No need to map if the virtual address is within 4GB limit since + // EFI uses identical mapping + // + if ((CpuAddr == 0) || (DeviceAddrPtr == 0)) { + DEBUG ((EFI_D_ERROR, "\nv2p: Null virtual address or physical pointer.\n")); + return ; + } + + if (CpuAddr < FOUR_GIGABYTES) { + *(UINT64 *) (UINTN) DeviceAddrPtr = CpuAddr; + return ; + } + // + // SNP creates a vaddr tp paddr mapping at the time of calling undi with any + // big address, this callback routine just looks up in the v2p list and + // returns the physical address for any given virtual address. + // + if (find_v2p (&v2p, (VOID *) (UINTN) CpuAddr) != EFI_SUCCESS) { + *(UINT64 *) (UINTN) DeviceAddrPtr = CpuAddr; + } else { + *(UINT64 *) (UINTN) DeviceAddrPtr = v2p->paddr; + } +} + +VOID +snp_undi32_callback_block_30 ( + IN UINT32 Enable + ) +/*++ + +Routine Description: + This is a callback routine supplied to UNDI at undi_start time. + UNDI call this routine when it wants to have exclusive access to a critical + section of the code/data + +Arguments: + Enable - non-zero indicates acquire + zero indicates release + +Returns: + void +--*/ +{ + // + // tcpip was calling snp at tpl_notify and if we acquire a lock that was + // created at a lower level (TPL_CALLBACK) it gives an assert! + // + if (mInitializeLock) { + EfiInitializeLock (&mLock, TPL_NOTIFY); + mInitializeLock = FALSE; + } + + if (Enable != 0) { + EfiAcquireLock (&mLock); + } else { + EfiReleaseLock (&mLock); + } +} + +VOID +snp_undi32_callback_delay_30 ( + IN UINT64 MicroSeconds + ) +/*++ + +Routine Description: + This is a callback routine supplied to UNDI at undi_start time. + UNDI call this routine with the number of micro seconds when it wants to + pause. + +Arguments: + MicroSeconds - number of micro seconds to pause, ususlly multiple of 10 + +Returns: + void +--*/ +{ + if (MicroSeconds != 0) { + gBS->Stall ((UINTN) MicroSeconds); + } +} + +VOID +snp_undi32_callback_memio_30 ( + IN UINT8 ReadOrWrite, + IN UINT8 NumBytes, + IN UINT64 Address, + IN OUT UINT64 BufferAddr + ) +/*++ + +Routine Description: + This is a callback routine supplied to UNDI at undi_start time. + This is the IO routine for UNDI. This is not currently being used by UNDI3.0 + because Undi3.0 uses io/mem offsets relative to the beginning of the device + io/mem address and so it needs to use the PCI_IO_FUNCTION that abstracts the + start of the device's io/mem addresses. Since SNP cannot retrive the context + of the undi3.0 interface it cannot use the PCI_IO_FUNCTION that specific for + that NIC and uses one global IO functions structure, this does not work. + This however works fine for EFI1.0 Undis because they use absolute addresses + for io/mem access. + +Arguments: + ReadOrWrite - indicates read or write, IO or Memory + NumBytes - number of bytes to read or write + Address - IO or memory address to read from or write to + BufferAddr - memory location to read into or that contains the bytes + to write + +Returns: + +--*/ +{ + EFI_PCI_IO_PROTOCOL_WIDTH Width; + + switch (NumBytes) { + case 2: + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 1; + break; + + case 4: + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 2; + break; + + case 8: + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 3; + break; + + default: + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 0; + } + + switch (ReadOrWrite) { + case PXE_IO_READ: + mPciIoFncs->Io.Read ( + mPciIoFncs, + Width, + 1, // BAR 1, IO base address + Address, + 1, // count + (VOID *) (UINTN) BufferAddr + ); + break; + + case PXE_IO_WRITE: + mPciIoFncs->Io.Write ( + mPciIoFncs, + Width, + 1, // BAR 1, IO base address + Address, + 1, // count + (VOID *) (UINTN) BufferAddr + ); + break; + + case PXE_MEM_READ: + mPciIoFncs->Mem.Read ( + mPciIoFncs, + Width, + 0, // BAR 0, Memory base address + Address, + 1, // count + (VOID *) (UINTN) BufferAddr + ); + break; + + case PXE_MEM_WRITE: + mPciIoFncs->Mem.Write ( + mPciIoFncs, + Width, + 0, // BAR 0, Memory base address + Address, + 1, // count + (VOID *) (UINTN) BufferAddr + ); + break; + } + + return ; +} +// +// New callbacks for 3.1: +// there won't be a virtual2physical callback for UNDI 3.1 because undi3.1 uses +// the MemMap call to map the required address by itself! +// +VOID +snp_undi32_callback_block ( + IN UINT64 UniqueId, + IN UINT32 Enable + ) +/*++ + +Routine Description: + This is a callback routine supplied to UNDI3.1 at undi_start time. + UNDI call this routine when it wants to have exclusive access to a critical + section of the code/data + +Arguments: + UniqueId - This was supplied to UNDI at Undi_Start, SNP uses this to store + Undi interface context (Undi does not read or write this variable) + Enable - non-zero indicates acquire + zero indicates release + +Returns: + void + +--*/ +{ + SNP_DRIVER *snp; + + snp = (SNP_DRIVER *) (UINTN) UniqueId; + // + // tcpip was calling snp at tpl_notify and when we acquire a lock that was + // created at a lower level (TPL_CALLBACK) it gives an assert! + // + if (Enable != 0) { + EfiAcquireLock (&snp->lock); + } else { + EfiReleaseLock (&snp->lock); + } +} + +VOID +snp_undi32_callback_delay ( + IN UINT64 UniqueId, + IN UINT64 MicroSeconds + ) +/*++ + +Routine Description: + This is a callback routine supplied to UNDI at undi_start time. + UNDI call this routine with the number of micro seconds when it wants to + pause. + +Arguments: + MicroSeconds - number of micro seconds to pause, ususlly multiple of 10 + +Returns: + void +--*/ +{ + if (MicroSeconds != 0) { + gBS->Stall ((UINTN) MicroSeconds); + } +} + +/* + * IO routine for UNDI start CPB. + */ +VOID +snp_undi32_callback_memio ( + UINT64 UniqueId, + UINT8 ReadOrWrite, + UINT8 NumBytes, + UINT64 Address, + UINT64 BufferAddr + ) +/*++ + +Routine Description: + This is a callback routine supplied to UNDI at undi_start time. + This is the IO routine for UNDI3.1. + +Arguments: + ReadOrWrite - indicates read or write, IO or Memory + NumBytes - number of bytes to read or write + Address - IO or memory address to read from or write to + BufferAddr - memory location to read into or that contains the bytes + to write + +Returns: + +--*/ +{ + SNP_DRIVER *snp; + EFI_PCI_IO_PROTOCOL_WIDTH Width; + + snp = (SNP_DRIVER *) (UINTN) UniqueId; + + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 0; + switch (NumBytes) { + case 2: + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 1; + break; + + case 4: + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 2; + break; + + case 8: + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 3; + break; + } + + switch (ReadOrWrite) { + case PXE_IO_READ: + snp->IoFncs->Io.Read ( + snp->IoFncs, + Width, + snp->IoBarIndex, // BAR 1 (for 32bit regs), IO base address + Address, + 1, // count + (VOID *) (UINTN) BufferAddr + ); + break; + + case PXE_IO_WRITE: + snp->IoFncs->Io.Write ( + snp->IoFncs, + Width, + snp->IoBarIndex, // BAR 1 (for 32bit regs), IO base address + Address, + 1, // count + (VOID *) (UINTN) BufferAddr + ); + break; + + case PXE_MEM_READ: + snp->IoFncs->Mem.Read ( + snp->IoFncs, + Width, + snp->MemoryBarIndex, // BAR 0, Memory base address + Address, + 1, // count + (VOID *) (UINTN) BufferAddr + ); + break; + + case PXE_MEM_WRITE: + snp->IoFncs->Mem.Write ( + snp->IoFncs, + Width, + snp->MemoryBarIndex, // BAR 0, Memory base address + Address, + 1, // count + (VOID *) (UINTN) BufferAddr + ); + break; + } + + return ; +} + +VOID +snp_undi32_callback_map ( + IN UINT64 UniqueId, + IN UINT64 CpuAddr, + IN UINT32 NumBytes, + IN UINT32 Direction, + IN OUT UINT64 DeviceAddrPtr + ) +/*++ + +Routine Description: + This is a callback routine supplied to UNDI at undi_start time. + UNDI call this routine when it has to map a CPU address to a device + address. + +Arguments: + UniqueId - This was supplied to UNDI at Undi_Start, SNP uses this to store + Undi interface context (Undi does not read or write this variable) + CpuAddr - Virtual address to be mapped! + NumBytes - size of memory to be mapped + Direction - direction of data flow for this memory's usage: + cpu->device, device->cpu or both ways + DeviceAddrPtr - pointer to return the mapped device address + +Returns: + None + +--*/ +{ + EFI_PHYSICAL_ADDRESS *DevAddrPtr; + EFI_PCI_IO_PROTOCOL_OPERATION DirectionFlag; + UINTN BuffSize; + SNP_DRIVER *snp; + UINTN Index; + EFI_STATUS Status; + + BuffSize = (UINTN) NumBytes; + snp = (SNP_DRIVER *) (UINTN) UniqueId; + DevAddrPtr = (EFI_PHYSICAL_ADDRESS *) (UINTN) DeviceAddrPtr; + + if (CpuAddr == 0) { + *DevAddrPtr = 0; + return ; + } + + switch (Direction) { + case TO_AND_FROM_DEVICE: + DirectionFlag = EfiPciIoOperationBusMasterCommonBuffer; + break; + + case FROM_DEVICE: + DirectionFlag = EfiPciIoOperationBusMasterWrite; + break; + + case TO_DEVICE: + DirectionFlag = EfiPciIoOperationBusMasterRead; + break; + + default: + *DevAddrPtr = 0; + // + // any non zero indicates error! + // + return ; + } + // + // find an unused map_list entry + // + for (Index = 0; Index < MAX_MAP_LENGTH; Index++) { + if (snp->map_list[Index].virt == 0) { + break; + } + } + + if (Index >= MAX_MAP_LENGTH) { + DEBUG ((EFI_D_INFO, "SNP maplist is FULL\n")); + *DevAddrPtr = 0; + return ; + } + + snp->map_list[Index].virt = (EFI_PHYSICAL_ADDRESS) CpuAddr; + + Status = snp->IoFncs->Map ( + snp->IoFncs, + DirectionFlag, + (VOID *) (UINTN) CpuAddr, + &BuffSize, + DevAddrPtr, + &(snp->map_list[Index].map_cookie) + ); + if (Status != EFI_SUCCESS) { + *DevAddrPtr = 0; + snp->map_list[Index].virt = 0; + } + + return ; +} + +VOID +snp_undi32_callback_unmap ( + IN UINT64 UniqueId, + IN UINT64 CpuAddr, + IN UINT32 NumBytes, + IN UINT32 Direction, + IN UINT64 DeviceAddr + ) +/*++ + +Routine Description: + This is a callback routine supplied to UNDI at undi_start time. + UNDI call this routine when it wants to unmap an address that was previously + mapped using map callback + +Arguments: + UniqueId - This was supplied to UNDI at Undi_Start, SNP uses this to store + Undi interface context (Undi does not read or write this variable) + CpuAddr - Virtual address that was mapped! + NumBytes - size of memory mapped + Direction- direction of data flow for this memory's usage: + cpu->device, device->cpu or both ways + DeviceAddr - the mapped device address + +Returns: + +--*/ +{ + SNP_DRIVER *snp; + UINT16 Index; + + snp = (SNP_DRIVER *) (UINTN) UniqueId; + + for (Index = 0; Index < MAX_MAP_LENGTH; Index++) { + if (snp->map_list[Index].virt == CpuAddr) { + break; + } + } + + if (Index >= MAX_MAP_LENGTH) + { + DEBUG ((EFI_D_ERROR, "SNP could not find a mapping, failed to unmap.\n")); + return ; + } + + snp->IoFncs->Unmap (snp->IoFncs, snp->map_list[Index].map_cookie); + snp->map_list[Index].virt = 0; + snp->map_list[Index].map_cookie = NULL; + return ; +} + +VOID +snp_undi32_callback_sync ( + UINT64 UniqueId, + UINT64 CpuAddr, + UINT32 NumBytes, + UINT32 Direction, + UINT64 DeviceAddr + ) +/*++ + +Routine Description: + This is a callback routine supplied to UNDI at undi_start time. + UNDI call this routine when it wants synchronize the virtual buffer contents + with the mapped buffer contents. The virtual and mapped buffers need not + correspond to the same physical memory (especially if the virtual address is + > 4GB). Depending on the direction for which the buffer is mapped, undi will + need to synchronize their contents whenever it writes to/reads from the buffer + using either the cpu address or the device address. + + EFI does not provide a sync call, since virt=physical, we sould just do + the synchronization ourself here! + +Arguments: + UniqueId - This was supplied to UNDI at Undi_Start, SNP uses this to store + Undi interface context (Undi does not read or write this variable) + CpuAddr - Virtual address that was mapped! + NumBytes - size of memory mapped + Direction- direction of data flow for this memory's usage: + cpu->device, device->cpu or both ways + DeviceAddr - the mapped device address + +Returns: + +--*/ +{ + if ((CpuAddr == 0) || (DeviceAddr == 0) || (NumBytes == 0)) { + return ; + + } + + switch (Direction) { + case FROM_DEVICE: + CopyMem ((UINT8 *) (UINTN) CpuAddr, (UINT8 *) (UINTN) DeviceAddr, NumBytes); + break; + + case TO_DEVICE: + CopyMem ((UINT8 *) (UINTN) DeviceAddr, (UINT8 *) (UINTN) CpuAddr, NumBytes); + break; + } + + return ; +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/get_status.c b/MdeModulePkg/Universal/Network/SnpDxe/get_status.c new file mode 100644 index 0000000000..0c1cd8a68e --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/get_status.c @@ -0,0 +1,195 @@ +/** @file +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + get_status.c + +Abstract: + +Revision history: + 2000-Feb-03 M(f)J Genesis. + +**/ + +#include "Snp.h" + +STATIC +/** + this routine calls undi to get the status of the interrupts, get the list of + transmit buffers that completed transmitting! + + @param snp pointer to snp driver structure + @param InterruptStatusPtr a non null pointer gets the interrupt status + @param TransmitBufferListPtrs a non null ointer gets the list of pointers of + previously transmitted buffers whose + transmission was completed asynchrnously. + + +**/ +EFI_STATUS +pxe_getstatus ( + SNP_DRIVER *snp, + UINT32 *InterruptStatusPtr, + VOID **TransmitBufferListPtr + ) +{ + PXE_DB_GET_STATUS *db; + UINT16 InterruptFlags; + UINT64 TempData; + + db = snp->db; + snp->cdb.OpCode = PXE_OPCODE_GET_STATUS; + + snp->cdb.OpFlags = 0; + + if (TransmitBufferListPtr != NULL) { + snp->cdb.OpFlags |= PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS; + } + + if (InterruptStatusPtr != NULL) { + snp->cdb.OpFlags |= PXE_OPFLAGS_GET_INTERRUPT_STATUS; + } + + snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED; + snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED; + + // + // size DB for return of one buffer + // + snp->cdb.DBsize = (UINT16) (((UINT16) (sizeof (PXE_DB_GET_STATUS)) - (UINT16) (sizeof db->TxBuffer)) + (UINT16) (sizeof db->TxBuffer[0])); + + snp->cdb.DBaddr = (UINT64)(UINTN) db; + + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + // + // Issue UNDI command and check result. + // + DEBUG ((EFI_D_NET, "\nsnp->undi.get_status() ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + if (snp->cdb.StatCode != EFI_SUCCESS) { + DEBUG ( + (EFI_D_NET, + "\nsnp->undi.get_status() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatFlags) + ); + + return EFI_DEVICE_ERROR; + } + // + // report the values back.. + // + if (InterruptStatusPtr != NULL) { + InterruptFlags = (UINT16) (snp->cdb.StatFlags & PXE_STATFLAGS_GET_STATUS_INTERRUPT_MASK); + + *InterruptStatusPtr = 0; + + if (InterruptFlags & PXE_STATFLAGS_GET_STATUS_RECEIVE) { + *InterruptStatusPtr |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; + } + + if (InterruptFlags & PXE_STATFLAGS_GET_STATUS_TRANSMIT) { + *InterruptStatusPtr |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; + } + + if (InterruptFlags & PXE_STATFLAGS_GET_STATUS_COMMAND) { + *InterruptStatusPtr |= EFI_SIMPLE_NETWORK_COMMAND_INTERRUPT; + } + + if (InterruptFlags & PXE_STATFLAGS_GET_STATUS_SOFTWARE) { + *InterruptStatusPtr |= EFI_SIMPLE_NETWORK_COMMAND_INTERRUPT; + } + + } + + if (TransmitBufferListPtr != NULL) { + *TransmitBufferListPtr = + ( + (snp->cdb.StatFlags & PXE_STATFLAGS_GET_STATUS_NO_TXBUFS_WRITTEN) || + (snp->cdb.StatFlags & PXE_STATFLAGS_GET_STATUS_TXBUF_QUEUE_EMPTY) + ) ? 0 : (VOID *) (UINTN) db->TxBuffer[0]; + + TempData = (UINT64) (UINTN) (*TransmitBufferListPtr); + if (snp->IsOldUndi && (TempData >= FOUR_GIGABYTES)) { + del_v2p ((VOID *) (UINTN) (db->TxBuffer[0])); + } + } + + return EFI_SUCCESS; +} + + +/** + This is the SNP interface routine for getting the status + This routine basically retrieves snp structure, checks the SNP state and + calls the pxe_getstatus routine to actually get the undi status + + @param this context pointer + @param InterruptStatusPtr a non null pointer gets the interrupt status + @param TransmitBufferListPtrs a non null ointer gets the list of pointers of + previously transmitted buffers whose + transmission was completed asynchrnously. + + +**/ +EFI_STATUS +EFIAPI +snp_undi32_get_status ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * this, + OUT UINT32 *InterruptStatusPtr OPTIONAL, + OUT VOID **TransmitBufferListPtr OPTIONAL + ) +{ + SNP_DRIVER *snp; + EFI_TPL OldTpl; + EFI_STATUS Status; + + if (this == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (InterruptStatusPtr == NULL && TransmitBufferListPtr == NULL) { + return EFI_INVALID_PARAMETER; + } + + snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (snp == NULL) { + return EFI_DEVICE_ERROR; + } + + switch (snp->mode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto ON_EXIT; + + default: + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Status = pxe_getstatus (snp, InterruptStatusPtr, TransmitBufferListPtr); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/initialize.c b/MdeModulePkg/Universal/Network/SnpDxe/initialize.c new file mode 100644 index 0000000000..69154fc0c7 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/initialize.c @@ -0,0 +1,245 @@ +/** @file +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + initialize.c + +Abstract: + +Revision history: + 2000-Feb-09 M(f)J Genesis. + +**/ + + +#include "Snp.h" + +VOID +EFIAPI +SnpWaitForPacketNotify ( + IN EFI_EVENT Event, + IN VOID *SnpPtr + ); + + +/** + this routine calls undi to initialize the interface. + + @param snp pointer to snp driver structure + @param CableDetectFlag Do/don't detect the cable (depending on what undi + supports) + + +**/ +EFI_STATUS +pxe_init ( + SNP_DRIVER *snp, + UINT16 CableDetectFlag + ) +{ + PXE_CPB_INITIALIZE *cpb; + VOID *addr; + EFI_STATUS Status; + + cpb = snp->cpb; + if (snp->tx_rx_bufsize != 0) { + Status = snp->IoFncs->AllocateBuffer ( + snp->IoFncs, + AllocateAnyPages, + EfiBootServicesData, + SNP_MEM_PAGES (snp->tx_rx_bufsize), + &addr, + 0 + ); + + if (Status != EFI_SUCCESS) { + DEBUG ( + (EFI_D_ERROR, + "\nsnp->pxe_init() AllocateBuffer %xh (%r)\n", + Status, + Status) + ); + + return Status; + } + + ASSERT (addr); + + snp->tx_rx_buffer = addr; + } + + cpb->MemoryAddr = (UINT64)(UINTN) snp->tx_rx_buffer; + + cpb->MemoryLength = snp->tx_rx_bufsize; + + // + // let UNDI decide/detect these values + // + cpb->LinkSpeed = 0; + cpb->TxBufCnt = 0; + cpb->TxBufSize = 0; + cpb->RxBufCnt = 0; + cpb->RxBufSize = 0; + + cpb->DuplexMode = PXE_DUPLEX_DEFAULT; + + cpb->LoopBackMode = LOOPBACK_NORMAL; + + snp->cdb.OpCode = PXE_OPCODE_INITIALIZE; + snp->cdb.OpFlags = CableDetectFlag; + + snp->cdb.CPBsize = sizeof (PXE_CPB_INITIALIZE); + snp->cdb.DBsize = sizeof (PXE_DB_INITIALIZE); + + snp->cdb.CPBaddr = (UINT64)(UINTN) snp->cpb; + snp->cdb.DBaddr = (UINT64)(UINTN) snp->db; + + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + DEBUG ((EFI_D_NET, "\nsnp->undi.initialize() ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + if (snp->cdb.StatCode == PXE_STATCODE_SUCCESS) { + snp->mode.State = EfiSimpleNetworkInitialized; + + Status = EFI_SUCCESS; + } else { + DEBUG ( + (EFI_D_WARN, + "\nsnp->undi.initialize() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + if (snp->tx_rx_buffer != NULL) { + snp->IoFncs->FreeBuffer ( + snp->IoFncs, + SNP_MEM_PAGES (snp->tx_rx_bufsize), + (VOID *) snp->tx_rx_buffer + ); + } + + snp->tx_rx_buffer = NULL; + + Status = EFI_DEVICE_ERROR; + } + + return Status; +} + + +/** + This is the SNP interface routine for initializing the interface + This routine basically retrieves snp structure, checks the SNP state and + calls the pxe_initialize routine to actually do the undi initialization + + @param this context pointer + @param extra_rx_buffer_size optional parameter, indicates extra space for + rx_buffers + @param extra_tx_buffer_size optional parameter, indicates extra space for + tx_buffers + + +**/ +EFI_STATUS +EFIAPI +snp_undi32_initialize ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *this, + IN UINTN extra_rx_buffer_size OPTIONAL, + IN UINTN extra_tx_buffer_size OPTIONAL + ) +{ + EFI_STATUS EfiStatus; + SNP_DRIVER *snp; + EFI_TPL OldTpl; + + // + // + // + if (this == NULL) { + return EFI_INVALID_PARAMETER; + } + + snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (snp == NULL) { + EfiStatus = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + switch (snp->mode.State) { + case EfiSimpleNetworkStarted: + break; + + case EfiSimpleNetworkStopped: + EfiStatus = EFI_NOT_STARTED; + goto ON_EXIT; + + default: + EfiStatus = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + EfiStatus = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + &SnpWaitForPacketNotify, + snp, + &snp->snp.WaitForPacket + ); + + if (EFI_ERROR (EfiStatus)) { + snp->snp.WaitForPacket = NULL; + EfiStatus = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + // + // + // + snp->mode.MCastFilterCount = 0; + snp->mode.ReceiveFilterSetting = 0; + ZeroMem (snp->mode.MCastFilter, sizeof snp->mode.MCastFilter); + CopyMem ( + &snp->mode.CurrentAddress, + &snp->mode.PermanentAddress, + sizeof (EFI_MAC_ADDRESS) + ); + + // + // Compute tx/rx buffer sizes based on UNDI init info and parameters. + // + snp->tx_rx_bufsize = (UINT32) (snp->init_info.MemoryRequired + extra_rx_buffer_size + extra_tx_buffer_size); + + if (snp->mode.MediaPresentSupported) { + if (pxe_init (snp, PXE_OPFLAGS_INITIALIZE_DETECT_CABLE) == EFI_SUCCESS) { + snp->mode.MediaPresent = TRUE; + goto ON_EXIT; + } + } + + snp->mode.MediaPresent = FALSE; + + EfiStatus = pxe_init (snp, PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE); + + if (EFI_ERROR (EfiStatus)) { + gBS->CloseEvent (snp->snp.WaitForPacket); + } + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + + return EfiStatus; +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/mcast_ip_to_mac.c b/MdeModulePkg/Universal/Network/SnpDxe/mcast_ip_to_mac.c new file mode 100644 index 0000000000..91b5e02fc1 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/mcast_ip_to_mac.c @@ -0,0 +1,165 @@ +/** @file +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + mcast_ip_to_mac.c + +Abstract: + +Revision history: + 2000-Feb-17 M(f)J Genesis. + +**/ + +#include "Snp.h" + +/** + this routine calls undi to convert an multicast IP address to a MAC address + + @param snp pointer to snp driver structure + @param IPv6 flag to indicate if this is an ipv6 address + @param IP multicast IP address + @param MAC pointer to hold the return MAC address + + +**/ +STATIC +EFI_STATUS +pxe_ip2mac ( + IN SNP_DRIVER *snp, + IN BOOLEAN IPv6, + IN EFI_IP_ADDRESS *IP, + IN OUT EFI_MAC_ADDRESS *MAC + ) +{ + PXE_CPB_MCAST_IP_TO_MAC *cpb; + PXE_DB_MCAST_IP_TO_MAC *db; + + cpb = snp->cpb; + db = snp->db; + snp->cdb.OpCode = PXE_OPCODE_MCAST_IP_TO_MAC; + snp->cdb.OpFlags = (UINT16) (IPv6 ? PXE_OPFLAGS_MCAST_IPV6_TO_MAC : PXE_OPFLAGS_MCAST_IPV4_TO_MAC); + snp->cdb.CPBsize = sizeof (PXE_CPB_MCAST_IP_TO_MAC); + snp->cdb.DBsize = sizeof (PXE_DB_MCAST_IP_TO_MAC); + + snp->cdb.CPBaddr = (UINT64)(UINTN) cpb; + snp->cdb.DBaddr = (UINT64)(UINTN) db; + + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + CopyMem (&cpb->IP, IP, sizeof (PXE_IP_ADDR)); + + // + // Issue UNDI command and check result. + // + DEBUG ((EFI_D_NET, "\nsnp->undi.mcast_ip_to_mac() ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + switch (snp->cdb.StatCode) { + case PXE_STATCODE_SUCCESS: + break; + + case PXE_STATCODE_INVALID_CPB: + return EFI_INVALID_PARAMETER; + + case PXE_STATCODE_UNSUPPORTED: + DEBUG ( + (EFI_D_NET, + "\nsnp->undi.mcast_ip_to_mac() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + return EFI_UNSUPPORTED; + + default: + // + // UNDI command failed. Return EFI_DEVICE_ERROR + // to caller. + // + DEBUG ( + (EFI_D_NET, + "\nsnp->undi.mcast_ip_to_mac() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + return EFI_DEVICE_ERROR; + } + + CopyMem (MAC, &db->MAC, sizeof (PXE_MAC_ADDR)); + return EFI_SUCCESS; +} + + +/** + This is the SNP interface routine for converting a multicast IP address to + a MAC address. + This routine basically retrieves snp structure, checks the SNP state and + calls the pxe_ip2mac routine to actually do the conversion + + @param this context pointer + @param IPv6 flag to indicate if this is an ipv6 address + @param IP multicast IP address + @param MAC pointer to hold the return MAC address + + +**/ +EFI_STATUS +EFIAPI +snp_undi32_mcast_ip_to_mac ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *this, + IN BOOLEAN IPv6, + IN EFI_IP_ADDRESS *IP, + OUT EFI_MAC_ADDRESS *MAC + ) +{ + SNP_DRIVER *snp; + EFI_TPL OldTpl; + EFI_STATUS Status; + + // + // Get pointer to SNP driver instance for *this. + // + if (this == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (IP == NULL || MAC == NULL) { + return EFI_INVALID_PARAMETER; + } + + snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + switch (snp->mode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto ON_EXIT; + + default: + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Status = pxe_ip2mac (snp, IPv6, IP, MAC); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/nvdata.c b/MdeModulePkg/Universal/Network/SnpDxe/nvdata.c new file mode 100644 index 0000000000..531a4d6929 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/nvdata.c @@ -0,0 +1,185 @@ +/** @file +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + nvdata.c + +Abstract: + +Revision history: + 2000-Feb-03 M(f)J Genesis. + +**/ + +#include "snp.h" + + +/** + This routine calls Undi to read the desired number of eeprom bytes. + + @param snp pointer to the snp driver structure + @param RegOffset eeprom register value relative to the base address + @param NumBytes number of bytes to read + @param BufferPtr pointer where to read into + + +**/ +STATIC +EFI_STATUS +pxe_nvdata_read ( + IN SNP_DRIVER *snp, + IN UINTN RegOffset, + IN UINTN NumBytes, + IN OUT VOID *BufferPtr + ) +{ + PXE_DB_NVDATA *db; + + db = snp->db; + snp->cdb.OpCode = PXE_OPCODE_NVDATA; + + snp->cdb.OpFlags = PXE_OPFLAGS_NVDATA_READ; + + snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED; + snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED; + + snp->cdb.DBsize = sizeof (PXE_DB_NVDATA); + snp->cdb.DBaddr = (UINT64)(UINTN) db; + + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + // + // Issue UNDI command and check result. + // + DEBUG ((EFI_D_NET, "\nsnp->undi.nvdata () ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + switch (snp->cdb.StatCode) { + case PXE_STATCODE_SUCCESS: + break; + + case PXE_STATCODE_UNSUPPORTED: + DEBUG ( + (EFI_D_NET, + "\nsnp->undi.nvdata() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + return EFI_UNSUPPORTED; + + default: + DEBUG ( + (EFI_D_NET, + "\nsnp->undi.nvdata() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + return EFI_DEVICE_ERROR; + } + + CopyMem (BufferPtr, db->Data.Byte + RegOffset, NumBytes); + + return EFI_SUCCESS; +} + + +/** + This is an interface call provided by SNP. + It does the basic checking on the input parameters and retrieves snp structure + and then calls the read_nvdata() call which does the actual reading + + @param this context pointer + @param ReadOrWrite true for reading and false for writing + @param RegOffset eeprom register relative to the base + @param NumBytes how many bytes to read + @param BufferPtr address of memory to read into + + +**/ +EFI_STATUS +EFIAPI +snp_undi32_nvdata ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *this, + IN BOOLEAN ReadOrWrite, + IN UINTN RegOffset, + IN UINTN NumBytes, + IN OUT VOID *BufferPtr + ) +{ + SNP_DRIVER *snp; + EFI_TPL OldTpl; + EFI_STATUS Status; + + // + // Get pointer to SNP driver instance for *this. + // + if (this == NULL) { + return EFI_INVALID_PARAMETER; + } + + snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Return error if the SNP is not initialized. + // + switch (snp->mode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto ON_EXIT; + + default: + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + // + // Return error if non-volatile memory variables are not valid. + // + if (snp->mode.NvRamSize == 0 || snp->mode.NvRamAccessSize == 0) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + // + // Check for invalid parameter combinations. + // + if ((NumBytes == 0) || + (BufferPtr == NULL) || + (RegOffset >= snp->mode.NvRamSize) || + (RegOffset + NumBytes > snp->mode.NvRamSize) || + (NumBytes % snp->mode.NvRamAccessSize != 0) || + (RegOffset % snp->mode.NvRamAccessSize != 0) + ) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + // + // check the implementation flags of undi if we can write the nvdata! + // + if (!ReadOrWrite) { + Status = EFI_UNSUPPORTED; + } else { + Status = pxe_nvdata_read (snp, RegOffset, NumBytes, BufferPtr); + } + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/receive.c b/MdeModulePkg/Universal/Network/SnpDxe/receive.c new file mode 100644 index 0000000000..64ca2565d9 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/receive.c @@ -0,0 +1,255 @@ +/** @file +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + receive.c + +Abstract: + +Revision history: + 2000-Feb-03 M(f)J Genesis. + +**/ + + +#include "Snp.h" + +/** + this routine calls undi to receive a packet and fills in the data in the + input pointers! + + @param snp pointer to snp driver structure + @param BufferPtr pointer to the memory for the received data + @param BuffSizePtr is a pointer to the length of the buffer on entry and + contains the length of the received data on return + @param HeaderSizePtr pointer to the header portion of the data received. + @param SourceAddrPtr optional parameter, is a pointer to contain the + source ethernet address on return + @param DestinationAddrPtr optional parameter, is a pointer to contain the + destination ethernet address on return + @param ProtocolPtr optional parameter, is a pointer to contain the + protocol type from the ethernet header on return + + +**/ +STATIC +EFI_STATUS +pxe_receive ( + SNP_DRIVER *snp, + VOID *BufferPtr, + UINTN *BuffSizePtr, + UINTN *HeaderSizePtr, + EFI_MAC_ADDRESS *SourceAddrPtr, + EFI_MAC_ADDRESS *DestinationAddrPtr, + UINT16 *ProtocolPtr + ) +{ + PXE_CPB_RECEIVE *cpb; + PXE_DB_RECEIVE *db; + UINTN buf_size; + UINT64 TempData; + + cpb = snp->cpb; + db = snp->db; + buf_size = *BuffSizePtr; + // + // IMPORTANT NOTE: + // In case of the older 3.0 UNDI, if the input buffer address is beyond 4GB, + // DO NOT call the map function on the given buffer, instead use + // a global buffer. The reason is that UNDI3.0 has some unnecessary check of + // making sure that all the addresses (whether or not they will be given + // to the NIC ) supplied to it are below 4GB. It may or may not use + // the mapped address after all (like in case of CPB and DB)! + // Instead of using the global buffer whose address is allocated within the + // 2GB limit if I start mapping the given buffer we lose the data, here is + // why!!! + // if our address is > 4GB, the map call creates another buffer below 2GB and + // copies data to/from the original buffer to the mapped buffer either at + // map time or unmap time depending on the map direction. + // UNDI will not complain since we already mapped the buffer to be + // within the 2GB limit but will not use (I know undi) the mapped address + // since it does not give the user buffers to the NIC's receive unit, + // It just copies the received packet into the user buffer using the virtual + // (CPU) address rather than the mapped (device or physical) address. + // When the UNDI call returns, if we then unmap the buffer, we will lose + // the contents because unmap copies the contents of the mapped buffer into + // the original buffer (since the direction is FROM_DEVICE) !!! + // + // this is not a problem in Undi 3.1 because this undi uses it's map callback + // routine to map a cpu address to device address and it does it only if + // it is giving the address to the device and unmaps it before using the cpu + // address! + // + TempData = (UINT64) (UINTN) BufferPtr; + if (snp->IsOldUndi && (TempData >= FOUR_GIGABYTES)) { + cpb->BufferAddr = (UINT64)(UINTN) snp->receive_buf; + cpb->BufferLen = (UINT32) (snp->init_info.MediaHeaderLen + snp->init_info.FrameDataLen); + } else { + cpb->BufferAddr = (UINT64)(UINTN) BufferPtr; + cpb->BufferLen = (UINT32) *BuffSizePtr; + } + + cpb->reserved = 0; + + snp->cdb.OpCode = PXE_OPCODE_RECEIVE; + snp->cdb.OpFlags = PXE_OPFLAGS_NOT_USED; + + snp->cdb.CPBsize = sizeof (PXE_CPB_RECEIVE); + snp->cdb.CPBaddr = (UINT64)(UINTN) cpb; + + snp->cdb.DBsize = sizeof (PXE_DB_RECEIVE); + snp->cdb.DBaddr = (UINT64)(UINTN) db; + + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + // + // Issue UNDI command and check result. + // + DEBUG ((EFI_D_INFO, "\nsnp->undi.receive () ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + switch (snp->cdb.StatCode) { + case PXE_STATCODE_SUCCESS: + break; + + case PXE_STATCODE_NO_DATA: + DEBUG ( + (EFI_D_INFO, + "\nsnp->undi.receive () %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + return EFI_NOT_READY; + + default: + DEBUG ( + (EFI_D_ERROR, + "\nsnp->undi.receive() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + return EFI_DEVICE_ERROR; + } + + *BuffSizePtr = db->FrameLen; + + if (HeaderSizePtr != NULL) { + *HeaderSizePtr = db->MediaHeaderLen; + } + + if (SourceAddrPtr != NULL) { + CopyMem (SourceAddrPtr, &db->SrcAddr, snp->mode.HwAddressSize); + } + + if (DestinationAddrPtr != NULL) { + CopyMem (DestinationAddrPtr, &db->DestAddr, snp->mode.HwAddressSize); + } + + if (ProtocolPtr != NULL) { + *ProtocolPtr = (UINT16) PXE_SWAP_UINT16 (db->Protocol); /* we need to do the byte swapping */ + } + + TempData = (UINT64) (UINTN) BufferPtr; + if (snp->IsOldUndi && (TempData >= FOUR_GIGABYTES)) { + CopyMem (BufferPtr, snp->receive_buf, snp->init_info.MediaHeaderLen + snp->init_info.FrameDataLen); + } + + return (*BuffSizePtr <= buf_size) ? EFI_SUCCESS : EFI_BUFFER_TOO_SMALL; +} + + +/** + This is the SNP interface routine for receiving network data. + This routine basically retrieves snp structure, checks the SNP state and + calls the pxe_receive routine to actually do the receive! + + @param this context pointer + @param HeaderSizePtr optional parameter and is a pointer to the header + portion of the data received. + @param BuffSizePtr is a pointer to the length of the buffer on entry and + contains the length of the received data on return + @param BufferPtr pointer to the memory for the received data + @param SourceAddrPtr optional parameter, is a pointer to contain the + source ethernet address on return + @param DestinationAddrPtr optional parameter, is a pointer to contain the + destination ethernet address on return + @param ProtocolPtr optional parameter, is a pointer to contain the + protocol type from the ethernet header on return + + +**/ +EFI_STATUS +EFIAPI +snp_undi32_receive ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * this, + OUT UINTN *HeaderSizePtr OPTIONAL, + IN OUT UINTN *BuffSizePtr, + OUT VOID *BufferPtr, + OUT EFI_MAC_ADDRESS * SourceAddrPtr OPTIONAL, + OUT EFI_MAC_ADDRESS * DestinationAddrPtr OPTIONAL, + OUT UINT16 *ProtocolPtr OPTIONAL + ) +{ + SNP_DRIVER *snp; + EFI_TPL OldTpl; + EFI_STATUS Status; + + if (this == NULL) { + return EFI_INVALID_PARAMETER; + } + + snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + switch (snp->mode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto ON_EXIT; + + default: + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + if ((BuffSizePtr == NULL) || (BufferPtr == NULL)) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + if (!snp->mode.ReceiveFilterSetting) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Status = pxe_receive ( + snp, + BufferPtr, + BuffSizePtr, + HeaderSizePtr, + SourceAddrPtr, + DestinationAddrPtr, + ProtocolPtr + ); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/receive_filters.c b/MdeModulePkg/Universal/Network/SnpDxe/receive_filters.c new file mode 100644 index 0000000000..3886d2078d --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/receive_filters.c @@ -0,0 +1,406 @@ +/** @file +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + receive_filters.c + +Abstract: + +Revision history: + 2000-Feb-17 M(f)J Genesis. + +**/ + + + +#include "Snp.h" + +/** + this routine calls undi to enable the receive filters. + + @param snp pointer to snp driver structure + @param EnableFlags bit mask for enabling the receive filters + @param MCastAddressCount multicast address count for a new multicast address + list + @param MCastAddressList list of new multicast addresses + + +**/ +STATIC +EFI_STATUS +pxe_rcvfilter_enable ( + SNP_DRIVER *snp, + UINT32 EnableFlags, + UINTN MCastAddressCount, + EFI_MAC_ADDRESS *MCastAddressList + ) +{ + snp->cdb.OpCode = PXE_OPCODE_RECEIVE_FILTERS; + snp->cdb.OpFlags = PXE_OPFLAGS_RECEIVE_FILTER_ENABLE; + snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED; + snp->cdb.DBsize = PXE_DBSIZE_NOT_USED; + snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED; + snp->cdb.DBaddr = PXE_DBADDR_NOT_USED; + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_UNICAST) != 0) { + snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_UNICAST; + } + + if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST) != 0) { + snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST; + } + + if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) { + snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS; + } + + if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) { + snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST; + } + + if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0) { + snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST; + } + + if (MCastAddressCount != 0) { + snp->cdb.CPBsize = (UINT16) (MCastAddressCount * sizeof (EFI_MAC_ADDRESS)); + snp->cdb.CPBaddr = (UINT64)(UINTN) snp->cpb; + CopyMem (snp->cpb, MCastAddressList, snp->cdb.CPBsize); + } + // + // Issue UNDI command and check result. + // + DEBUG ((EFI_D_NET, "\nsnp->undi.receive_filters() ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + if (snp->cdb.StatCode != EFI_SUCCESS) { + // + // UNDI command failed. Return UNDI status to caller. + // + DEBUG ( + (EFI_D_ERROR, + "\nsnp->undi.receive_filters() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + switch (snp->cdb.StatCode) { + case PXE_STATCODE_INVALID_CDB: + case PXE_STATCODE_INVALID_CPB: + case PXE_STATCODE_INVALID_PARAMETER: + return EFI_INVALID_PARAMETER; + + case PXE_STATCODE_UNSUPPORTED: + return EFI_UNSUPPORTED; + } + + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + this routine calls undi to disable the receive filters. + + @param snp pointer to snp driver structure + @param DisableFlags bit mask for disabling the receive filters + @param ResetMCastList boolean flag to reset/delete the multicast filter list + + +**/ +STATIC +EFI_STATUS +pxe_rcvfilter_disable ( + SNP_DRIVER *snp, + UINT32 DisableFlags, + BOOLEAN ResetMCastList + ) +{ + snp->cdb.OpCode = PXE_OPCODE_RECEIVE_FILTERS; + snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED; + snp->cdb.DBsize = PXE_DBSIZE_NOT_USED; + snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED; + snp->cdb.DBaddr = PXE_DBADDR_NOT_USED; + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + snp->cdb.OpFlags = (UINT16) (DisableFlags ? PXE_OPFLAGS_RECEIVE_FILTER_DISABLE : PXE_OPFLAGS_NOT_USED); + + if (ResetMCastList) { + snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST; + } + + if ((DisableFlags & EFI_SIMPLE_NETWORK_RECEIVE_UNICAST) != 0) { + snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_UNICAST; + } + + if ((DisableFlags & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST) != 0) { + snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST; + } + + if ((DisableFlags & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) { + snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS; + } + + if ((DisableFlags & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) { + snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST; + } + + if ((DisableFlags & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0) { + snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST; + } + // + // Issue UNDI command and check result. + // + DEBUG ((EFI_D_NET, "\nsnp->undi.receive_filters() ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + if (snp->cdb.StatCode != EFI_SUCCESS) { + // + // UNDI command failed. Return UNDI status to caller. + // + DEBUG ( + (EFI_D_ERROR, + "\nsnp->undi.receive_filters() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + this routine calls undi to read the receive filters. + + @param snp pointer to snp driver structure + + +**/ +STATIC +EFI_STATUS +pxe_rcvfilter_read ( + SNP_DRIVER *snp + ) +{ + snp->cdb.OpCode = PXE_OPCODE_RECEIVE_FILTERS; + snp->cdb.OpFlags = PXE_OPFLAGS_RECEIVE_FILTER_READ; + snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED; + snp->cdb.DBsize = (UINT16) (snp->mode.MaxMCastFilterCount * sizeof (EFI_MAC_ADDRESS)); + snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED; + if (snp->cdb.DBsize == 0) { + snp->cdb.DBaddr = (UINT64)(UINTN) NULL; + } else { + snp->cdb.DBaddr = (UINT64)(UINTN) snp->db; + ZeroMem (snp->db, snp->cdb.DBsize); + } + + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + DEBUG ((EFI_D_NET, "\nsnp->undi.receive_filters() ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + if (snp->cdb.StatCode != EFI_SUCCESS) { + // + // UNDI command failed. Return UNDI status to caller. + // + DEBUG ( + (EFI_D_ERROR, + "\nsnp->undi.receive_filters() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + return EFI_DEVICE_ERROR; + } + // + // Convert UNDI32 StatFlags to EFI SNP filter flags. + // + snp->mode.ReceiveFilterSetting = 0; + + if ((snp->cdb.StatFlags & PXE_STATFLAGS_RECEIVE_FILTER_UNICAST) != 0) { + snp->mode.ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_UNICAST; + } + + if ((snp->cdb.StatFlags & PXE_STATFLAGS_RECEIVE_FILTER_BROADCAST) != 0) { + snp->mode.ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST; + } + + if ((snp->cdb.StatFlags & PXE_STATFLAGS_RECEIVE_FILTER_PROMISCUOUS) != 0) { + snp->mode.ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS; + } + + if ((snp->cdb.StatFlags & PXE_STATFLAGS_RECEIVE_FILTER_ALL_MULTICAST) != 0) { + snp->mode.ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + } + + if ((snp->cdb.StatFlags & PXE_STATFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) { + snp->mode.ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST; + } + + CopyMem (snp->mode.MCastFilter, snp->db, snp->cdb.DBsize); + + // + // Count number of active entries in multicast filter list. + // + { + EFI_MAC_ADDRESS ZeroMacAddr; + + SetMem (&ZeroMacAddr, sizeof ZeroMacAddr, 0); + + for (snp->mode.MCastFilterCount = 0; + snp->mode.MCastFilterCount < snp->mode.MaxMCastFilterCount; + snp->mode.MCastFilterCount++ + ) { + if (CompareMem ( + &snp->mode.MCastFilter[snp->mode.MCastFilterCount], + &ZeroMacAddr, + sizeof ZeroMacAddr + ) == 0) { + break; + } + } + } + + return EFI_SUCCESS; +} + + +/** + This is the SNP interface routine for reading/enabling/disabling the + receive filters. + This routine basically retrieves snp structure, checks the SNP state and + checks the parameter validity, calls one of the above routines to actually + do the work + + @param this context pointer + @param EnableFlags bit mask for enabling the receive filters + @param DisableFlags bit mask for disabling the receive filters + @param ResetMCastList boolean flag to reset/delete the multicast filter list + @param MCastAddressCount multicast address count for a new multicast address + list + @param MCastAddressList list of new multicast addresses + + +**/ +EFI_STATUS +EFIAPI +snp_undi32_receive_filters ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * this, + IN UINT32 EnableFlags, + IN UINT32 DisableFlags, + IN BOOLEAN ResetMCastList, + IN UINTN MCastAddressCount OPTIONAL, + IN EFI_MAC_ADDRESS * MCastAddressList OPTIONAL + ) +{ + SNP_DRIVER *snp; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (this == NULL) { + return EFI_INVALID_PARAMETER; + } + + snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + switch (snp->mode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto ON_EXIT; + + default: + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + // + // check if we are asked to enable or disable something that the UNDI + // does not even support! + // + if (((EnableFlags &~snp->mode.ReceiveFilterMask) != 0) || + ((DisableFlags &~snp->mode.ReceiveFilterMask) != 0)) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + if (ResetMCastList) { + + DisableFlags |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST & snp->mode.ReceiveFilterMask; + MCastAddressCount = 0; + MCastAddressList = NULL; + } else { + if (MCastAddressCount != 0) { + if ((MCastAddressCount > snp->mode.MaxMCastFilterCount) || + (MCastAddressList == NULL)) { + + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + } + } + + if (EnableFlags == 0 && DisableFlags == 0 && !ResetMCastList && MCastAddressCount == 0) { + Status = EFI_SUCCESS; + goto ON_EXIT; + } + + if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0 && MCastAddressCount == 0) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + if ((EnableFlags != 0) || (MCastAddressCount != 0)) { + Status = pxe_rcvfilter_enable ( + snp, + EnableFlags, + MCastAddressCount, + MCastAddressList + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + if ((DisableFlags != 0) || ResetMCastList) { + Status = pxe_rcvfilter_disable (snp, DisableFlags, ResetMCastList); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + Status = pxe_rcvfilter_read (snp); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/reset.c b/MdeModulePkg/Universal/Network/SnpDxe/reset.c new file mode 100644 index 0000000000..0a08032fc4 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/reset.c @@ -0,0 +1,128 @@ +/** @file +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + reset.c + +Abstract: + +Revision history: + 2000-Feb-09 M(f)J Genesis. + +**/ + +#include "Snp.h" + + +/** + This routine calls undi to reset the nic. + + @param snp pointer to the snp driver structure + + @return EFI_SUCCESSFUL for a successful completion + @return other for failed calls + +**/ +STATIC +EFI_STATUS +pxe_reset ( + SNP_DRIVER *snp + ) +{ + snp->cdb.OpCode = PXE_OPCODE_RESET; + snp->cdb.OpFlags = PXE_OPFLAGS_NOT_USED; + snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED; + snp->cdb.DBsize = PXE_DBSIZE_NOT_USED; + snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED; + snp->cdb.DBaddr = PXE_DBADDR_NOT_USED; + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + // + // Issue UNDI command and check result. + // + DEBUG ((EFI_D_NET, "\nsnp->undi.reset() ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) { + DEBUG ( + (EFI_D_WARN, + "\nsnp->undi32.reset() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + // + // UNDI could not be reset. Return UNDI error. + // + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + This is the SNP interface routine for resetting the NIC + This routine basically retrieves snp structure, checks the SNP state and + calls the pxe_reset routine to actually do the reset! + + @param this context pointer + @param ExtendedVerification not implemented + + +**/ +EFI_STATUS +EFIAPI +snp_undi32_reset ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *this, + IN BOOLEAN ExtendedVerification + ) +{ + SNP_DRIVER *snp; + EFI_TPL OldTpl; + EFI_STATUS Status; + + // + // Resolve Warning 4 unreferenced parameter problem + // + ExtendedVerification = 0; + + if (this == NULL) { + return EFI_INVALID_PARAMETER; + } + + snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + switch (snp->mode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto ON_EXIT; + + default: + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Status = pxe_reset (snp); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/shutdown.c b/MdeModulePkg/Universal/Network/SnpDxe/shutdown.c new file mode 100644 index 0000000000..8e0ae45503 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/shutdown.c @@ -0,0 +1,148 @@ +/** @file +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + shutdown.c + +Abstract: + +Revision history: + 2000-Feb-14 M(f)J Genesis. + +**/ + +#include "Snp.h" + + +/** + this routine calls undi to shut down the interface. + + @param snp pointer to snp driver structure + + +**/ +EFI_STATUS +pxe_shutdown ( + IN SNP_DRIVER *snp + ) +{ + snp->cdb.OpCode = PXE_OPCODE_SHUTDOWN; + snp->cdb.OpFlags = PXE_OPFLAGS_NOT_USED; + snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED; + snp->cdb.DBsize = PXE_DBSIZE_NOT_USED; + snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED; + snp->cdb.DBaddr = PXE_DBADDR_NOT_USED; + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + // + // Issue UNDI command and check result. + // + DEBUG ((EFI_D_NET, "\nsnp->undi.shutdown() ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) { + // + // UNDI could not be shutdown. Return UNDI error. + // + DEBUG ((EFI_D_WARN, "\nsnp->undi.shutdown() %xh:%xh\n", snp->cdb.StatFlags, snp->cdb.StatCode)); + + return EFI_DEVICE_ERROR; + } + // + // Free allocated memory. + // + if (snp->tx_rx_buffer != NULL) { + snp->IoFncs->FreeBuffer ( + snp->IoFncs, + SNP_MEM_PAGES (snp->tx_rx_bufsize), + (VOID *) snp->tx_rx_buffer + ); + } + + snp->tx_rx_buffer = NULL; + snp->tx_rx_bufsize = 0; + + return EFI_SUCCESS; +} + + +/** + This is the SNP interface routine for shutting down the interface + This routine basically retrieves snp structure, checks the SNP state and + calls the pxe_shutdown routine to actually do the undi shutdown + + @param this context pointer + + +**/ +EFI_STATUS +EFIAPI +snp_undi32_shutdown ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *this + ) +{ + SNP_DRIVER *snp; + EFI_STATUS Status; + EFI_TPL OldTpl; + + // + // + // + if (this == NULL) { + return EFI_INVALID_PARAMETER; + } + + snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // + // + switch (snp->mode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto ON_EXIT; + + default: + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + // + // + // + Status = pxe_shutdown (snp); + + snp->mode.State = EfiSimpleNetworkStarted; + snp->mode.ReceiveFilterSetting = 0; + + snp->mode.MCastFilterCount = 0; + snp->mode.ReceiveFilterSetting = 0; + ZeroMem (snp->mode.MCastFilter, sizeof snp->mode.MCastFilter); + CopyMem ( + &snp->mode.CurrentAddress, + &snp->mode.PermanentAddress, + sizeof (EFI_MAC_ADDRESS) + ); + + gBS->CloseEvent (snp->snp.WaitForPacket); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/snp.c b/MdeModulePkg/Universal/Network/SnpDxe/snp.c new file mode 100644 index 0000000000..460cdfd472 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/snp.c @@ -0,0 +1,1269 @@ +/** @file +Copyright (c) 2004 - 2005, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + snp.c + +Abstract: + + +**/ + +#include "Snp.h" + +EFI_STATUS +pxe_start ( + SNP_DRIVER *snp + ); +EFI_STATUS +pxe_stop ( + SNP_DRIVER *snp + ); +EFI_STATUS +pxe_init ( + SNP_DRIVER *snp, + UINT16 OpFlags + ); +EFI_STATUS +pxe_shutdown ( + SNP_DRIVER *snp + ); +EFI_STATUS +pxe_get_stn_addr ( + SNP_DRIVER *snp + ); + +EFI_STATUS +EFIAPI +InitializeSnpNiiDriver ( + IN EFI_HANDLE image_handle, + IN EFI_SYSTEM_TABLE *system_table + ); + +EFI_STATUS +EFIAPI +SimpleNetworkDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +SimpleNetworkDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +SimpleNetworkDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// Simple Network Protocol Driver Global Variables +// +EFI_DRIVER_BINDING_PROTOCOL mSimpleNetworkDriverBinding = { + SimpleNetworkDriverSupported, + SimpleNetworkDriverStart, + SimpleNetworkDriverStop, + 0xa, + NULL, + NULL +}; + +// +// Module global variables needed to support undi 3.0 interface +// +EFI_PCI_IO_PROTOCOL *mPciIoFncs; +struct s_v2p *_v2p = NULL; // undi3.0 map_list head +// End Global variables +// + +/** + This routine maps the given CPU address to a Device address. It creates a + an entry in the map list with the virtual and physical addresses and the + un map cookie. + + @param v2p pointer to return a map list node pointer. + @param type the direction in which the data flows from the given + virtual address device->cpu or cpu->device or both + ways. + @param vaddr virtual address (or CPU address) to be mapped + @param bsize size of the buffer to be mapped. + + @retval EFI_SUCEESS routine has completed the mapping + @retval other error as indicated. + +**/ +EFI_STATUS +add_v2p ( + IN OUT struct s_v2p **v2p, + EFI_PCI_IO_PROTOCOL_OPERATION type, + VOID *vaddr, + UINTN bsize + ) +{ + EFI_STATUS Status; + + if ((v2p == NULL) || (vaddr == NULL) || (bsize == 0)) { + return EFI_INVALID_PARAMETER; + } + + *v2p = AllocatePool (sizeof (struct s_v2p)); + if (*v2p != NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = mPciIoFncs->Map ( + mPciIoFncs, + type, + vaddr, + &bsize, + &(*v2p)->paddr, + &(*v2p)->unmap + ); + if (Status != EFI_SUCCESS) { + FreePool (*v2p); + return Status; + } + (*v2p)->vaddr = vaddr; + (*v2p)->bsize = bsize; + (*v2p)->next = _v2p; + _v2p = *v2p; + + return EFI_SUCCESS; +} + + +/** + This routine searches the linked list of mapped address nodes (for undi3.0 + interface) to find the node that corresponds to the given virtual address and + returns a pointer to that node. + + @param v2p pointer to return a map list node pointer. + @param vaddr virtual address (or CPU address) to be searched in + the map list + + @retval EFI_SUCEESS if a match found! + @retval Other match not found + +**/ +EFI_STATUS +find_v2p ( + struct s_v2p **v2p, + VOID *vaddr + ) +{ + struct s_v2p *v; + + if (v2p == NULL || vaddr == NULL) { + return EFI_INVALID_PARAMETER; + } + + for (v = _v2p; v != NULL; v = v->next) { + if (v->vaddr == vaddr) { + *v2p = v; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + + +/** + This routine unmaps the given virtual address and frees the memory allocated + for the map list node corresponding to that address. + + @param vaddr virtual address (or CPU address) to be unmapped + + @retval EFI_SUCEESS if successfully unmapped + @retval Other as indicated by the error + +**/ +EFI_STATUS +del_v2p ( + VOID *vaddr + ) +{ + struct s_v2p *v; + struct s_v2p *t; + EFI_STATUS Status; + + if (vaddr == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (_v2p == NULL) { + return EFI_NOT_FOUND; + } + // + // Is our node at the head of the list?? + // + if ((v = _v2p)->vaddr == vaddr) { + _v2p = _v2p->next; + + Status = mPciIoFncs->Unmap (mPciIoFncs, v->unmap); + + FreePool (v); + + if (Status) { + DEBUG ((EFI_D_ERROR, "Unmap failed with status = %x\n", Status)); + } + return Status; + } + + for (; v->next != NULL; v = t) { + if ((t = v->next)->vaddr == vaddr) { + v->next = t->next; + Status = mPciIoFncs->Unmap (mPciIoFncs, t->unmap); + FreePool (t); + + if (Status) { + DEBUG ((EFI_D_ERROR, "Unmap failed with status = %x\n", Status)); + } + return Status; + } + } + + return EFI_NOT_FOUND; +} + +STATIC +EFI_STATUS +issue_hwundi_command ( + UINT64 cdb + ) +/*++ + +Routine Description: + +Arguments: + +Returns: + +--*/ +{ + DEBUG ((EFI_D_ERROR, "\nissue_hwundi_command() - This should not be called!")); + + if (cdb == 0) { + return EFI_INVALID_PARAMETER; + + } + // + // %%TBD - For now, nothing is done. + // + return EFI_UNSUPPORTED; +} + + +/** + Compute 8-bit checksum of a buffer. + + @param ptr Pointer to buffer. + @param len Length of buffer in bytes. + + @return 8-bit checksum of all bytes in buffer. + @return If ptr is NULL or len is zero, zero is returned. + +**/ +STATIC +UINT8 +calc_8bit_cksum ( + VOID *ptr, + UINTN len + ) +{ + UINT8 *bptr; + UINT8 cksum; + + bptr = ptr; + cksum = 0; + + if (ptr == NULL || len == 0) { + return 0; + } + + while (len--) { + cksum = (UINT8) (cksum +*bptr++); + } + + return cksum; +} + + +/** + Test to see if this driver supports Controller. Any Controller + that contains a Nii protocol can be supported. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Not used. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +SimpleNetworkDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *NiiProtocol; + PXE_UNDI *pxe; + BOOLEAN IsUndi31; + + IsUndi31 = FALSE; + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + (VOID **) &NiiProtocol, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) + { + DEBUG ((EFI_D_INFO, "Support(): Already Started. on handle %x\n", Controller)); + return EFI_ALREADY_STARTED; + } + + if (!EFI_ERROR (Status)) + { + DEBUG ((EFI_D_INFO, "Support(): UNDI3.1 found on handle %x\n", Controller)); + IsUndi31 = TRUE; + } else { + // + // try the older 3.0 driver + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiNetworkInterfaceIdentifierProtocolGuid, + (VOID **) &NiiProtocol, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG ((EFI_D_INFO, "Support(): UNDI3.0 found on handle %x\n", Controller)); + } + // + // check the version, we don't want to connect to the undi16 + // + if (NiiProtocol->Type != EfiNetworkInterfaceUndi) { + Status = EFI_UNSUPPORTED; + goto Done; + } + // + // Check to see if !PXE structure is valid. Paragraph alignment of !PXE structure is required. + // + if (NiiProtocol->ID & 0x0F) { + DEBUG ((EFI_D_NET, "\n!PXE structure is not paragraph aligned.\n")); + Status = EFI_UNSUPPORTED; + goto Done; + } + + pxe = (PXE_UNDI *) (UINTN) (NiiProtocol->ID); + + // + // Verify !PXE revisions. + // + if (pxe->hw.Signature != PXE_ROMID_SIGNATURE) { + DEBUG ((EFI_D_NET, "\n!PXE signature is not valid.\n")); + Status = EFI_UNSUPPORTED; + goto Done; + } + + if (pxe->hw.Rev < PXE_ROMID_REV) { + DEBUG ((EFI_D_NET, "\n!PXE.Rev is not supported.\n")); + Status = EFI_UNSUPPORTED; + goto Done; + } + + if (pxe->hw.MajorVer < PXE_ROMID_MAJORVER) { + + DEBUG ((EFI_D_NET, "\n!PXE.MajorVer is not supported.\n")); + Status = EFI_UNSUPPORTED; + goto Done; + + } else if (pxe->hw.MajorVer == PXE_ROMID_MAJORVER && pxe->hw.MinorVer < PXE_ROMID_MINORVER) { + DEBUG ((EFI_D_NET, "\n!PXE.MinorVer is not supported.")); + Status = EFI_UNSUPPORTED; + goto Done; + } + // + // Do S/W UNDI specific checks. + // + if ((pxe->hw.Implementation & PXE_ROMID_IMP_HW_UNDI) == 0) { + if (pxe->sw.EntryPoint < pxe->sw.Len) { + DEBUG ((EFI_D_NET, "\n!PXE S/W entry point is not valid.")); + Status = EFI_UNSUPPORTED; + goto Done; + } + + if (pxe->sw.BusCnt == 0) { + DEBUG ((EFI_D_NET, "\n!PXE.BusCnt is zero.")); + Status = EFI_UNSUPPORTED; + goto Done; + } + } + + Status = EFI_SUCCESS; + DEBUG ((EFI_D_INFO, "Support(): supported on %x\n", Controller)); + +Done: + if (IsUndi31) { + gBS->CloseProtocol ( + Controller, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + This->DriverBindingHandle, + Controller + ); + + } else { + gBS->CloseProtocol ( + Controller, + &gEfiNetworkInterfaceIdentifierProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + return Status; +} + + +/** + called for any handle that we said "supported" in the above call! + + @param This Protocol instance pointer. + @param Controller Handle of device to start + @param RemainingDevicePath Not used. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver failed to start this device. + +**/ +EFI_STATUS +EFIAPI +SimpleNetworkDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii; + EFI_DEVICE_PATH_PROTOCOL *NiiDevicePath; + EFI_STATUS Status; + PXE_UNDI *pxe; + SNP_DRIVER *snp; + VOID *addr; + VOID *addrUnmap; + EFI_PHYSICAL_ADDRESS paddr; + EFI_HANDLE Handle; + UINTN Size; + BOOLEAN UndiNew; + PXE_PCI_CONFIG_INFO ConfigInfo; + PCI_TYPE00 *ConfigHeader; + UINT32 *TempBar; + UINT8 BarIndex; + PXE_STATFLAGS InitStatFlags; + + DEBUG ((EFI_D_NET, "\nSnpNotifyNetworkInterfaceIdentifier() ")); + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &NiiDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->LocateDevicePath ( + &gEfiPciIoProtocolGuid, + &NiiDevicePath, + &Handle + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Handle, + &gEfiPciIoProtocolGuid, + (VOID **) &mPciIoFncs, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Get the NII interface. look for 3.1 undi first, if it is not there + // then look for 3.0, validate the interface. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + (VOID **) &Nii, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return Status; + } + + if (!EFI_ERROR (Status)) { + // + // probably not a 3.1 UNDI + // + UndiNew = TRUE; + DEBUG ((EFI_D_INFO, "Start(): UNDI3.1 found\n")); + + } else { + UndiNew = FALSE; + Status = gBS->OpenProtocol ( + Controller, + &gEfiNetworkInterfaceIdentifierProtocolGuid, + (VOID **) &Nii, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; + } + + DEBUG ((EFI_D_INFO, "Start(): UNDI3.0 found\n")); + } + + pxe = (PXE_UNDI *) (UINTN) (Nii->ID); + + if (calc_8bit_cksum (pxe, pxe->hw.Len) != 0) { + DEBUG ((EFI_D_NET, "\n!PXE checksum is not correct.\n")); + goto NiiError; + } + + if ((pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED) != 0) { + // + // We can get any packets. + // + } else if ((pxe->hw.Implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED) != 0) { + // + // We need to be able to get broadcast packets for DHCP. + // If we do not have promiscuous support, we must at least have + // broadcast support or we cannot do DHCP! + // + } else { + DEBUG ((EFI_D_NET, "\nUNDI does not have promiscuous or broadcast support.")); + goto NiiError; + } + // + // OK, we like this UNDI, and we know snp is not already there on this handle + // Allocate and initialize a new simple network protocol structure. + // + Status = mPciIoFncs->AllocateBuffer ( + mPciIoFncs, + AllocateAnyPages, + EfiBootServicesData, + SNP_MEM_PAGES (sizeof (SNP_DRIVER)), + &addr, + 0 + ); + + if (Status != EFI_SUCCESS) { + DEBUG ((EFI_D_NET, "\nCould not allocate SNP_DRIVER structure.\n")); + goto NiiError; + } + + snp = (SNP_DRIVER *) (UINTN) addr; + + if (!UndiNew) { + Size = SNP_MEM_PAGES (sizeof (SNP_DRIVER)); + + Status = mPciIoFncs->Map ( + mPciIoFncs, + EfiPciIoOperationBusMasterCommonBuffer, + addr, + &Size, + &paddr, + &addrUnmap + ); + + ASSERT (paddr); + + DEBUG ((EFI_D_NET, "\nSNP_DRIVER @ %Xh, sizeof(SNP_DRIVER) == %d", addr, sizeof (SNP_DRIVER))); + snp = (SNP_DRIVER *) (UINTN) paddr; + snp->SnpDriverUnmap = addrUnmap; + } + + ZeroMem (snp, sizeof (SNP_DRIVER)); + + snp->IoFncs = mPciIoFncs; + snp->IsOldUndi = (BOOLEAN) (!UndiNew); + + snp->Signature = SNP_DRIVER_SIGNATURE; + + EfiInitializeLock (&snp->lock, TPL_NOTIFY); + + snp->snp.Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION; + snp->snp.Start = snp_undi32_start; + snp->snp.Stop = snp_undi32_stop; + snp->snp.Initialize = snp_undi32_initialize; + snp->snp.Reset = snp_undi32_reset; + snp->snp.Shutdown = snp_undi32_shutdown; + snp->snp.ReceiveFilters = snp_undi32_receive_filters; + snp->snp.StationAddress = snp_undi32_station_address; + snp->snp.Statistics = snp_undi32_statistics; + snp->snp.MCastIpToMac = snp_undi32_mcast_ip_to_mac; + snp->snp.NvData = snp_undi32_nvdata; + snp->snp.GetStatus = snp_undi32_get_status; + snp->snp.Transmit = snp_undi32_transmit; + snp->snp.Receive = snp_undi32_receive; + snp->snp.WaitForPacket = NULL; + + snp->snp.Mode = &snp->mode; + + snp->tx_rx_bufsize = 0; + snp->tx_rx_buffer = NULL; + + snp->if_num = Nii->IfNum; + + if ((pxe->hw.Implementation & PXE_ROMID_IMP_HW_UNDI) != 0) { + snp->is_swundi = FALSE; + snp->issue_undi32_command = &issue_hwundi_command; + } else { + snp->is_swundi = TRUE; + + if ((pxe->sw.Implementation & PXE_ROMID_IMP_SW_VIRT_ADDR) != 0) { + snp->issue_undi32_command = (issue_undi32_command) (UINTN) pxe->sw.EntryPoint; + } else { + snp->issue_undi32_command = (issue_undi32_command) (UINTN) ((UINT8) (UINTN) pxe + pxe->sw.EntryPoint); + } + } + // + // Allocate a global CPB and DB buffer for this UNDI interface. + // we do this because: + // + // -UNDI 3.0 wants all the addresses passed to it (even the cpb and db) to be + // within 2GB limit, create them here and map them so that when undi calls + // v2p callback to check if the physical address is < 2gb, we will pass. + // + // -This is not a requirement for 3.1 or later UNDIs but the code looks + // simpler if we use the same cpb, db variables for both old and new undi + // interfaces from all the SNP interface calls (we don't map the buffers + // for the newer undi interfaces though) + // . + // -it is OK to allocate one global set of CPB, DB pair for each UNDI + // interface as EFI does not multi-task and so SNP will not be re-entered! + // + Status = mPciIoFncs->AllocateBuffer ( + mPciIoFncs, + AllocateAnyPages, + EfiBootServicesData, + SNP_MEM_PAGES (4096), + &addr, + 0 + ); + + if (Status != EFI_SUCCESS) { + DEBUG ((EFI_D_NET, "\nCould not allocate CPB and DB structures.\n")); + goto Error_DeleteSNP; + } + + if (snp->IsOldUndi) { + Size = SNP_MEM_PAGES (4096); + + Status = mPciIoFncs->Map ( + mPciIoFncs, + EfiPciIoOperationBusMasterCommonBuffer, + addr, + &Size, + &paddr, + &snp->CpbUnmap + ); + + ASSERT (paddr); + + snp->cpb = (VOID *) (UINTN) paddr; + snp->db = (VOID *) ((UINTN) paddr + 2048); + } else { + snp->cpb = (VOID *) (UINTN) addr; + snp->db = (VOID *) ((UINTN) addr + 2048); + } + // + // pxe_start call is going to give the callback functions to UNDI, these callback + // functions use the BarIndex values from the snp structure, so these must be initialized + // with default values before doing a pxe_start. The correct values can be obtained after + // getting the config information from UNDI + // + snp->MemoryBarIndex = 0; + snp->IoBarIndex = 1; + + // + // we need the undi init information many times in this snp code, just get it + // once here and store it in the snp driver structure. to get Init Info + // from UNDI we have to start undi first. + // + Status = pxe_start (snp); + + if (Status != EFI_SUCCESS) { + goto Error_DeleteCPBDB; + } + + snp->cdb.OpCode = PXE_OPCODE_GET_INIT_INFO; + snp->cdb.OpFlags = PXE_OPFLAGS_NOT_USED; + + snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED; + snp->cdb.CPBaddr = PXE_DBADDR_NOT_USED; + + snp->cdb.DBsize = sizeof snp->init_info; + snp->cdb.DBaddr = (UINT64)(UINTN) &snp->init_info; + + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + DEBUG ((EFI_D_NET, "\nsnp->undi.get_init_info() ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + // + // Save the INIT Stat Code... + // + InitStatFlags = snp->cdb.StatFlags; + + if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) { + DEBUG ((EFI_D_NET, "\nsnp->undi.init_info() %xh:%xh\n", snp->cdb.StatFlags, snp->cdb.StatCode)); + pxe_stop (snp); + goto Error_DeleteCPBDB; + } + + snp->cdb.OpCode = PXE_OPCODE_GET_CONFIG_INFO; + snp->cdb.OpFlags = PXE_OPFLAGS_NOT_USED; + + snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED; + snp->cdb.CPBaddr = PXE_DBADDR_NOT_USED; + + snp->cdb.DBsize = sizeof ConfigInfo; + snp->cdb.DBaddr = (UINT64)(UINTN) &ConfigInfo; + + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + DEBUG ((EFI_D_NET, "\nsnp->undi.get_config_info() ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) { + DEBUG ((EFI_D_NET, "\nsnp->undi.config_info() %xh:%xh\n", snp->cdb.StatFlags, snp->cdb.StatCode)); + pxe_stop (snp); + goto Error_DeleteCPBDB; + } + // + // Find the correct BAR to do IO. + // + // + // Enumerate through the PCI BARs for the device to determine which one is + // the IO BAR. Save the index of the BAR into the adapter info structure. + // for regular 32bit BARs, 0 is memory mapped, 1 is io mapped + // + ConfigHeader = (PCI_TYPE00 *) &ConfigInfo.Config.Byte[0]; + TempBar = (UINT32 *) &ConfigHeader->Device.Bar[0]; + for (BarIndex = 0; BarIndex <= 5; BarIndex++) { + if ((*TempBar & PCI_BAR_MEM_MASK) == PCI_BAR_MEM_64BIT) { + // + // This is a 64-bit memory bar, skip this and the + // next bar as well. + // + TempBar++; + } + + if ((*TempBar & PCI_BAR_IO_MASK) == PCI_BAR_IO_MODE) { + snp->IoBarIndex = BarIndex; + break; + } + + TempBar++; + } + + // + // We allocate 2 more global buffers for undi 3.0 interface. We use these + // buffers to pass to undi when the user buffers are beyond 4GB. + // UNDI 3.0 wants all the addresses passed to it to be + // within 2GB limit, create them here and map them so that when undi calls + // v2p callback to check if the physical address is < 2gb, we will pass. + // + // For 3.1 and later UNDIs, we do not do this because undi is + // going to call the map() callback if and only if it wants to use the + // device address for any address it receives. + // + if (snp->IsOldUndi) { + // + // buffer for receive + // + Size = SNP_MEM_PAGES (snp->init_info.MediaHeaderLen + snp->init_info.FrameDataLen); + Status = mPciIoFncs->AllocateBuffer ( + mPciIoFncs, + AllocateAnyPages, + EfiBootServicesData, + Size, + &addr, + 0 + ); + + if (Status != EFI_SUCCESS) { + DEBUG ((EFI_D_ERROR, "\nCould not allocate receive buffer.\n")); + goto Error_DeleteCPBDB; + } + + Status = mPciIoFncs->Map ( + mPciIoFncs, + EfiPciIoOperationBusMasterCommonBuffer, + addr, + &Size, + &paddr, + &snp->ReceiveBufUnmap + ); + + ASSERT (paddr); + + snp->receive_buf = (UINT8 *) (UINTN) paddr; + + // + // buffer for fill_header + // + Size = SNP_MEM_PAGES (snp->init_info.MediaHeaderLen); + Status = mPciIoFncs->AllocateBuffer ( + mPciIoFncs, + AllocateAnyPages, + EfiBootServicesData, + Size, + &addr, + 0 + ); + + if (Status != EFI_SUCCESS) { + DEBUG ((EFI_D_ERROR, "\nCould not allocate fill_header buffer.\n")); + goto Error_DeleteRCVBuf; + } + + Status = mPciIoFncs->Map ( + mPciIoFncs, + EfiPciIoOperationBusMasterCommonBuffer, + addr, + &Size, + &paddr, + &snp->FillHdrBufUnmap + ); + + ASSERT (paddr); + snp->fill_hdr_buf = (UINT8 *) (UINTN) paddr; + } + // + // Initialize simple network protocol mode structure + // + snp->mode.State = EfiSimpleNetworkStopped; + snp->mode.HwAddressSize = snp->init_info.HWaddrLen; + snp->mode.MediaHeaderSize = snp->init_info.MediaHeaderLen; + snp->mode.MaxPacketSize = snp->init_info.FrameDataLen; + snp->mode.NvRamAccessSize = snp->init_info.NvWidth; + snp->mode.NvRamSize = snp->init_info.NvCount * snp->mode.NvRamAccessSize; + snp->mode.IfType = snp->init_info.IFtype; + snp->mode.MaxMCastFilterCount = snp->init_info.MCastFilterCnt; + snp->mode.MCastFilterCount = 0; + + switch (InitStatFlags & PXE_STATFLAGS_CABLE_DETECT_MASK) { + case PXE_STATFLAGS_CABLE_DETECT_SUPPORTED: + snp->mode.MediaPresentSupported = TRUE; + break; + + case PXE_STATFLAGS_CABLE_DETECT_NOT_SUPPORTED: + default: + snp->mode.MediaPresentSupported = FALSE; + } + + if ((pxe->hw.Implementation & PXE_ROMID_IMP_STATION_ADDR_SETTABLE) != 0) { + snp->mode.MacAddressChangeable = TRUE; + } else { + snp->mode.MacAddressChangeable = FALSE; + } + + if ((pxe->hw.Implementation & PXE_ROMID_IMP_MULTI_FRAME_SUPPORTED) != 0) { + snp->mode.MultipleTxSupported = TRUE; + } else { + snp->mode.MultipleTxSupported = FALSE; + } + + snp->mode.ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST; + + if ((pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED) != 0) { + snp->mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + + } + + if ((pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED) != 0) { + snp->mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS; + + } + + if ((pxe->hw.Implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED) != 0) { + snp->mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST; + + } + + if ((pxe->hw.Implementation & PXE_ROMID_IMP_FILTERED_MULTICAST_RX_SUPPORTED) != 0) { + snp->mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST; + + } + + if (pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED) { + snp->mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + + } + + snp->mode.ReceiveFilterSetting = 0; + + // + // need to get the station address to save in the mode structure. we need to + // initialize the UNDI first for this. + // + snp->tx_rx_bufsize = snp->init_info.MemoryRequired; + Status = pxe_init (snp, PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE); + + if (Status) { + pxe_stop (snp); + goto Error_DeleteHdrBuf; + } + + Status = pxe_get_stn_addr (snp); + + if (Status != EFI_SUCCESS) { + DEBUG ((EFI_D_ERROR, "\nsnp->undi.get_station_addr() failed.\n")); + pxe_shutdown (snp); + pxe_stop (snp); + goto Error_DeleteHdrBuf; + } + + snp->mode.MediaPresent = FALSE; + + // + // We should not leave UNDI started and initialized here. this DriverStart() + // routine must only find and attach the SNP interface to UNDI layer that it + // finds on the given handle! + // The UNDI layer will be started when upper layers call snp->start. + // How ever, this DriverStart() must fill up the snp mode structure which + // contains the MAC address of the NIC. For this reason we started and + // initialized UNDI here, now we are done, do a shutdown and stop of the + // UNDI interface! + // + pxe_shutdown (snp); + pxe_stop (snp); + + // + // add SNP to the undi handle + // + Status = gBS->InstallProtocolInterface ( + &Controller, + &gEfiSimpleNetworkProtocolGuid, + EFI_NATIVE_INTERFACE, + &(snp->snp) + ); + + if (!EFI_ERROR (Status)) { + return Status; + } + +Error_DeleteHdrBuf: + if (snp->IsOldUndi) { + Status = mPciIoFncs->Unmap ( + mPciIoFncs, + snp->FillHdrBufUnmap + ); + Size = SNP_MEM_PAGES (snp->init_info.MediaHeaderLen); + mPciIoFncs->FreeBuffer ( + mPciIoFncs, + Size, + snp->fill_hdr_buf + ); + } + +Error_DeleteRCVBuf: + if (snp->IsOldUndi) { + Status = mPciIoFncs->Unmap ( + mPciIoFncs, + snp->ReceiveBufUnmap + ); + Size = SNP_MEM_PAGES (snp->init_info.MediaHeaderLen + snp->init_info.FrameDataLen); + mPciIoFncs->FreeBuffer ( + mPciIoFncs, + Size, + snp->receive_buf + ); + + } + +Error_DeleteCPBDB: + if (snp->IsOldUndi) { + Status = mPciIoFncs->Unmap ( + mPciIoFncs, + snp->CpbUnmap + ); + } + + Status = mPciIoFncs->FreeBuffer ( + mPciIoFncs, + SNP_MEM_PAGES (4096), + snp->cpb + ); + +Error_DeleteSNP: + if (snp->IsOldUndi) { + Status = mPciIoFncs->Unmap ( + mPciIoFncs, + snp->SnpDriverUnmap + ); + } + + mPciIoFncs->FreeBuffer ( + mPciIoFncs, + SNP_MEM_PAGES (sizeof (SNP_DRIVER)), + snp + ); +NiiError: + if (!UndiNew) { + gBS->CloseProtocol ( + Controller, + &gEfiNetworkInterfaceIdentifierProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } else { + gBS->CloseProtocol ( + Controller, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + This->DriverBindingHandle, + Controller + ); + } + + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + + + +**/ +EFI_STATUS +EFIAPI +SimpleNetworkDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_PROTOCOL *SnpProtocol; + SNP_DRIVER *Snp; + + // + // Get our context back. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &SnpProtocol, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (SnpProtocol); + + Status = gBS->UninstallProtocolInterface ( + Controller, + &gEfiSimpleNetworkProtocolGuid, + &Snp->snp + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + if (!Snp->IsOldUndi) { + Status = gBS->CloseProtocol ( + Controller, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + This->DriverBindingHandle, + Controller + ); + } else { + Status = gBS->CloseProtocol ( + Controller, + &gEfiNetworkInterfaceIdentifierProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + Status = gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + pxe_shutdown (Snp); + pxe_stop (Snp); + + if (Snp->IsOldUndi) { + Status = mPciIoFncs->Unmap ( + mPciIoFncs, + Snp->FillHdrBufUnmap + ); + + mPciIoFncs->FreeBuffer ( + mPciIoFncs, + SNP_MEM_PAGES (Snp->init_info.MediaHeaderLen), + Snp->fill_hdr_buf + ); + Status = mPciIoFncs->Unmap ( + mPciIoFncs, + Snp->ReceiveBufUnmap + ); + + mPciIoFncs->FreeBuffer ( + mPciIoFncs, + SNP_MEM_PAGES (Snp->init_info.MediaHeaderLen + Snp->init_info.FrameDataLen), + Snp->receive_buf + ); + + Status = mPciIoFncs->Unmap ( + mPciIoFncs, + Snp->CpbUnmap + ); + Status = mPciIoFncs->Unmap ( + mPciIoFncs, + Snp->SnpDriverUnmap + ); + } + + mPciIoFncs->FreeBuffer ( + mPciIoFncs, + SNP_MEM_PAGES (4096), + Snp->cpb + ); + + mPciIoFncs->FreeBuffer ( + mPciIoFncs, + SNP_MEM_PAGES (sizeof (SNP_DRIVER)), + Snp + ); + + return Status; +} + + +/** + Install all the driver protocol + + @param entry EFI_IMAGE_ENTRY_POINT) + + @retval EFI_SUCEESS Initialization routine has found UNDI hardware, + loaded it's ROM, and installed a notify event for + the Network Indentifier Interface Protocol + successfully. + @retval Other Return value from HandleProtocol for + DeviceIoProtocol or LoadedImageProtocol + +**/ +EFI_STATUS +EFIAPI +InitializeSnpNiiDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EfiLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &mSimpleNetworkDriverBinding, + NULL, + COMPONENT_NAME, + NULL, + NULL + ); +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/snp.h b/MdeModulePkg/Universal/Network/SnpDxe/snp.h new file mode 100644 index 0000000000..624a44c971 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/snp.h @@ -0,0 +1,454 @@ +/** @file + +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + snp.h + +Abstract: + +Revision history: + + +**/ +#ifndef _SNP_H +#define _SNP_H + + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FOUR_GIGABYTES (UINT64) 0x100000000ULL + +// +// Driver Consumed Protocol Prototypes +// +//@MT:#include EFI_PROTOCOL_DEFINITION (DevicePath) +//@MT:#include EFI_PROTOCOL_DEFINITION (PciIo) +//@MT:#include EFI_PROTOCOL_DEFINITION (EfiNetworkInterfaceIdentifier) + +// +// Driver Produced Protocol Prototypes +// +//@MT:#include EFI_PROTOCOL_DEFINITION (DriverBinding) +//@MT:#include EFI_PROTOCOL_DEFINITION (ComponentName) +//@MT:#include EFI_PROTOCOL_DEFINITION (ComponentName2) +//@MT:#include EFI_PROTOCOL_DEFINITION (SimpleNetwork) + +#define SNP_DRIVER_SIGNATURE EFI_SIGNATURE_32 ('s', 'n', 'd', 's') +#define MAX_MAP_LENGTH 100 + +#define PCI_BAR_IO_MASK 0x00000003 +#define PCI_BAR_IO_MODE 0x00000001 + +#define PCI_BAR_MEM_MASK 0x0000000F +#define PCI_BAR_MEM_MODE 0x00000000 +#define PCI_BAR_MEM_64BIT 0x00000004 + +typedef struct { + UINT32 Signature; + EFI_LOCK lock; + + EFI_SIMPLE_NETWORK_PROTOCOL snp; + EFI_SIMPLE_NETWORK_MODE mode; + + EFI_HANDLE device_handle; + EFI_DEVICE_PATH_PROTOCOL *device_path; + + // + // Local instance data needed by SNP driver + // + // Pointer to S/W UNDI API entry point + // This will be NULL for H/W UNDI + // + EFI_STATUS (*issue_undi32_command) (UINT64 cdb); + + BOOLEAN is_swundi; + + // + // undi interface number, if one undi manages more nics + // + PXE_IFNUM if_num; + + // + // Allocated tx/rx buffer that was passed to UNDI Initialize. + // + UINT32 tx_rx_bufsize; + VOID *tx_rx_buffer; + // + // mappable buffers for receive and fill header for undi3.0 + // these will be used if the user buffers are above 4GB limit (instead of + // mapping the user buffers) + // + UINT8 *receive_buf; + VOID *ReceiveBufUnmap; + UINT8 *fill_hdr_buf; + VOID *FillHdrBufUnmap; + + EFI_PCI_IO_PROTOCOL *IoFncs; + UINT8 IoBarIndex; + UINT8 MemoryBarIndex; + BOOLEAN IsOldUndi; // true for EFI1.0 UNDI (3.0) drivers + // + // Buffers for command descriptor block, command parameter block + // and data block. + // + PXE_CDB cdb; + VOID *cpb; + VOID *CpbUnmap; + VOID *db; + + // + // UNDI structure, we need to remember the init info for a long time! + // + PXE_DB_GET_INIT_INFO init_info; + + VOID *SnpDriverUnmap; + // + // when ever we map an address, we must remember it's address and the un-map + // cookie so that we can unmap later + // + struct s_map_list { + EFI_PHYSICAL_ADDRESS virt; + VOID *map_cookie; + } map_list[MAX_MAP_LENGTH]; +} +SNP_DRIVER; + +#define EFI_SIMPLE_NETWORK_DEV_FROM_THIS(a) CR (a, SNP_DRIVER, snp, SNP_DRIVER_SIGNATURE) + +// +// Global Variables +// +extern EFI_COMPONENT_NAME_PROTOCOL gSimpleNetworkComponentName; + +// +// Virtual to physical mapping for all UNDI 3.0s. +// +extern struct s_v2p { + struct s_v2p *next; + VOID *vaddr; + UINTN bsize; + EFI_PHYSICAL_ADDRESS paddr; + VOID *unmap; +} +*_v2p; + +EFI_STATUS +add_v2p ( + struct s_v2p **v2p, + EFI_PCI_IO_PROTOCOL_OPERATION type, + VOID *vaddr, + UINTN bsize + ) +; + +EFI_STATUS +find_v2p ( + struct s_v2p **v2p, + VOID *vaddr + ) +; + +EFI_STATUS +del_v2p ( + VOID *vaddr + ) +; + +extern +VOID +snp_undi32_callback_block_30 ( + IN UINT32 Enable + ) +; + +extern +VOID +snp_undi32_callback_delay_30 ( + IN UINT64 MicroSeconds + ) +; + +extern +VOID +snp_undi32_callback_memio_30 ( + IN UINT8 ReadOrWrite, + IN UINT8 NumBytes, + IN UINT64 MemOrPortAddress, + IN OUT UINT64 BufferPtr + ) +; + +extern +VOID +snp_undi32_callback_v2p_30 ( + IN UINT64 CpuAddr, + IN OUT UINT64 DeviceAddrPtr + ) +; + +extern +VOID +snp_undi32_callback_block ( + IN UINT64 UniqueId, + IN UINT32 Enable + ) +; + +extern +VOID +snp_undi32_callback_delay ( + IN UINT64 UniqueId, + IN UINT64 MicroSeconds + ) +; + +extern +VOID +snp_undi32_callback_memio ( + IN UINT64 UniqueId, + IN UINT8 ReadOrWrite, + IN UINT8 NumBytes, + IN UINT64 MemOrPortAddr, + IN OUT UINT64 BufferPtr + ) +; + +extern +VOID +snp_undi32_callback_map ( + IN UINT64 UniqueId, + IN UINT64 CpuAddr, + IN UINT32 NumBytes, + IN UINT32 Direction, + IN OUT UINT64 DeviceAddrPtr + ) +; + +extern +VOID +snp_undi32_callback_unmap ( + IN UINT64 UniqueId, + IN UINT64 CpuAddr, + IN UINT32 NumBytes, + IN UINT32 Direction, + IN UINT64 DeviceAddr // not a pointer to device address + ) +; + +extern +VOID +snp_undi32_callback_sync ( + IN UINT64 UniqueId, + IN UINT64 CpuAddr, + IN UINT32 NumBytes, + IN UINT32 Direction, + IN UINT64 DeviceAddr // not a pointer to device address + ) +; + +extern +EFI_STATUS +EFIAPI +snp_undi32_start ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *this + ) +; + +extern +EFI_STATUS +EFIAPI +snp_undi32_stop ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *this + ) +; + +extern +EFI_STATUS +EFIAPI +snp_undi32_initialize ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *this, + IN UINTN extra_rx_buffer_size OPTIONAL, + IN UINTN extra_tx_buffer_size OPTIONAL + ) +; + +extern +EFI_STATUS +EFIAPI +snp_undi32_reset ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *this, + IN BOOLEAN ExtendedVerification + ) +; + +extern +EFI_STATUS +EFIAPI +snp_undi32_shutdown ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *this + ) +; + +extern +EFI_STATUS +EFIAPI +snp_undi32_receive_filters ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * this, + IN UINT32 enable, + IN UINT32 disable, + IN BOOLEAN reset_mcast_filter, + IN UINTN mcast_filter_count OPTIONAL, + IN EFI_MAC_ADDRESS * mcast_filter OPTIONAL + ) +; + +extern +EFI_STATUS +EFIAPI +snp_undi32_station_address ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * this, + IN BOOLEAN reset, + IN EFI_MAC_ADDRESS *new OPTIONAL + ) +; + +extern +EFI_STATUS +EFIAPI +snp_undi32_statistics ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * this, + IN BOOLEAN reset, + IN OUT UINTN *statistics_size OPTIONAL, + IN OUT EFI_NETWORK_STATISTICS * statistics_table OPTIONAL + ) +; + +extern +EFI_STATUS +EFIAPI +snp_undi32_mcast_ip_to_mac ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *this, + IN BOOLEAN IPv6, + IN EFI_IP_ADDRESS *IP, + OUT EFI_MAC_ADDRESS *MAC + ) +; + +extern +EFI_STATUS +EFIAPI +snp_undi32_nvdata ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *this, + IN BOOLEAN read_write, + IN UINTN offset, + IN UINTN buffer_size, + IN OUT VOID *buffer + ) +; + +extern +EFI_STATUS +EFIAPI +snp_undi32_get_status ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * this, + OUT UINT32 *interrupt_status OPTIONAL, + OUT VOID **tx_buffer OPTIONAL + ) +; + +extern +EFI_STATUS +EFIAPI +snp_undi32_transmit ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * this, + IN UINTN header_size, + IN UINTN buffer_size, + IN VOID *buffer, + IN EFI_MAC_ADDRESS * src_addr OPTIONAL, + IN EFI_MAC_ADDRESS * dest_addr OPTIONAL, + IN UINT16 *protocol OPTIONAL + ) +; + +extern +EFI_STATUS +EFIAPI +snp_undi32_receive ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * this, + OUT UINTN *header_size OPTIONAL, + IN OUT UINTN *buffer_size, + OUT VOID *buffer, + OUT EFI_MAC_ADDRESS * src_addr OPTIONAL, + OUT EFI_MAC_ADDRESS * dest_addr OPTIONAL, + OUT UINT16 *protocol OPTIONAL + ) +; + +typedef +EFI_STATUS +(*issue_undi32_command) ( + UINT64 cdb + ); +typedef +VOID +(*ptr) ( + VOID + ); + + +/** + Install all the driver protocol + + @param ImageHandle Driver image handle + @param SystemTable System services table + + @retval EFI_SUCEESS Initialization routine has found UNDI hardware, loaded it's + ROM, and installed a notify event for the Network + Indentifier Interface Protocol successfully. + @retval Other Return value from HandleProtocol for DeviceIoProtocol or + LoadedImageProtocol + +**/ +EFI_STATUS +EFIAPI +InitializeSnpNiiDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +; + +#ifdef EFI_SIZE_REDUCTION_APPLIED + #define COMPONENT_NAME_CODE(code) + #define COMPONENT_NAME NULL +#else + #define COMPONENT_NAME_CODE(code) code + #define COMPONENT_NAME &gSimpleNetworkComponentName +#endif + +#define SNP_MEM_PAGES(x) (((x) - 1) / 4096 + 1) + + +#endif /* _SNP_H */ diff --git a/MdeModulePkg/Universal/Network/SnpDxe/start.c b/MdeModulePkg/Universal/Network/SnpDxe/start.c new file mode 100644 index 0000000000..0a43f8e60c --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/start.c @@ -0,0 +1,191 @@ +/** @file +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + start.c + +Abstract: + +Revision history: + 2000-Feb-07 M(f)J Genesis. + +**/ + +#include "Snp.h" + + +/** + this routine calls undi to start the interface and changes the snp state! + + @param snp pointer to snp driver structure + + +**/ +EFI_STATUS +pxe_start ( + SNP_DRIVER *snp + ) +{ + PXE_CPB_START_30 *cpb; + PXE_CPB_START_31 *cpb_31; + + cpb = snp->cpb; + cpb_31 = snp->cpb; + // + // Initialize UNDI Start CDB for H/W UNDI + // + snp->cdb.OpCode = PXE_OPCODE_START; + snp->cdb.OpFlags = PXE_OPFLAGS_NOT_USED; + snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED; + snp->cdb.DBsize = PXE_DBSIZE_NOT_USED; + snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED; + snp->cdb.DBaddr = PXE_DBADDR_NOT_USED; + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + // + // Make changes to H/W UNDI Start CDB if this is + // a S/W UNDI. + // + if (snp->is_swundi) { + if (snp->IsOldUndi) { + snp->cdb.CPBsize = sizeof (PXE_CPB_START_30); + snp->cdb.CPBaddr = (UINT64)(UINTN) cpb; + + cpb->Delay = (UINT64)(UINTN) &snp_undi32_callback_delay_30; + cpb->Block = (UINT64)(UINTN) &snp_undi32_callback_block_30; + + // + // Virtual == Physical. This can be set to zero. + // + cpb->Virt2Phys = (UINT64)(UINTN) &snp_undi32_callback_v2p_30; + cpb->Mem_IO = (UINT64)(UINTN) &snp_undi32_callback_memio_30; + } else { + snp->cdb.CPBsize = sizeof (PXE_CPB_START_31); + snp->cdb.CPBaddr = (UINT64)(UINTN) cpb_31; + + cpb_31->Delay = (UINT64)(UINTN) &snp_undi32_callback_delay; + cpb_31->Block = (UINT64)(UINTN) &snp_undi32_callback_block; + + // + // Virtual == Physical. This can be set to zero. + // + cpb_31->Virt2Phys = (UINT64)(UINTN) 0; + cpb_31->Mem_IO = (UINT64)(UINTN) &snp_undi32_callback_memio; + + cpb_31->Map_Mem = (UINT64)(UINTN) &snp_undi32_callback_map; + cpb_31->UnMap_Mem = (UINT64)(UINTN) &snp_undi32_callback_unmap; + cpb_31->Sync_Mem = (UINT64)(UINTN) &snp_undi32_callback_sync; + + cpb_31->Unique_ID = (UINT64)(UINTN) snp; + } + } + // + // Issue UNDI command and check result. + // + DEBUG ((EFI_D_NET, "\nsnp->undi.start() ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) { + // + // UNDI could not be started. Return UNDI error. + // + DEBUG ( + (EFI_D_ERROR, + "\nsnp->undi.start() %xh:%xh\n", + snp->cdb.StatCode, + snp->cdb.StatFlags) + ); + + return EFI_DEVICE_ERROR; + } + // + // Set simple network state to Started and return success. + // + snp->mode.State = EfiSimpleNetworkStarted; + + return EFI_SUCCESS; +} + + +/** + This is the SNP interface routine for starting the interface + This routine basically retrieves snp structure, checks the SNP state and + calls the pxe_start routine to actually do start undi interface + + @param This context pointer + + @retval EFI_INVALID_PARAMETER "This" is Null + @retval No SNP driver can be extracted from "This" + @retval EFI_ALREADY_STARTED The state of SNP is EfiSimpleNetworkStarted or + EfiSimpleNetworkInitialized + @retval EFI_DEVICE_ERROR The state of SNP is other than + EfiSimpleNetworkStarted, + EfiSimpleNetworkInitialized, and + EfiSimpleNetworkStopped + @retval EFI_SUCCESS UNDI interface is succesfully started + @retval Other Error occurs while calling pxe_start function. + +**/ +EFI_STATUS +EFIAPI +snp_undi32_start ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ) +{ + SNP_DRIVER *Snp; + EFI_STATUS Status; + UINTN Index; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + switch (Snp->mode.State) { + case EfiSimpleNetworkStopped: + break; + + case EfiSimpleNetworkStarted: + case EfiSimpleNetworkInitialized: + Status = EFI_ALREADY_STARTED; + goto ON_EXIT; + + default: + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Status = pxe_start (Snp); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // clear the map_list in SNP structure + // + for (Index = 0; Index < MAX_MAP_LENGTH; Index++) { + Snp->map_list[Index].virt = 0; + Snp->map_list[Index].map_cookie = 0; + } + + Snp->mode.MCastFilterCount = 0; + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/station_address.c b/MdeModulePkg/Universal/Network/SnpDxe/station_address.c new file mode 100644 index 0000000000..b6e0728d71 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/station_address.c @@ -0,0 +1,237 @@ +/** @file +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + station_address.c + +Abstract: + +Revision history: + 2000-Feb-17 M(f)J Genesis. + +**/ + +#include "Snp.h" + + +/** + this routine calls undi to read the MAC address of the NIC and updates the + mode structure with the address. + + @param snp pointer to snp driver structure + + +**/ +EFI_STATUS +pxe_get_stn_addr ( + SNP_DRIVER *snp + ) +{ + PXE_DB_STATION_ADDRESS *db; + + db = snp->db; + snp->cdb.OpCode = PXE_OPCODE_STATION_ADDRESS; + snp->cdb.OpFlags = PXE_OPFLAGS_STATION_ADDRESS_READ; + + snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED; + snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED; + + snp->cdb.DBsize = sizeof (PXE_DB_STATION_ADDRESS); + snp->cdb.DBaddr = (UINT64)(UINTN) db; + + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + // + // Issue UNDI command and check result. + // + DEBUG ((EFI_D_NET, "\nsnp->undi.station_addr() ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) { + DEBUG ( + (EFI_D_ERROR, + "\nsnp->undi.station_addr() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + return EFI_DEVICE_ERROR; + } + // + // Set new station address in SNP->Mode structure and return success. + // + CopyMem ( + &(snp->mode.CurrentAddress), + &db->StationAddr, + snp->mode.HwAddressSize + ); + + CopyMem ( + &snp->mode.BroadcastAddress, + &db->BroadcastAddr, + snp->mode.HwAddressSize + ); + + CopyMem ( + &snp->mode.PermanentAddress, + &db->PermanentAddr, + snp->mode.HwAddressSize + ); + + return EFI_SUCCESS; +} + + +/** + this routine calls undi to set a new MAC address for the NIC, + + @param snp pointer to snp driver structure + @param NewMacAddr pointer to a mac address to be set for the nic, if this is + NULL then this routine resets the mac address to the NIC's + original address. + + +**/ +STATIC +EFI_STATUS +pxe_set_stn_addr ( + SNP_DRIVER *snp, + EFI_MAC_ADDRESS *NewMacAddr + ) +{ + PXE_CPB_STATION_ADDRESS *cpb; + PXE_DB_STATION_ADDRESS *db; + + cpb = snp->cpb; + db = snp->db; + snp->cdb.OpCode = PXE_OPCODE_STATION_ADDRESS; + + if (NewMacAddr == NULL) { + snp->cdb.OpFlags = PXE_OPFLAGS_STATION_ADDRESS_RESET; + snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED; + snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED; + } else { + snp->cdb.OpFlags = PXE_OPFLAGS_STATION_ADDRESS_READ; + // + // even though the OPFLAGS are set to READ, supplying a new address + // in the CPB will make undi change the mac address to the new one. + // + CopyMem (&cpb->StationAddr, NewMacAddr, snp->mode.HwAddressSize); + + snp->cdb.CPBsize = sizeof (PXE_CPB_STATION_ADDRESS); + snp->cdb.CPBaddr = (UINT64)(UINTN) cpb; + } + + snp->cdb.DBsize = sizeof (PXE_DB_STATION_ADDRESS); + snp->cdb.DBaddr = (UINT64)(UINTN) db; + + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + // + // Issue UNDI command and check result. + // + DEBUG ((EFI_D_NET, "\nsnp->undi.station_addr() ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) { + DEBUG ( + (EFI_D_ERROR, + "\nsnp->undi.station_addr() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + // + // UNDI command failed. Return UNDI status to caller. + // + return EFI_DEVICE_ERROR; + } + // + // read the changed address and save it in SNP->Mode structure + // + pxe_get_stn_addr (snp); + + return EFI_SUCCESS; +} + + +/** + This is the SNP interface routine for changing the NIC's mac address. + This routine basically retrieves snp structure, checks the SNP state and + calls the above routines to actually do the work + + @param this context pointer + @param NewMacAddr pointer to a mac address to be set for the nic, if this is + NULL then this routine resets the mac address to the NIC's + original address. + @param ResetFlag If true, the mac address will change to NIC's original + address + + +**/ +EFI_STATUS +EFIAPI +snp_undi32_station_address ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * this, + IN BOOLEAN ResetFlag, + IN EFI_MAC_ADDRESS * NewMacAddr OPTIONAL + ) +{ + SNP_DRIVER *snp; + EFI_STATUS Status; + EFI_TPL OldTpl; + + // + // Check for invalid parameter combinations. + // + if ((this == NULL) || + (!ResetFlag && (NewMacAddr == NULL))) { + return EFI_INVALID_PARAMETER; + } + + snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Return error if the SNP is not initialized. + // + switch (snp->mode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto ON_EXIT; + + default: + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + if (ResetFlag) { + Status = pxe_set_stn_addr (snp, NULL); + } else { + Status = pxe_set_stn_addr (snp, NewMacAddr); + } + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/statistics.c b/MdeModulePkg/Universal/Network/SnpDxe/statistics.c new file mode 100644 index 0000000000..19f28239a5 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/statistics.c @@ -0,0 +1,201 @@ +/** @file +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + statistics.c + +Abstract: + +Revision history: + 2000-Feb-17 M(f)J Genesis. + +**/ + + +#include "Snp.h" + + +/** + This is the SNP interface routine for getting the NIC's statistics. + This routine basically retrieves snp structure, checks the SNP state and + calls the pxe_ routine to actually do the + + @param this context pointer + @param ResetFlag true to reset the NIC's statistics counters to zero. + @param StatTableSizePtr pointer to the statistics table size + @param StatTablePtr pointer to the statistics table + + +**/ +EFI_STATUS +EFIAPI +snp_undi32_statistics ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * this, + IN BOOLEAN ResetFlag, + IN OUT UINTN *StatTableSizePtr OPTIONAL, + IN OUT EFI_NETWORK_STATISTICS * StatTablePtr OPTIONAL + ) +{ + SNP_DRIVER *snp; + PXE_DB_STATISTICS *db; + UINT64 *stp; + UINT64 mask; + UINTN size; + UINTN n; + EFI_TPL OldTpl; + EFI_STATUS Status; + + // + // Get pointer to SNP driver instance for *this. + // + if (this == NULL) { + return EFI_INVALID_PARAMETER; + } + + snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Return error if the SNP is not initialized. + // + switch (snp->mode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto ON_EXIT; + + default: + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + // + // if we are not resetting the counters, we have to have a valid stat table + // with >0 size. if no reset, no table and no size, return success. + // + if (!ResetFlag && StatTableSizePtr == NULL) { + Status = StatTablePtr ? EFI_INVALID_PARAMETER : EFI_SUCCESS; + goto ON_EXIT; + } + // + // Initialize UNDI Statistics CDB + // + snp->cdb.OpCode = PXE_OPCODE_STATISTICS; + snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED; + snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED; + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + if (ResetFlag) { + snp->cdb.OpFlags = PXE_OPFLAGS_STATISTICS_RESET; + snp->cdb.DBsize = PXE_DBSIZE_NOT_USED; + snp->cdb.DBaddr = PXE_DBADDR_NOT_USED; + db = snp->db; + } else { + snp->cdb.OpFlags = PXE_OPFLAGS_STATISTICS_READ; + snp->cdb.DBsize = sizeof (PXE_DB_STATISTICS); + snp->cdb.DBaddr = (UINT64)(UINTN) (db = snp->db); + } + // + // Issue UNDI command and check result. + // + DEBUG ((EFI_D_NET, "\nsnp->undi.statistics() ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + switch (snp->cdb.StatCode) { + case PXE_STATCODE_SUCCESS: + break; + + case PXE_STATCODE_UNSUPPORTED: + DEBUG ( + (EFI_D_ERROR, + "\nsnp->undi.statistics() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + + default: + DEBUG ( + (EFI_D_ERROR, + "\nsnp->undi.statistics() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + if (ResetFlag) { + Status = EFI_SUCCESS; + goto ON_EXIT; + } + + if (StatTablePtr == NULL) { + *StatTableSizePtr = sizeof (EFI_NETWORK_STATISTICS); + Status = EFI_BUFFER_TOO_SMALL; + goto ON_EXIT; + } + // + // Convert the UNDI statistics information to SNP statistics + // information. + // + ZeroMem (StatTablePtr, *StatTableSizePtr); + stp = (UINT64 *) StatTablePtr; + size = 0; + + for (n = 0, mask = 1; n < 64; n++, mask = LShiftU64 (mask, 1), stp++) { + // + // There must be room for a full UINT64. Partial + // numbers will not be stored. + // + if ((n + 1) * sizeof (UINT64) > *StatTableSizePtr) { + break; + } + + if (db->Supported & mask) { + *stp = db->Data[n]; + size = n + 1; + } else { + SetMem (stp, sizeof (UINT64), 0xFF); + } + } + // + // Compute size up to last supported statistic. + // + while (++n < 64) { + if (db->Supported & (mask = LShiftU64 (mask, 1))) { + size = n; + } + } + + size *= sizeof (UINT64); + + if (*StatTableSizePtr >= size) { + *StatTableSizePtr = size; + Status = EFI_SUCCESS; + } else { + *StatTableSizePtr = size; + Status = EFI_BUFFER_TOO_SMALL; + } + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/stop.c b/MdeModulePkg/Universal/Network/SnpDxe/stop.c new file mode 100644 index 0000000000..63725237b7 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/stop.c @@ -0,0 +1,118 @@ +/** @file +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + stop.c + +Abstract: + +Revision history: + 2000-Feb-09 M(f)J Genesis. + +**/ + +#include "Snp.h" + + +/** + this routine calls undi to stop the interface and changes the snp state + + @param snp pointer to snp driver structure + + +**/ +EFI_STATUS +pxe_stop ( + SNP_DRIVER *snp + ) +{ + snp->cdb.OpCode = PXE_OPCODE_STOP; + snp->cdb.OpFlags = PXE_OPFLAGS_NOT_USED; + snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED; + snp->cdb.DBsize = PXE_DBSIZE_NOT_USED; + snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED; + snp->cdb.DBaddr = PXE_DBADDR_NOT_USED; + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + // + // Issue UNDI command + // + DEBUG ((EFI_D_NET, "\nsnp->undi.stop() ")); + + (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb); + + if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) { + DEBUG ( + (EFI_D_WARN, + "\nsnp->undi.stop() %xh:%xh\n", + snp->cdb.StatCode, + snp->cdb.StatFlags) + ); + + return EFI_DEVICE_ERROR; + } + // + // Set simple network state to Started and return success. + // + snp->mode.State = EfiSimpleNetworkStopped; + return EFI_SUCCESS; +} + + +/** + This is the SNP interface routine for stopping the interface. + This routine basically retrieves snp structure, checks the SNP state and + calls the pxe_stop routine to actually stop the undi interface + + @param this context pointer + + +**/ +EFI_STATUS +EFIAPI +snp_undi32_stop ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *this + ) +{ + SNP_DRIVER *snp; + EFI_TPL OldTpl; + EFI_STATUS Status; + + if (this == NULL) { + return EFI_INVALID_PARAMETER; + } + + snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + switch (snp->mode.State) { + case EfiSimpleNetworkStarted: + break; + + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto ON_EXIT; + + default: + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Status = pxe_stop (snp); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/SnpDxe/transmit.c b/MdeModulePkg/Universal/Network/SnpDxe/transmit.c new file mode 100644 index 0000000000..d113edec96 --- /dev/null +++ b/MdeModulePkg/Universal/Network/SnpDxe/transmit.c @@ -0,0 +1,399 @@ +/** @file +Copyright (c) 2004 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module name: + + transmit.c + +Abstract: + +Revision history: + 2000-Feb-03 M(f)J Genesis. + +**/ + +#include "Snp.h" + + +/** + This routine calls undi to create the meadia header for the given data buffer. + + @param snp pointer to SNP driver structure + @param MacHeaderPtr address where the media header will be filled in. + @param MacHeaderSize size of the memory at MacHeaderPtr + @param BufferPtr data buffer pointer + @param BufferLength Size of data in the BufferPtr + @param DestinationAddrPtr address of the destination mac address buffer + @param SourceAddrPtr address of the source mac address buffer + @param ProtocolPtr address of the protocol type + + @retval EFI_SUCCESS if successfully completed the undi call + @retval Other error return from undi call. + +**/ +STATIC +EFI_STATUS +pxe_fillheader ( + SNP_DRIVER *snp, + VOID *MacHeaderPtr, + UINTN MacHeaderSize, + VOID *BufferPtr, + UINTN BufferLength, + EFI_MAC_ADDRESS *DestinationAddrPtr, + EFI_MAC_ADDRESS *SourceAddrPtr, + UINT16 *ProtocolPtr + ) +{ + PXE_CPB_FILL_HEADER_FRAGMENTED *cpb; + EFI_STATUS Status; + struct s_v2p *pkt_v2p; + UINT64 TempData; + + cpb = snp->cpb; + if (SourceAddrPtr) { + CopyMem ( + (VOID *) cpb->SrcAddr, + (VOID *) SourceAddrPtr, + snp->mode.HwAddressSize + ); + } else { + CopyMem ( + (VOID *) cpb->SrcAddr, + (VOID *) &(snp->mode.CurrentAddress), + snp->mode.HwAddressSize + ); + } + + CopyMem ( + (VOID *) cpb->DestAddr, + (VOID *) DestinationAddrPtr, + snp->mode.HwAddressSize + ); + + // + // we need to do the byte swapping + // + cpb->Protocol = (UINT16) PXE_SWAP_UINT16 (*ProtocolPtr); + + cpb->PacketLen = (UINT32) (BufferLength); + cpb->MediaHeaderLen = (UINT16) MacHeaderSize; + + cpb->FragCnt = 2; + cpb->reserved = 0; + + cpb->FragDesc[0].FragAddr = (UINT64)(UINTN) MacHeaderPtr; + cpb->FragDesc[0].FragLen = (UINT32) MacHeaderSize; + cpb->FragDesc[1].FragAddr = (UINT64)(UINTN) BufferPtr; + cpb->FragDesc[1].FragLen = (UINT32) BufferLength; + + cpb->FragDesc[0].reserved = cpb->FragDesc[1].reserved = 0; + + if (snp->IsOldUndi) { + TempData = (UINT64) (UINTN) MacHeaderPtr; + if (TempData >= FOUR_GIGABYTES) { + cpb->FragDesc[0].FragAddr = (UINT64) (UINTN) snp->fill_hdr_buf; + cpb->FragDesc[0].FragLen = (UINT32) snp->init_info.MediaHeaderLen; + } + + TempData = (UINT64) (UINTN) (BufferPtr); + if (TempData >= FOUR_GIGABYTES) { + // + // Let the device just read this buffer + // + Status = add_v2p ( + &pkt_v2p, + EfiPciIoOperationBusMasterRead, + BufferPtr, + BufferLength + ); + if (Status != EFI_SUCCESS) { + return Status; + } + // + // give the virtual address to UNDI and it will call back on Virt2Phys + // to get the mapped address, if it needs it + // + cpb->FragDesc[1].FragLen = (UINT32) pkt_v2p->bsize; + } + } + + snp->cdb.OpCode = PXE_OPCODE_FILL_HEADER; + snp->cdb.OpFlags = PXE_OPFLAGS_FILL_HEADER_FRAGMENTED; + + snp->cdb.DBsize = PXE_DBSIZE_NOT_USED; + snp->cdb.DBaddr = PXE_DBADDR_NOT_USED; + + snp->cdb.CPBsize = sizeof (PXE_CPB_FILL_HEADER_FRAGMENTED); + snp->cdb.CPBaddr = (UINT64)(UINTN) cpb; + + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + // + // Issue UNDI command and check result. + // + DEBUG ((EFI_D_NET, "\nsnp->undi.fill_header() ")); + + (*snp->issue_undi32_command) ((UINT64) (UINTN) &snp->cdb); + + if (snp->IsOldUndi) { + TempData = (UINT64) (UINTN) (BufferPtr); + if (TempData >= FOUR_GIGABYTES) { + del_v2p (BufferPtr); + } + // + // if we used the global buffer for header, copy the contents + // + TempData = (UINT64) (UINTN) MacHeaderPtr; + if (TempData >= FOUR_GIGABYTES) { + CopyMem ( + MacHeaderPtr, + snp->fill_hdr_buf, + snp->init_info.MediaHeaderLen + ); + } + } + + switch (snp->cdb.StatCode) { + case PXE_STATCODE_SUCCESS: + return EFI_SUCCESS; + + case PXE_STATCODE_INVALID_PARAMETER: + DEBUG ( + (EFI_D_ERROR, + "\nsnp->undi.fill_header() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + return EFI_INVALID_PARAMETER; + + default: + DEBUG ( + (EFI_D_ERROR, + "\nsnp->undi.fill_header() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + return EFI_DEVICE_ERROR; + } +} + + +/** + This routine calls undi to transmit the given data buffer + + @param snp pointer to SNP driver structure + @param BufferPtr data buffer pointer + @param BufferLength Size of data in the BufferPtr + + @retval EFI_SUCCESS if successfully completed the undi call + @retval Other error return from undi call. + +**/ +STATIC +EFI_STATUS +pxe_transmit ( + SNP_DRIVER *snp, + VOID *BufferPtr, + UINTN BufferLength + ) +{ + PXE_CPB_TRANSMIT *cpb; + EFI_STATUS Status; + struct s_v2p *v2p; + UINT64 TempData; + + cpb = snp->cpb; + cpb->FrameAddr = (UINT64) (UINTN) BufferPtr; + cpb->DataLen = (UINT32) BufferLength; + + TempData = (UINT64) (UINTN) BufferPtr; + if (snp->IsOldUndi && (TempData >= FOUR_GIGABYTES)) { + // + // we need to create a mapping now and give it to the undi when it calls + // the Virt2Phys on this address. + // this is a transmit, just map it for the device to READ + // + Status = add_v2p ( + &v2p, + EfiPciIoOperationBusMasterRead, + BufferPtr, + BufferLength + ); + if (Status != EFI_SUCCESS) { + return Status; + } + + cpb->DataLen = (UINT32) v2p->bsize; + } + + cpb->MediaheaderLen = 0; + cpb->reserved = 0; + + snp->cdb.OpFlags = PXE_OPFLAGS_TRANSMIT_WHOLE; + + snp->cdb.CPBsize = sizeof (PXE_CPB_TRANSMIT); + snp->cdb.CPBaddr = (UINT64)(UINTN) cpb; + + snp->cdb.OpCode = PXE_OPCODE_TRANSMIT; + snp->cdb.DBsize = PXE_DBSIZE_NOT_USED; + snp->cdb.DBaddr = PXE_DBADDR_NOT_USED; + + snp->cdb.StatCode = PXE_STATCODE_INITIALIZE; + snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE; + snp->cdb.IFnum = snp->if_num; + snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST; + + // + // Issue UNDI command and check result. + // + DEBUG ((EFI_D_NET, "\nsnp->undi.transmit() ")); + DEBUG ((EFI_D_NET, "\nsnp->cdb.OpCode == %x", snp->cdb.OpCode)); + DEBUG ((EFI_D_NET, "\nsnp->cdb.CPBaddr == %X", snp->cdb.CPBaddr)); + DEBUG ((EFI_D_NET, "\nsnp->cdb.DBaddr == %X", snp->cdb.DBaddr)); + DEBUG ((EFI_D_NET, "\ncpb->FrameAddr == %X\n", cpb->FrameAddr)); + + (*snp->issue_undi32_command) ((UINT64) (UINTN) &snp->cdb); + + DEBUG ((EFI_D_NET, "\nexit snp->undi.transmit() ")); + DEBUG ((EFI_D_NET, "\nsnp->cdb.StatCode == %r", snp->cdb.StatCode)); + + // + // we will unmap the buffers in get_status call, not here + // + switch (snp->cdb.StatCode) { + case PXE_STATCODE_SUCCESS: + return EFI_SUCCESS; + + case PXE_STATCODE_QUEUE_FULL: + case PXE_STATCODE_BUSY: + Status = EFI_NOT_READY; + break; + + default: + Status = EFI_DEVICE_ERROR; + } + + DEBUG ( + (EFI_D_ERROR, + "\nsnp->undi.transmit() %xh:%xh\n", + snp->cdb.StatFlags, + snp->cdb.StatCode) + ); + + return Status; +} + + +/** + This is the snp interface routine for transmitting a packet. this routine + basically retrieves the snp structure, checks the snp state and calls + pxe_fill_header and pxe_transmit calls to complete the transmission. + + @param this pointer to SNP driver context + @param MacHeaderSize size of the memory at MacHeaderPtr + @param BufferLength Size of data in the BufferPtr + @param BufferPtr data buffer pointer + @param SourceAddrPtr address of the source mac address buffer + @param DestinationAddrPtr address of the destination mac address buffer + @param ProtocolPtr address of the protocol type + + @retval EFI_SUCCESS if successfully completed the undi call + @retval Other error return from undi call. + +**/ +EFI_STATUS +EFIAPI +snp_undi32_transmit ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * this, + IN UINTN MacHeaderSize, + IN UINTN BufferLength, + IN VOID *BufferPtr, + IN EFI_MAC_ADDRESS * SourceAddrPtr OPTIONAL, + IN EFI_MAC_ADDRESS * DestinationAddrPtr OPTIONAL, + IN UINT16 *ProtocolPtr OPTIONAL + ) +{ + SNP_DRIVER *snp; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (this == NULL) { + return EFI_INVALID_PARAMETER; + } + + snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (snp == NULL) { + return EFI_DEVICE_ERROR; + } + + switch (snp->mode.State) { + case EfiSimpleNetworkInitialized: + break; + + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto ON_EXIT; + + default: + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + if (BufferPtr == NULL) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + if (BufferLength < snp->mode.MediaHeaderSize) { + Status = EFI_BUFFER_TOO_SMALL; + goto ON_EXIT; + } + + // + // if the MacHeaderSize is non-zero, we need to fill up the header and for that + // we need the destination address and the protocol + // + if (MacHeaderSize != 0) { + if (MacHeaderSize != snp->mode.MediaHeaderSize || DestinationAddrPtr == 0 || ProtocolPtr == 0) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Status = pxe_fillheader ( + snp, + BufferPtr, + MacHeaderSize, + (UINT8 *) BufferPtr + MacHeaderSize, + BufferLength - MacHeaderSize, + DestinationAddrPtr, + SourceAddrPtr, + ProtocolPtr + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + Status = pxe_transmit (snp, BufferPtr, BufferLength); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/ComponentName.c new file mode 100644 index 0000000000..477af22e1e --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/ComponentName.c @@ -0,0 +1,169 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + ComponentName.c + +Abstract: + + +**/ + +#include "Tcp4Main.h" + +// +// EFI Component Name Functions +// +EFI_STATUS +EFIAPI +TcpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +TcpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +EFI_COMPONENT_NAME_PROTOCOL gTcp4ComponentName = { + TcpComponentNameGetDriverName, + TcpComponentNameGetControllerName, + "eng" +}; + +static EFI_UNICODE_STRING_TABLE mTcpDriverNameTable[] = { + { + "eng", + L"Tcp Network Service Driver" + }, + { + NULL, + NULL + } +}; + +EFI_STATUS +EFIAPI +TcpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +/*++ + +Routine Description: + + Retrieves a Unicode string that is the user readable name of the EFI Driver. + +Arguments: + + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + Language - A pointer to a three character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages + specified in SupportedLanguages. The number of languages + supported by a driver is up to the driver writer. + DriverName - A pointer to the Unicode string to return. This Unicode + string is the name of the driver specified by This in the + language specified by Language. + +Returns: + + EFI_SUCCES - The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - DriverName is NULL. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return LookupUnicodeString ( + Language, + gTcp4ComponentName.SupportedLanguages, + mTcpDriverNameTable, + DriverName + ); +} + +EFI_STATUS +EFIAPI +TcpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +/*++ + +Routine Description: + + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + +Arguments: + + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + ControllerHandle - The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + ChildHandle - The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. + It will be NULL for device drivers. It will also be + NULL for a bus drivers that wish to retrieve the name of + the bus controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a child + controller. + Language - A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller + name that that the caller is requesting, and it must + match one of the languages specified in supported + languages. The number of languages supported by a driver + is up to the driver writer. + ControllerName - A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language + specified by Language from the point of view of the + driver specified by This. + +Returns: + + EFI_SUCCESS - The Unicode string for the user readable name in + the language specified by Language for the driver + specified by This was returned in DriverName. + EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - ControllerName is NULL. + EFI_UNSUPPORTED - The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return EFI_UNSUPPORTED; +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c new file mode 100644 index 0000000000..1f81fc39d3 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c @@ -0,0 +1,1269 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + SockImpl.c + +Abstract: + + +**/ + +#include "SockImpl.h" + +STATIC +UINT32 +SockTcpDataToRcv ( + IN SOCK_BUFFER *SockBuffer, + OUT BOOLEAN *IsOOB, + IN UINT32 BufLen + ); + +STATIC +VOID +SockProcessSndToken ( + IN SOCKET *Sock + ); + +VOID +SockFreeFoo ( + IN EFI_EVENT Event + ) +{ + return ; +} + + +/** + Get the length of the data that can be retrieved from the socket + receive buffer. + + @param SockBuffer Pointer to the socket receive buffer. + @param IsUrg Pointer to a BOOLEAN variable. If TRUE the data is + OOB. + @param BufLen The maximum length of the data buffer to store the + received data in socket layer. + + @return The length of the data can be retreived. + +**/ +STATIC +UINT32 +SockTcpDataToRcv ( + IN SOCK_BUFFER *SockBuffer, + OUT BOOLEAN *IsUrg, + IN UINT32 BufLen + ) +{ + NET_BUF *RcvBufEntry; + UINT32 DataLen; + TCP_RSV_DATA *TcpRsvData; + BOOLEAN Urg; + + ASSERT (SockBuffer && IsUrg && (BufLen > 0)); + + RcvBufEntry = SockBufFirst (SockBuffer); + ASSERT (RcvBufEntry); + + TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData; + + *IsUrg = ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE); + + if (*IsUrg && TcpRsvData->UrgLen < RcvBufEntry->TotalSize) { + + DataLen = NET_MIN (TcpRsvData->UrgLen, BufLen); + + if (DataLen < TcpRsvData->UrgLen) { + TcpRsvData->UrgLen = TcpRsvData->UrgLen - DataLen; + } else { + TcpRsvData->UrgLen = 0; + } + + return DataLen; + + } + + DataLen = RcvBufEntry->TotalSize; + + RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry); + + while ((BufLen > DataLen) && (RcvBufEntry != NULL)) { + + TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData; + + Urg = ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE); + + if (*IsUrg != Urg) { + break; + } + + if (*IsUrg && TcpRsvData->UrgLen < RcvBufEntry->TotalSize) { + + if (TcpRsvData->UrgLen + DataLen < BufLen) { + TcpRsvData->UrgLen = 0; + } else { + TcpRsvData->UrgLen = TcpRsvData->UrgLen - (BufLen - DataLen); + } + + return NET_MIN (TcpRsvData->UrgLen + DataLen, BufLen); + + } + + DataLen += RcvBufEntry->TotalSize; + + RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry); + } + + DataLen = NET_MIN (BufLen, DataLen); + return DataLen; +} + + +/** + Copy data from socket buffer to application provided receive buffer. + + @param Sock Pointer to the socket. + @param TcpRxData Pointer to the application provided receive buffer. + @param RcvdBytes The maximum length of the data can be copied. + @param IsOOB If TURE the data is OOB, else the data is normal. + + @return None. + +**/ +VOID +SockSetTcpRxData ( + IN SOCKET *Sock, + IN VOID *TcpRxData, + IN UINT32 RcvdBytes, + IN BOOLEAN IsOOB + ) +{ + UINT32 Index; + UINT32 CopyBytes; + UINT32 OffSet; + EFI_TCP4_RECEIVE_DATA *RxData; + EFI_TCP4_FRAGMENT_DATA *Fragment; + + RxData = (EFI_TCP4_RECEIVE_DATA *) TcpRxData; + + OffSet = 0; + + ASSERT (RxData->DataLength >= RcvdBytes); + + RxData->DataLength = RcvdBytes; + RxData->UrgentFlag = IsOOB; + + for (Index = 0; (Index < RxData->FragmentCount) && (RcvdBytes > 0); Index++) { + + Fragment = &RxData->FragmentTable[Index]; + CopyBytes = NET_MIN (Fragment->FragmentLength, RcvdBytes); + + NetbufQueCopy ( + Sock->RcvBuffer.DataQueue, + OffSet, + CopyBytes, + Fragment->FragmentBuffer + ); + + Fragment->FragmentLength = CopyBytes; + RcvdBytes -= CopyBytes; + OffSet += CopyBytes; + } +} + + +/** + Get received data from the socket layer to the receive token. + + @param Sock Pointer to the socket. + @param RcvToken Pointer to the application provided receive token. + + @return The length of data received in this token. + +**/ +UINT32 +SockProcessRcvToken ( + IN SOCKET *Sock, + IN SOCK_IO_TOKEN *RcvToken + ) +{ + UINT32 TokenRcvdBytes; + EFI_TCP4_RECEIVE_DATA *RxData; + BOOLEAN IsUrg; + + ASSERT (Sock); + + ASSERT (SOCK_STREAM == Sock->Type); + + RxData = RcvToken->Packet.RxData; + + TokenRcvdBytes = SockTcpDataToRcv ( + &Sock->RcvBuffer, + &IsUrg, + RxData->DataLength + ); + + // + // Copy data from RcvBuffer of socket to user + // provided RxData and set the fields in TCP RxData + // + SockSetTcpRxData (Sock, RxData, TokenRcvdBytes, IsUrg); + + SOCK_TRIM_RCV_BUFF (Sock, TokenRcvdBytes); + SIGNAL_TOKEN (&(RcvToken->Token), EFI_SUCCESS); + + return TokenRcvdBytes; +} + + +/** + Process the TCP send data, buffer the tcp txdata and append + the buffer to socket send buffer,then try to send it. + + @param Sock Pointer to the socket. + @param TcpTxData Pointer to the tcp txdata. + + @retval EFI_SUCCESS The operation is completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limit. + +**/ +EFI_STATUS +SockProcessTcpSndData ( + IN SOCKET *Sock, + IN VOID *TcpTxData + ) +{ + NET_BUF *SndData; + EFI_STATUS Status; + EFI_TCP4_TRANSMIT_DATA *TxData; + + TxData = (EFI_TCP4_TRANSMIT_DATA *) TcpTxData; + + // + // transform this TxData into a NET_BUFFER + // and insert it into Sock->SndBuffer + // + SndData = NetbufFromExt ( + (NET_FRAGMENT *) TxData->FragmentTable, + TxData->FragmentCount, + 0, + 0, + SockFreeFoo, + NULL + ); + + if (NULL == SndData) { + SOCK_DEBUG_ERROR (("SockKProcessSndData: Failed to" + " call NetBufferFromExt\n")); + + return EFI_OUT_OF_RESOURCES; + } + + NetbufQueAppend (Sock->SndBuffer.DataQueue, SndData); + + // + // notify the low layer protocol to handle this send token + // + if (TxData->Urgent) { + Status = Sock->ProtoHandler (Sock, SOCK_SNDURG, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (TxData->Push) { + Status = Sock->ProtoHandler (Sock, SOCK_SNDPUSH, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // low layer protocol should really handle the sending + // process when catching SOCK_SND request + // + Status = Sock->ProtoHandler (Sock, SOCK_SND, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + + +/** + Flush the tokens in the specific token list. + + @param Sock Pointer to the socket. + @param PendingTokenList Pointer to the token list to be flushed. + + @return None. + +**/ +STATIC +VOID +SockFlushPendingToken ( + IN SOCKET *Sock, + IN NET_LIST_ENTRY *PendingTokenList + ) +{ + SOCK_TOKEN *SockToken; + SOCK_COMPLETION_TOKEN *Token; + + ASSERT (Sock && PendingTokenList); + + while (!NetListIsEmpty (PendingTokenList)) { + SockToken = NET_LIST_HEAD ( + PendingTokenList, + SOCK_TOKEN, + TokenList + ); + + Token = SockToken->Token; + SIGNAL_TOKEN (Token, Sock->SockError); + + NetListRemoveEntry (&(SockToken->TokenList)); + NetFreePool (SockToken); + } +} + + +/** + Wake up the connection token while the connection is + successfully established, then try to process any + pending send token. + + @param Sock Pointer to the socket. + + @return None. + +**/ +STATIC +VOID +SockWakeConnToken ( + IN SOCKET *Sock + ) +{ + ASSERT (Sock->ConnectionToken != NULL); + + SIGNAL_TOKEN (Sock->ConnectionToken, EFI_SUCCESS); + Sock->ConnectionToken = NULL; + + // + // check to see if some pending send token existed? + // + SockProcessSndToken (Sock); + return ; +} + + +/** + Wake up the listen token while the connection is + established successfully. + + @param Sock Pointer to the socket. + + @return None. + +**/ +STATIC +VOID +SockWakeListenToken ( + IN SOCKET *Sock + ) +{ + SOCKET *Parent; + SOCK_TOKEN *SockToken; + EFI_TCP4_LISTEN_TOKEN *ListenToken; + + Parent = Sock->Parent; + + ASSERT (Parent && SOCK_IS_LISTENING (Parent) && SOCK_IS_CONNECTED (Sock)); + + if (!NetListIsEmpty (&Parent->ListenTokenList)) { + SockToken = NET_LIST_HEAD ( + &Parent->ListenTokenList, + SOCK_TOKEN, + TokenList + ); + + ListenToken = (EFI_TCP4_LISTEN_TOKEN *) SockToken->Token; + ListenToken->NewChildHandle = Sock->SockHandle; + + SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS); + + NetListRemoveEntry (&SockToken->TokenList); + NetFreePool (SockToken); + + NetListRemoveEntry (&Sock->ConnectionList); + + Parent->ConnCnt--; + SOCK_DEBUG_WARN (("SockWakeListenToken: accept a socket," + "now conncnt is %d", Parent->ConnCnt)); + + Sock->Parent = NULL; + } +} + + +/** + Wake up the receive token while some data is received. + + @param Sock Pointer to the socket. + + @return None. + +**/ +STATIC +VOID +SockWakeRcvToken ( + IN SOCKET *Sock + ) +{ + UINT32 RcvdBytes; + UINT32 TokenRcvdBytes; + SOCK_TOKEN *SockToken; + SOCK_IO_TOKEN *RcvToken; + + ASSERT (Sock->RcvBuffer.DataQueue); + + RcvdBytes = (Sock->RcvBuffer.DataQueue)->BufSize; + + ASSERT (RcvdBytes > 0); + + while (RcvdBytes > 0 && !NetListIsEmpty (&Sock->RcvTokenList)) { + + SockToken = NET_LIST_HEAD ( + &Sock->RcvTokenList, + SOCK_TOKEN, + TokenList + ); + + RcvToken = (SOCK_IO_TOKEN *) SockToken->Token; + TokenRcvdBytes = SockProcessRcvToken (Sock, RcvToken); + + if (0 == TokenRcvdBytes) { + return ; + } + + NetListRemoveEntry (&(SockToken->TokenList)); + NetFreePool (SockToken); + RcvdBytes -= TokenRcvdBytes; + } +} + + +/** + Process the send token. + + @param Sock Pointer to the socket. + + @return None. + +**/ +STATIC +VOID +SockProcessSndToken ( + IN SOCKET *Sock + ) +{ + UINT32 FreeSpace; + SOCK_TOKEN *SockToken; + UINT32 DataLen; + SOCK_IO_TOKEN *SndToken; + EFI_TCP4_TRANSMIT_DATA *TxData; + EFI_STATUS Status; + + ASSERT (Sock && (SOCK_STREAM == Sock->Type)); + + FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF); + + // + // to determine if process a send token using + // socket layer flow control policy + // + while ((FreeSpace >= Sock->SndBuffer.LowWater) && + !NetListIsEmpty (&Sock->SndTokenList)) { + + SockToken = NET_LIST_HEAD ( + &(Sock->SndTokenList), + SOCK_TOKEN, + TokenList + ); + + // + // process this token + // + NetListRemoveEntry (&(SockToken->TokenList)); + NetListInsertTail ( + &(Sock->ProcessingSndTokenList), + &(SockToken->TokenList) + ); + + // + // Proceess it in the light of SockType + // + SndToken = (SOCK_IO_TOKEN *) SockToken->Token; + TxData = SndToken->Packet.TxData; + + DataLen = TxData->DataLength; + Status = SockProcessTcpSndData (Sock, TxData); + + if (EFI_ERROR (Status)) { + goto OnError; + } + + if (DataLen >= FreeSpace) { + FreeSpace = 0; + + } else { + FreeSpace -= DataLen; + + } + } + + return ; + +OnError: + + NetListRemoveEntry (&SockToken->TokenList); + SIGNAL_TOKEN (SockToken->Token, Status); + NetFreePool (SockToken); +} + + +/** + Create a socket with initial data SockInitData. + + @param SockInitData Pointer to the initial data of the socket. + + @return Pointer to the newly created socket. + +**/ +SOCKET * +SockCreate ( + IN SOCK_INIT_DATA *SockInitData + ) +{ + SOCKET *Sock; + SOCKET *Parent; + EFI_STATUS Status; + + ASSERT (SockInitData && SockInitData->ProtoHandler); + ASSERT (SockInitData->Type == SOCK_STREAM); + + Parent = SockInitData->Parent; + + if (Parent && (Parent->ConnCnt == Parent->BackLog)) { + SOCK_DEBUG_ERROR ( + ("SockCreate: Socket parent has " + "reached its connection limit with %d ConnCnt and %d BackLog\n", + Parent->ConnCnt, + Parent->BackLog) + ); + + return NULL; + } + + Sock = NetAllocateZeroPool (sizeof (SOCKET)); + if (NULL == Sock) { + + SOCK_DEBUG_ERROR (("SockCreate: No resource to create a new socket\n")); + return NULL; + } + + NetListInit (&Sock->ConnectionList); + NetListInit (&Sock->ListenTokenList); + NetListInit (&Sock->RcvTokenList); + NetListInit (&Sock->SndTokenList); + NetListInit (&Sock->ProcessingSndTokenList); + + NET_LOCK_INIT (&(Sock->Lock)); + + Sock->SndBuffer.DataQueue = NetbufQueAlloc (); + if (NULL == Sock->SndBuffer.DataQueue) { + SOCK_DEBUG_ERROR (("SockCreate: No resource to allocate" + " SndBuffer for new socket\n")); + + goto OnError; + } + + Sock->RcvBuffer.DataQueue = NetbufQueAlloc (); + if (NULL == Sock->RcvBuffer.DataQueue) { + SOCK_DEBUG_ERROR (("SockCreate: No resource to allocate " + "RcvBuffer for new socket\n")); + + goto OnError; + } + + Sock->Signature = SOCK_SIGNATURE; + + Sock->Parent = Parent; + Sock->BackLog = SockInitData->BackLog; + Sock->ProtoHandler = SockInitData->ProtoHandler; + Sock->SndBuffer.HighWater = SockInitData->SndBufferSize; + Sock->RcvBuffer.HighWater = SockInitData->RcvBufferSize; + Sock->Type = SockInitData->Type; + Sock->DriverBinding = SockInitData->DriverBinding; + Sock->State = SockInitData->State; + + Sock->SockError = EFI_ABORTED; + Sock->SndBuffer.LowWater = SOCK_BUFF_LOW_WATER; + Sock->RcvBuffer.LowWater = SOCK_BUFF_LOW_WATER; + + // + // Install protocol on Sock->SockHandle + // + NetCopyMem ( + &(Sock->NetProtocol.TcpProtocol), + SockInitData->Protocol, + sizeof (EFI_TCP4_PROTOCOL) + ); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Sock->SockHandle, + &gEfiTcp4ProtocolGuid, + &(Sock->NetProtocol.TcpProtocol), + NULL + ); + + if (EFI_ERROR (Status)) { + SOCK_DEBUG_ERROR (("SockCreate: Install TCP protocol in " + "socket failed with %r\n", Status)); + + goto OnError; + } + + if (Parent != NULL) { + ASSERT (Parent->BackLog > 0); + ASSERT (SOCK_IS_LISTENING (Parent)); + + // + // need to add it into Parent->ConnectionList + // if the Parent->ConnCnt < Parent->BackLog + // + Parent->ConnCnt++; + + SOCK_DEBUG_WARN (("SockCreate: Create a new socket and" + "add to parent, now conncnt is %d\n", Parent->ConnCnt)); + + NetListInsertTail (&Parent->ConnectionList, &Sock->ConnectionList); + } + + return Sock; + +OnError: + if (NULL != Sock) { + + if (NULL != Sock->SndBuffer.DataQueue) { + NetbufQueFree (Sock->SndBuffer.DataQueue); + } + + if (NULL != Sock->RcvBuffer.DataQueue) { + NetbufQueFree (Sock->RcvBuffer.DataQueue); + } + + NetFreePool (Sock); + } + + return NULL; +} + + +/** + Destroy a socket. + + @param Sock Pointer to the socket. + + @return None. + +**/ +VOID +SockDestroy ( + IN SOCKET *Sock + ) +{ + VOID *SockProtocol; + EFI_GUID *ProtocolGuid; + EFI_STATUS Status; + + ASSERT (SOCK_STREAM == Sock->Type); + + // + // Flush the completion token buffered + // by sock and rcv, snd buffer + // + if (!SOCK_IS_UNCONFIGURED (Sock)) { + + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + Sock->ConfigureState = SO_UNCONFIGURED; + + } + // + // Destory the RcvBuffer Queue and SendBuffer Queue + // + NetbufQueFree (Sock->RcvBuffer.DataQueue); + NetbufQueFree (Sock->SndBuffer.DataQueue); + + // + // Remove it from parent connection list if needed + // + if (Sock->Parent) { + + NetListRemoveEntry (&(Sock->ConnectionList)); + (Sock->Parent->ConnCnt)--; + + SOCK_DEBUG_WARN (("SockDestory: Delete a unaccepted socket from parent" + "now conncnt is %d\n", Sock->Parent->ConnCnt)); + + Sock->Parent = NULL; + } + + // + // Set the protocol guid and driver binding handle + // in the light of Sock->SockType + // + ProtocolGuid = &gEfiTcp4ProtocolGuid; + + // + // Retrieve the protocol installed on this sock + // + Status = gBS->OpenProtocol ( + Sock->SockHandle, + ProtocolGuid, + &SockProtocol, + Sock->DriverBinding, + Sock->SockHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockDestroy: Open protocol installed " + "on socket failed with %r\n", Status)); + + goto FreeSock; + } + + // + // Uninstall the protocol installed on this sock + // in the light of Sock->SockType + // + gBS->UninstallMultipleProtocolInterfaces ( + Sock->SockHandle, + ProtocolGuid, + SockProtocol, + NULL + ); + +FreeSock: + NetFreePool (Sock); + return ; +} + + +/** + Flush the socket. + + @param Sock Pointer to the socket. + + @return None. + +**/ +VOID +SockConnFlush ( + IN SOCKET *Sock + ) +{ + SOCKET *Child; + + ASSERT (Sock); + + // + // Clear the flag in this socket + // + Sock->Flag = 0; + + // + // Flush the SndBuffer and RcvBuffer of Sock + // + NetbufQueFlush (Sock->SndBuffer.DataQueue); + NetbufQueFlush (Sock->RcvBuffer.DataQueue); + + // + // Signal the pending token + // + if (Sock->ConnectionToken != NULL) { + SIGNAL_TOKEN (Sock->ConnectionToken, Sock->SockError); + Sock->ConnectionToken = NULL; + } + + if (Sock->CloseToken != NULL) { + SIGNAL_TOKEN (Sock->CloseToken, Sock->SockError); + Sock->CloseToken = NULL; + } + + SockFlushPendingToken (Sock, &(Sock->ListenTokenList)); + SockFlushPendingToken (Sock, &(Sock->RcvTokenList)); + SockFlushPendingToken (Sock, &(Sock->SndTokenList)); + SockFlushPendingToken (Sock, &(Sock->ProcessingSndTokenList)); + + // + // Destroy the pending connection, if it is a listening socket + // + if (SOCK_IS_LISTENING (Sock)) { + while (!NetListIsEmpty (&Sock->ConnectionList)) { + Child = NET_LIST_HEAD ( + &Sock->ConnectionList, + SOCKET, + ConnectionList + ); + + SockDestroyChild (Child); + } + + Sock->ConnCnt = 0; + } + + return ; +} + + +/** + Set the state of the socket. + + @param Sock Pointer to the socket. + @param State The new state to be set. + + @return None. + +**/ +VOID +SockSetState ( + IN SOCKET *Sock, + IN SOCK_STATE State + ) +{ + Sock->State = State; +} + + +/** + Clone a new socket including its associated protocol control block. + + @param Sock Pointer to the socket to be cloned. + + @retval * Pointer to the newly cloned socket. If NULL, error + condition occurred. + +**/ +SOCKET * +SockClone ( + IN SOCKET *Sock + ) +{ + SOCKET *ClonedSock; + SOCK_INIT_DATA InitData; + + InitData.BackLog = Sock->BackLog; + InitData.Parent = Sock; + InitData.State = Sock->State; + InitData.ProtoHandler = Sock->ProtoHandler; + InitData.Type = Sock->Type; + InitData.RcvBufferSize = Sock->RcvBuffer.HighWater; + InitData.SndBufferSize = Sock->SndBuffer.HighWater; + InitData.DriverBinding = Sock->DriverBinding; + InitData.Protocol = &(Sock->NetProtocol); + + ClonedSock = SockCreate (&InitData); + + if (NULL == ClonedSock) { + SOCK_DEBUG_ERROR (("SockClone: no resource to create a cloned sock\n")); + return NULL; + } + + NetCopyMem ( + ClonedSock->ProtoReserved, + Sock->ProtoReserved, + PROTO_RESERVED_LEN + ); + + SockSetState (ClonedSock, SO_CONNECTING); + ClonedSock->ConfigureState = Sock->ConfigureState; + + return ClonedSock; +} + + +/** + Called by the low layer protocol to indicate the socket + a connection is established. This function just changes + the socket's state to SO_CONNECTED and signals the token + used for connection establishment. + + @param Sock Pointer to the socket associated with the + established connection. + + @return None. + +**/ +VOID +SockConnEstablished ( + IN SOCKET *Sock + ) +{ + + ASSERT (SO_CONNECTING == Sock->State); + + SockSetState (Sock, SO_CONNECTED); + + if (NULL == Sock->Parent) { + SockWakeConnToken (Sock); + } else { + SockWakeListenToken (Sock); + } + + return ; +} + + +/** + Called by the low layer protocol to indicate the connection + is closed. This function flushes the socket, sets the state + to SO_CLOSED and signals the close token. + + @param Sock Pointer to the socket associated with the closed + connection. + + @return None. + +**/ +VOID +SockConnClosed ( + IN SOCKET *Sock + ) +{ + if (Sock->CloseToken) { + SIGNAL_TOKEN (Sock->CloseToken, EFI_SUCCESS); + Sock->CloseToken = NULL; + } + + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + + if (Sock->Parent != NULL) { + SockDestroyChild (Sock); + } + +} + + +/** + Called by low layer protocol to indicate that some + data is sent or processed. This function trims the + sent data in the socket send buffer, signals the + data token if proper + + @param Sock Pointer to the socket. + @param Count The length of the data processed or sent, in bytes. + + @return None. + +**/ +VOID +SockDataSent ( + IN SOCKET *Sock, + IN UINT32 Count + ) +{ + SOCK_TOKEN *SockToken; + SOCK_COMPLETION_TOKEN *SndToken; + + ASSERT (!NetListIsEmpty (&Sock->ProcessingSndTokenList)); + ASSERT (Count <= (Sock->SndBuffer.DataQueue)->BufSize); + + NetbufQueTrim (Sock->SndBuffer.DataQueue, Count); + + // + // To check if we can signal some snd token in this socket + // + while (Count > 0) { + SockToken = NET_LIST_HEAD ( + &(Sock->ProcessingSndTokenList), + SOCK_TOKEN, + TokenList + ); + + SndToken = SockToken->Token; + + if (SockToken->RemainDataLen <= Count) { + + NetListRemoveEntry (&(SockToken->TokenList)); + SIGNAL_TOKEN (SndToken, EFI_SUCCESS); + Count -= SockToken->RemainDataLen; + NetFreePool (SockToken); + } else { + + SockToken->RemainDataLen -= Count; + Count = 0; + } + } + + // + // to judge if we can process some send token in + // Sock->SndTokenList, if so process those send token + // + SockProcessSndToken (Sock); + return ; +} + + +/** + Called by the low layer protocol to copy some data in socket send + buffer starting from the specific offset to a buffer provided by + the caller. + + @param Sock Pointer to the socket. + @param Offset The start point of the data to be copied. + @param Len The length of the data to be copied. + @param Dest Pointer to the destination to copy the data. + + @return The data size copied. + +**/ +UINT32 +SockGetDataToSend ( + IN SOCKET *Sock, + IN UINT32 Offset, + IN UINT32 Len, + IN UINT8 *Dest + ) +{ + ASSERT (Sock && SOCK_STREAM == Sock->Type); + + return NetbufQueCopy ( + Sock->SndBuffer.DataQueue, + Offset, + Len, + Dest + ); +} + + +/** + Called by the low layer protocol to deliver received data + to socket layer. This function will append the data to the + socket receive buffer, set ther urgent data length and then + check if any receive token can be signaled. + + @param Sock Pointer to the socket. + @param NetBuffer Pointer to the buffer that contains the received + data. + @param UrgLen The length of the urgent data in the received data. + + @return None. + +**/ +VOID +SockDataRcvd ( + IN SOCKET *Sock, + IN NET_BUF *NetBuffer, + IN UINT32 UrgLen + ) +{ + ASSERT (Sock && Sock->RcvBuffer.DataQueue && + UrgLen <= NetBuffer->TotalSize); + + NET_GET_REF (NetBuffer); + + ((TCP_RSV_DATA *) (NetBuffer->ProtoData))->UrgLen = UrgLen; + + NetbufQueAppend (Sock->RcvBuffer.DataQueue, NetBuffer); + + SockWakeRcvToken (Sock); + return ; +} + + +/** + Get the length of the free space of the specific socket buffer. + + @param Sock Pointer to the socket. + @param Which Flag to indicate which socket buffer to check, + either send buffer or receive buffer. + + @return The length of the free space, in bytes. + +**/ +UINT32 +SockGetFreeSpace ( + IN SOCKET *Sock, + IN UINT32 Which + ) +{ + UINT32 BufferCC; + SOCK_BUFFER *SockBuffer; + + ASSERT (Sock && ((SOCK_SND_BUF == Which) || (SOCK_RCV_BUF == Which))); + + if (SOCK_SND_BUF == Which) { + SockBuffer = &(Sock->SndBuffer); + } else { + SockBuffer = &(Sock->RcvBuffer); + } + + BufferCC = (SockBuffer->DataQueue)->BufSize; + + if (BufferCC >= SockBuffer->HighWater) { + + return 0; + } + + return SockBuffer->HighWater - BufferCC; +} + + +/** + Signal the receive token with the specific error or + set socket error code after error is received. + + @param Sock Pointer to the socket. + @param Error The error code received. + + @return None. + +**/ +VOID +SockRcvdErr ( + IN SOCKET *Sock, + IN EFI_STATUS Error + ) +{ + SOCK_TOKEN *SockToken; + + if (!NetListIsEmpty (&Sock->RcvTokenList)) { + + SockToken = NET_LIST_HEAD ( + &Sock->RcvTokenList, + SOCK_TOKEN, + TokenList + ); + + NetListRemoveEntry (&SockToken->TokenList); + + SIGNAL_TOKEN (SockToken->Token, Error); + + NetFreePool (SockToken); + } else { + + SOCK_ERROR (Sock, Error); + } +} + + +/** + Called by the low layer protocol to indicate that there + will be no more data from the communication peer. This + function set the socket's state to SO_NO_MORE_DATA and + signal all queued IO tokens with the error status + EFI_CONNECTION_FIN. + + @param Sock Pointer to the socket. + + @return None. + +**/ +VOID +SockNoMoreData ( + IN SOCKET *Sock + ) +{ + EFI_STATUS Err; + + SOCK_NO_MORE_DATA (Sock); + + if (!NetListIsEmpty (&Sock->RcvTokenList)) { + + ASSERT (0 == GET_RCV_DATASIZE (Sock)); + + Err = Sock->SockError; + + SOCK_ERROR (Sock, EFI_CONNECTION_FIN); + + SockFlushPendingToken (Sock, &Sock->RcvTokenList); + + SOCK_ERROR (Sock, Err); + + } + +} + + +/** + Get the first buffer block in the specific socket buffer. + + @param Sockbuf Pointer to the socket buffer. + + @return Pointer to the first buffer in the queue. NULL if the queue is empty. + +**/ +NET_BUF * +SockBufFirst ( + IN SOCK_BUFFER *Sockbuf + ) +{ + NET_LIST_ENTRY *NetbufList; + + NetbufList = &(Sockbuf->DataQueue->BufList); + + if (NetListIsEmpty (NetbufList)) { + return NULL; + } + + return NET_LIST_HEAD (NetbufList, NET_BUF, List); +} + + +/** + Get the next buffer block in the specific socket buffer. + + @param Sockbuf Pointer to the socket buffer. + @param SockEntry Pointer to the buffer block prior to the required + one. + + @return Pointer to the buffer block next to SockEntry. NULL if SockEntry is the tail or head entry. + +**/ +NET_BUF * +SockBufNext ( + IN SOCK_BUFFER *Sockbuf, + IN NET_BUF *SockEntry + ) +{ + NET_LIST_ENTRY *NetbufList; + + NetbufList = &(Sockbuf->DataQueue->BufList); + + if ((SockEntry->List.ForwardLink == NetbufList) || + (SockEntry->List.BackLink == &SockEntry->List) || + (SockEntry->List.ForwardLink == &SockEntry->List) + ) { + + return NULL; + } + + return NET_LIST_USER_STRUCT (SockEntry->List.ForwardLink, NET_BUF, List); +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h new file mode 100644 index 0000000000..69a1ac6222 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h @@ -0,0 +1,84 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + SockImpl.h + +Abstract: + + +**/ + +#ifndef _SOCK_IMPL_H_ +#define _SOCK_IMPL_H_ + +#include "Socket.h" + +#define SOCK_DEBUG_ERROR(PrintArg) NET_DEBUG_ERROR("Sock", PrintArg) +#define SOCK_DEBUG_WARN(PrintArg) NET_DEBUG_WARNING("Sock", PrintArg) +#define SOCK_DEBUG_TRACE(PrintArg) NET_DEBUG_TRACE("Sock", PrintArg) + +#define SOCK_TRIM_RCV_BUFF(Sock, Len) \ + (NetbufQueTrim ((Sock)->RcvBuffer.DataQueue, (Len))) + +#define SIGNAL_TOKEN(Token, TokenStatus) \ + do { \ + (Token)->Status = (TokenStatus); \ + gBS->SignalEvent ((Token)->Event); \ + } while (0) + +#define SOCK_HEADER_SPACE (60 + 60 + 72) + +// +// Supporting function for both SockImpl and SockInterface +// +VOID +SockFreeFoo ( + IN EFI_EVENT Event + ); + +EFI_STATUS +SockProcessTcpSndData ( + IN SOCKET *Sock, + IN VOID *TcpTxData + ); + +VOID +SockSetTcpRxData ( + IN SOCKET *Sock, + IN VOID *TcpRxData, + IN UINT32 RcvdBytes, + IN BOOLEAN IsOOB + ); + +UINT32 +SockProcessRcvToken ( + IN SOCKET *Sock, + IN SOCK_IO_TOKEN *RcvToken + ); + +VOID +SockConnFlush ( + IN SOCKET *Sock + ); + +SOCKET * +SockCreate ( + IN SOCK_INIT_DATA *SockInitData + ); + +VOID +SockDestroy ( + IN SOCKET *Sock + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c new file mode 100644 index 0000000000..0b71996e14 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c @@ -0,0 +1,980 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + SockInterface.c + +Abstract: + + +**/ + + +#include "SockImpl.h" + + +/** + Check whether the Event is in the List. + + @param List Pointer to the token list to be searched. + @param Event The event to be checked. + + @retval BOOLEAN If TRUE, the specific Event exists in the List. If + FALSE, the specific Event is not in the List. + +**/ +STATIC +BOOLEAN +SockTokenExistedInList ( + IN NET_LIST_ENTRY *List, + IN EFI_EVENT Event + ) +{ + NET_LIST_ENTRY *ListEntry; + SOCK_TOKEN *SockToken; + + NET_LIST_FOR_EACH (ListEntry, List) { + SockToken = NET_LIST_USER_STRUCT ( + ListEntry, + SOCK_TOKEN, + TokenList + ); + + if (Event == SockToken->Token->Event) { + return TRUE; + } + } + + return FALSE; +} + + +/** + Call SockTokenExistedInList() to check whether the Event is + in the related socket's lists. + + @param Sock Pointer to the instance's socket. + @param Event The event to be checked. + + @return The specific Event exists in one of socket's lists or not. + +**/ +BOOLEAN +SockTokenExisted ( + IN SOCKET *Sock, + IN EFI_EVENT Event + ) +{ + + if (SockTokenExistedInList (&Sock->SndTokenList, Event) || + SockTokenExistedInList (&Sock->ProcessingSndTokenList, Event) || + SockTokenExistedInList (&Sock->RcvTokenList, Event) || + SockTokenExistedInList (&Sock->ListenTokenList, Event) + ) { + + return TRUE; + } + + if ((Sock->ConnectionToken != NULL) && + (Sock->ConnectionToken->Event == Event)) { + + return TRUE; + } + + if ((Sock->CloseToken != NULL) && (Sock->CloseToken->Event == Event)) { + return TRUE; + } + + return FALSE; +} + + +/** + Buffer a token into the specific list of socket Sock. + + @param Sock Pointer to the instance's socket. + @param List Pointer to the list to store the token. + @param Token Pointer to the token to be buffered. + @param DataLen The data length of the buffer contained in Token. + + @return Pointer to the token that wraps Token. If NULL, error condition occurred. + +**/ +SOCK_TOKEN * +SockBufferToken ( + IN SOCKET *Sock, + IN NET_LIST_ENTRY *List, + IN VOID *Token, + IN UINT32 DataLen + ) +{ + SOCK_TOKEN *SockToken; + + SockToken = NetAllocatePool (sizeof (SOCK_TOKEN)); + if (NULL == SockToken) { + + SOCK_DEBUG_ERROR (("SockBufferIOToken: No Memory " + "to allocate SockToken\n")); + + return NULL; + } + + SockToken->Sock = Sock; + SockToken->Token = (SOCK_COMPLETION_TOKEN *) Token; + SockToken->RemainDataLen = DataLen; + NetListInsertTail (List, &SockToken->TokenList); + + return SockToken; +} + + +/** + Destory the socket Sock and its associated protocol control block. + + @param Sock The socket to be destroyed. + + @retval EFI_SUCCESS The socket Sock is destroyed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + +**/ +EFI_STATUS +SockDestroyChild ( + IN SOCKET *Sock + ) +{ + EFI_STATUS Status; + + ASSERT (Sock && Sock->ProtoHandler); + + if (Sock->IsDestroyed) { + return EFI_SUCCESS; + } + + Sock->IsDestroyed = TRUE; + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockDestroyChild: Get the lock to " + "access socket failed with %r\n", Status)); + + return EFI_ACCESS_DENIED; + } + + // + // force protocol layer to detach the PCB + // + Status = Sock->ProtoHandler (Sock, SOCK_DETACH, NULL); + + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockDestroyChild: Protocol detach socket" + " failed with %r\n", Status)); + + Sock->IsDestroyed = FALSE; + } else if (SOCK_IS_CONFIGURED (Sock)) { + + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + + Sock->ConfigureState = SO_UNCONFIGURED; + } + + NET_UNLOCK (&(Sock->Lock)); + + if (EFI_ERROR (Status)) { + return Status; + } + + SockDestroy (Sock); + return EFI_SUCCESS; +} + + +/** + Create a socket and its associated protocol control block + with the intial data SockInitData and protocol specific + data ProtoData. + + @param SockInitData Inital data to setting the socket. + @param ProtoData Pointer to the protocol specific data. + @param Len Length of the protocol specific data. + + @return Pointer to the newly created socket. If NULL, error condition occured. + +**/ +SOCKET * +SockCreateChild ( + IN SOCK_INIT_DATA *SockInitData, + IN VOID *ProtoData, + IN UINT32 Len + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + ASSERT (ProtoData && (Len <= PROTO_RESERVED_LEN)); + + // + // create a new socket + // + Sock = SockCreate (SockInitData); + if (NULL == Sock) { + + SOCK_DEBUG_ERROR (("SockCreateChild: No resource to " + "create a new socket\n")); + + return NULL; + } + + // + // Open the + // + + // + // copy the protodata into socket + // + NetCopyMem (Sock->ProtoReserved, ProtoData, Len); + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockCreateChild: Get the lock to " + "access socket failed with %r\n", Status)); + + SockDestroy (Sock); + return NULL; + } + // + // inform the protocol layer to attach the socket + // with a new protocol control block + // + Status = Sock->ProtoHandler (Sock, SOCK_ATTACH, NULL); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockCreateChild: Protocol failed to" + " attach a socket with %r\n", Status)); + + SockDestroy (Sock); + Sock = NULL; + } + + NET_UNLOCK (&(Sock->Lock)); + return Sock; +} + + +/** + Configure the specific socket Sock using configuration data + ConfigData. + + @param Sock Pointer to the socket to be configured. + @param ConfigData Pointer to the configuration data. + + @retval EFI_SUCCESS The socket is configured successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket or the + socket is already configured. + +**/ +EFI_STATUS +SockConfigure ( + IN SOCKET *Sock, + IN VOID *ConfigData + ) +{ + EFI_STATUS Status; + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockConfigure: Get the access for " + "socket failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_CONFIGURED (Sock)) { + Status = EFI_ACCESS_DENIED; + goto OnExit; + } + + ASSERT (Sock->State == SO_CLOSED); + + Status = Sock->ProtoHandler (Sock, SOCK_CONFIGURE, ConfigData); + +OnExit: + NET_UNLOCK (&(Sock->Lock)); + + return Status; +} + + +/** + Initiate a connection establishment process. + + @param Sock Pointer to the socket to initiate the initate the + connection. + @param Token Pointer to the token used for the connection + operation. + + @retval EFI_SUCCESS The connection is initialized successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not configured to + be an active one, or the token is already in one of + this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockConnect ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockConnect: Get the access for " + "socket failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto OnExit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + + Status = EFI_NOT_STARTED; + goto OnExit; + } + + if (!SOCK_IS_CLOSED (Sock) || !SOCK_IS_CONFIGURED_ACTIVE (Sock)) { + + Status = EFI_ACCESS_DENIED; + goto OnExit; + } + + Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event; + + if (SockTokenExisted (Sock, Event)) { + + Status = EFI_ACCESS_DENIED; + goto OnExit; + } + + Sock->ConnectionToken = (SOCK_COMPLETION_TOKEN *) Token; + SockSetState (Sock, SO_CONNECTING); + Status = Sock->ProtoHandler (Sock, SOCK_CONNECT, NULL); + +OnExit: + NET_UNLOCK (&(Sock->Lock)); + return Status; +} + + +/** + Issue a listen token to get an existed connected network instance + or wait for a connection if there is none. + + @param Sock Pointer to the socket to accept connections. + @param Token The token to accept a connection. + + @retval EFI_SUCCESS Either a connection is accpeted or the Token is + buffered for further acception. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not configured to + be a passive one, or the token is already in one of + this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the Token due to memory limit. + +**/ +EFI_STATUS +SockAccept ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + EFI_TCP4_LISTEN_TOKEN *ListenToken; + NET_LIST_ENTRY *ListEntry; + EFI_STATUS Status; + SOCKET *Socket; + EFI_EVENT Event; + + ASSERT (SOCK_STREAM == Sock->Type); + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockAccept: Get the access for socket" + " failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (!SOCK_IS_LISTENING (Sock)) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event; + + if (SockTokenExisted (Sock, Event)) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + ListenToken = (EFI_TCP4_LISTEN_TOKEN *) Token; + + // + // Check if a connection has already in this Sock->ConnectionList + // + NET_LIST_FOR_EACH (ListEntry, &Sock->ConnectionList) { + + Socket = NET_LIST_USER_STRUCT (ListEntry, SOCKET, ConnectionList); + + if (SOCK_IS_CONNECTED (Socket)) { + ListenToken->NewChildHandle = Socket->SockHandle; + SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS); + + NetListRemoveEntry (ListEntry); + + ASSERT (Socket->Parent); + + Socket->Parent->ConnCnt--; + + SOCK_DEBUG_WARN (("SockAccept: Accept a socket," + "now conncount is %d", Socket->Parent->ConnCnt) + ); + Socket->Parent = NULL; + + goto Exit; + } + } + + // + // Buffer this token for latter incoming connection request + // + if (NULL == SockBufferToken (Sock, &(Sock->ListenTokenList), Token, 0)) { + + Status = EFI_OUT_OF_RESOURCES; + } + +Exit: + NET_UNLOCK (&(Sock->Lock)); + + return Status; +} + + +/** + Issue a token with data to the socket to send out. + + @param Sock Pointer to the socket to process the token with + data. + @param Token The token with data that needs to send out. + + @retval EFI_SUCCESS The token is processed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limit. + +**/ +EFI_STATUS +SockSend ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + SOCK_IO_TOKEN *SndToken; + EFI_EVENT Event; + UINT32 FreeSpace; + EFI_TCP4_TRANSMIT_DATA *TxData; + EFI_STATUS Status; + SOCK_TOKEN *SockToken; + UINT32 DataLen; + + ASSERT (SOCK_STREAM == Sock->Type); + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockSend: Get the access for socket" + " failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + SndToken = (SOCK_IO_TOKEN *) Token; + TxData = (EFI_TCP4_TRANSMIT_DATA *) SndToken->Packet.TxData; + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (!(SOCK_IS_CONNECTING (Sock) || SOCK_IS_CONNECTED (Sock))) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + // + // check if a token is already in the token buffer + // + Event = SndToken->Token.Event; + + if (SockTokenExisted (Sock, Event)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + DataLen = TxData->DataLength; + + // + // process this sending token now or buffer it only? + // + FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF); + + if ((FreeSpace < Sock->SndBuffer.LowWater) || !SOCK_IS_CONNECTED (Sock)) { + + SockToken = SockBufferToken ( + Sock, + &Sock->SndTokenList, + SndToken, + DataLen + ); + + if (NULL == SockToken) { + Status = EFI_OUT_OF_RESOURCES; + } + } else { + + SockToken = SockBufferToken ( + Sock, + &Sock->ProcessingSndTokenList, + SndToken, + DataLen + ); + + if (NULL == SockToken) { + SOCK_DEBUG_ERROR (("SockSend: Failed to buffer IO token into" + " socket processing SndToken List\n", Status)); + + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + Status = SockProcessTcpSndData (Sock, TxData); + + if (EFI_ERROR (Status)) { + SOCK_DEBUG_ERROR (("SockSend: Failed to process " + "Snd Data\n", Status)); + + NetListRemoveEntry (&(SockToken->TokenList)); + NetFreePool (SockToken); + } + } + +Exit: + NET_UNLOCK (&(Sock->Lock)); + return Status; +} + + +/** + Issue a token to get data from the socket. + + @param Sock Pointer to the socket to get data from. + @param Token The token to store the received data from the + socket. + + @retval EFI_SUCCESS The token is processed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_CONNECTION_FIN The connection is closed and there is no more data. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limit. + +**/ +EFI_STATUS +SockRcv ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + SOCK_IO_TOKEN *RcvToken; + UINT32 RcvdBytes; + EFI_STATUS Status; + EFI_EVENT Event; + + ASSERT (SOCK_STREAM == Sock->Type); + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockRcv: Get the access for socket" + " failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (!(SOCK_IS_CONNECTED (Sock) || SOCK_IS_CONNECTING (Sock))) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + RcvToken = (SOCK_IO_TOKEN *) Token; + + // + // check if a token is already in the token buffer of this socket + // + Event = RcvToken->Token.Event; + if (SockTokenExisted (Sock, Event)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + RcvToken = (SOCK_IO_TOKEN *) Token; + RcvdBytes = GET_RCV_DATASIZE (Sock); + + // + // check whether an error has happened before + // + if (EFI_ABORTED != Sock->SockError) { + + SIGNAL_TOKEN (&(RcvToken->Token), Sock->SockError); + Sock->SockError = EFI_ABORTED; + goto Exit; + } + + // + // check whether can not receive and there is no any + // data buffered in Sock->RcvBuffer + // + if (SOCK_IS_NO_MORE_DATA (Sock) && (0 == RcvdBytes)) { + + Status = EFI_CONNECTION_FIN; + goto Exit; + } + + if (RcvdBytes != 0) { + Status = SockProcessRcvToken (Sock, RcvToken); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_CONSUMED, NULL); + } else { + + if (NULL == SockBufferToken (Sock, &Sock->RcvTokenList, RcvToken, 0)) { + Status = EFI_OUT_OF_RESOURCES; + } + } + +Exit: + NET_UNLOCK (&(Sock->Lock)); + return Status; +} + + +/** + Reset the socket and its associated protocol control block. + + @param Sock Pointer to the socket to be flushed. + + @retval EFI_SUCCESS The socket is flushed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + +**/ +EFI_STATUS +SockFlush ( + IN SOCKET *Sock + ) +{ + EFI_STATUS Status; + + ASSERT (SOCK_STREAM == Sock->Type); + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockFlush: Get the access for socket" + " failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (!SOCK_IS_CONFIGURED (Sock)) { + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_FLUSH, NULL); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockFlush: Protocol failed handling" + " SOCK_FLUSH with %r", Status)); + + goto Exit; + } + + SOCK_ERROR (Sock, EFI_ABORTED); + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + + Sock->ConfigureState = SO_UNCONFIGURED; + +Exit: + NET_UNLOCK (&(Sock->Lock)); + return Status; +} + + +/** + Close or abort the socket associated connection. + + @param Sock Pointer to the socket of the connection to close or + abort. + @param Token The token for close operation. + @param OnAbort TRUE for aborting the connection, FALSE to close it. + + @retval EFI_SUCCESS The close or abort operation is initialized + successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockClose ( + IN SOCKET *Sock, + IN VOID *Token, + IN BOOLEAN OnAbort + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + + ASSERT (SOCK_STREAM == Sock->Type); + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + SOCK_DEBUG_ERROR (("SockClose: Get the access for socket" + " failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (SOCK_IS_DISCONNECTING (Sock)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event; + + if (SockTokenExisted (Sock, Event)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + Sock->CloseToken = Token; + SockSetState (Sock, SO_DISCONNECTING); + + if (OnAbort) { + Status = Sock->ProtoHandler (Sock, SOCK_ABORT, NULL); + } else { + Status = Sock->ProtoHandler (Sock, SOCK_CLOSE, NULL); + } + +Exit: + NET_UNLOCK (&(Sock->Lock)); + return Status; +} + + +/** + Get the mode data of the low layer protocol. + + @param Sock Pointer to the socket to get mode data from. + @param Mode Pointer to the data to store the low layer mode + information. + + @retval EFI_SUCCESS The mode data is got successfully. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockGetMode ( + IN SOCKET *Sock, + IN VOID *Mode + ) +{ + return Sock->ProtoHandler (Sock, SOCK_MODE, Mode); +} + + +/** + Configure the low level protocol to join a multicast group for + this socket's connection. + + @param Sock Pointer to the socket of the connection to join the + specific multicast group. + @param GroupInfo Pointer to the multicast group info. + + @retval EFI_SUCCESS The configuration is done successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockGroup ( + IN SOCKET *Sock, + IN VOID *GroupInfo + ) +{ + EFI_STATUS Status; + + Status = NET_TRYLOCK (&(Sock->Lock)); + + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockGroup: Get the access for socket" + " failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_GROUP, GroupInfo); + +Exit: + NET_UNLOCK (&(Sock->Lock)); + return Status; +} + + +/** + Add or remove route information in IP route table associated + with this socket. + + @param Sock Pointer to the socket associated with the IP route + table to operate on. + @param RouteInfo Pointer to the route information to be processed. + + @retval EFI_SUCCESS The route table is updated successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockRoute ( + IN SOCKET *Sock, + IN VOID *RouteInfo + ) +{ + EFI_STATUS Status; + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + SOCK_DEBUG_ERROR (("SockRoute: Get the access for socket" + " failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_ROUTE, RouteInfo); + +Exit: + NET_UNLOCK (&(Sock->Lock)); + return Status; +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h new file mode 100644 index 0000000000..f3978541fb --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h @@ -0,0 +1,518 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Socket.h + +Abstract: + + +**/ + +#ifndef _SOCKET_H_ +#define _SOCKET_H_ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOCK_SND_BUF 0 +#define SOCK_RCV_BUF 1 + +#define SOCK_BUFF_LOW_WATER 2 * 1024 +#define SOCK_RCV_BUFF_SIZE 8 * 1024 +#define SOCK_SND_BUFF_SIZE 8 * 1024 +#define SOCK_BACKLOG 5 + +#define PROTO_RESERVED_LEN 20 + +#define SO_NO_MORE_DATA 0x0001 + +// +// +// +// When a socket is created it enters into SO_UNCONFIGURED, +// no actions can be taken on this socket, only after calling +// SockConfigure. The state transition diagram of socket is +// as following: +// +// SO_UNCONFIGURED --- SO_CONFIGURED --- SO_CONNECTING +// ^ | | +// | ---> SO_LISTENING | +// | | +// |------------------SO_DISCONNECTING<-- SO_CONNECTED +// +// A passive socket can only go into SO_LISTENING and +// SO_UNCONFIGURED state. SO_XXXING state is a middle state +// when a socket is undergoing a protocol procedure such +// as requesting a TCP connection. +// +// +// +typedef enum { + SO_CLOSED = 0, + SO_LISTENING, + SO_CONNECTING, + SO_CONNECTED, + SO_DISCONNECTING +} SOCK_STATE; + +typedef enum { + SO_UNCONFIGURED = 0, + SO_CONFIGURED_ACTIVE, + SO_CONFIGURED_PASSIVE, + SO_NO_MAPPING +} SOCK_CONFIGURE_STATE; + +#define SOCK_NO_MORE_DATA(Sock) ((Sock)->Flag |= SO_NO_MORE_DATA) + +#define SOCK_IS_UNCONFIGURED(Sock) ((Sock)->ConfigureState == SO_UNCONFIGURED) + +#define SOCK_IS_CONFIGURED(Sock) \ + (((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) || \ + ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE)) + +#define SOCK_IS_CONFIGURED_ACTIVE(Sock) \ + ((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) + +#define SOCK_IS_CONNECTED_PASSIVE(Sock) \ + ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE) + +#define SOCK_IS_NO_MAPPING(Sock) \ + ((Sock)->ConfigureState == SO_NO_MAPPING) + +#define SOCK_IS_CLOSED(Sock) ((Sock)->State == SO_CLOSED) + +#define SOCK_IS_LISTENING(Sock) ((Sock)->State == SO_LISTENING) + +#define SOCK_IS_CONNECTING(Sock) ((Sock)->State == SO_CONNECTING) + +#define SOCK_IS_CONNECTED(Sock) ((Sock)->State == SO_CONNECTED) + +#define SOCK_IS_DISCONNECTING(Sock) ((Sock)->State == SO_DISCONNECTING) + +#define SOCK_IS_NO_MORE_DATA(Sock) (0 != ((Sock)->Flag & SO_NO_MORE_DATA)) + +#define SOCK_SIGNATURE EFI_SIGNATURE_32 ('S', 'O', 'C', 'K') + +#define SOCK_FROM_THIS(a) CR ((a), SOCKET, NetProtocol, SOCK_SIGNATURE) + +#define SET_RCV_BUFFSIZE(Sock, Size) ((Sock)->RcvBuffer.HighWater = (Size)) + +#define GET_RCV_BUFFSIZE(Sock) ((Sock)->RcvBuffer.HighWater) + +#define GET_RCV_DATASIZE(Sock) (((Sock)->RcvBuffer.DataQueue)->BufSize) + +#define SET_SND_BUFFSIZE(Sock, Size) ((Sock)->SndBuffer.HighWater = (Size)) + +#define GET_SND_BUFFSIZE(Sock) ((Sock)->SndBuffer.HighWater) + +#define GET_SND_DATASIZE(Sock) (((Sock)->SndBuffer.DataQueue)->BufSize) + +#define SET_BACKLOG(Sock, Value) ((Sock)->BackLog = (Value)) + +#define GET_BACKLOG(Sock) ((Sock)->BackLog) + +#define SOCK_ERROR(Sock, Error) ((Sock)->SockError = (Error)) + +#define SND_BUF_HDR_LEN(Sock) \ + ((SockBufFirst (&((Sock)->SndBuffer)))->TotalSize) + +#define RCV_BUF_HDR_LEN(Sock) \ + ((SockBufFirst (&((Sock)->RcvBuffer)))->TotalSize) + +#define SOCK_FROM_TOKEN(Token) (((SOCK_TOKEN *) (Token))->Sock) + +#define PROTO_TOKEN_FORM_SOCK(SockToken, Type) \ + ((Type *) (((SOCK_TOKEN *) (SockToken))->Token)) + +typedef struct _SOCKET SOCKET; + +typedef struct _SOCK_COMPLETION_TOKEN { + EFI_EVENT Event; + EFI_STATUS Status; +} SOCK_COMPLETION_TOKEN; + +typedef struct _SOCK_IO_TOKEN { + SOCK_COMPLETION_TOKEN Token; + union { + VOID *RxData; + VOID *TxData; + } Packet; +} SOCK_IO_TOKEN; + +// +// the request issued from socket layer to protocol layer +// +typedef enum { + SOCK_ATTACH, // attach current socket to a new PCB + SOCK_DETACH, // detach current socket from the PCB + SOCK_CONFIGURE, // configure attached PCB + SOCK_FLUSH, // flush attached PCB + SOCK_SND, // need protocol to send something + SOCK_SNDPUSH, // need protocol to send pushed data + SOCK_SNDURG, // need protocol to send urgent data + SOCK_CONSUMED, // application has retrieved data from socket + SOCK_CONNECT, // need to connect to a peer + SOCK_CLOSE, // need to close the protocol process + SOCK_ABORT, // need to reset the protocol process + SOCK_POLL, // need to poll to the protocol layer + SOCK_ROUTE, // need to add a route information + SOCK_MODE, // need to get the mode data of the protocol + SOCK_GROUP // need to join a mcast group +} SOCK_REQUEST; + +// +// the socket type +// +typedef enum { + SOCK_DGRAM, // this socket providing datagram service + SOCK_STREAM // this socket providing stream service +} SOCK_TYPE; + +// +// the handler of protocol for request from socket +// +typedef +EFI_STATUS +(*SOCK_PROTO_HANDLER) ( + IN SOCKET * Socket, // the socket issuing the request to protocol + IN SOCK_REQUEST Request, // the request issued by socket + IN VOID *RequestData // the request related data + ); + +// +// the buffer structure of rcvd data and send data used by socket +// +typedef struct _SOCK_BUFFER { + UINT32 HighWater; // the buffersize upper limit of sock_buffer + UINT32 LowWater; // the low warter mark of sock_buffer + NET_BUF_QUEUE *DataQueue; // the queue to buffer data +} SOCK_BUFFER; + +// +// the initialize data for create a new socket +// +typedef struct _SOCK_INIT_DATA { + SOCK_TYPE Type; + SOCK_STATE State; + + SOCKET *Parent; // the parent of this socket + UINT32 BackLog; // the connection limit for listening socket + UINT32 SndBufferSize; // the high warter mark of send buffer + UINT32 RcvBufferSize; // the high warter mark of receive buffer + VOID *Protocol; // the pointer to protocol function template + // wanted to install on socket + + SOCK_PROTO_HANDLER ProtoHandler; + + EFI_HANDLE DriverBinding; // the driver binding handle +} SOCK_INIT_DATA; + +// +// socket provided oprerations for low layer protocol +// + +// +// socket provided operations for user interface +// +VOID +SockSetState ( + IN SOCKET *Sock, + IN SOCK_STATE State + ); + +// +// when the connection establishment process for a Sock +// is finished low layer protocol calling this function +// to notify socket layer +// +VOID +SockConnEstablished ( + IN SOCKET *Sock + ); + +VOID +SockConnClosed ( + IN SOCKET *Sock + ); + +// +// called by low layer protocol to trim send buffer of +// Sock, when Count data is sent out completely +// +VOID +SockDataSent ( + IN SOCKET *Sock, + IN UINT32 Count + ); + +// +// called by low layer protocol to get Len of data from +// socket to send and copy it in Dest +// +UINT32 +SockGetDataToSend ( + IN SOCKET *Sock, + IN UINT32 Offset, + IN UINT32 Len, + IN UINT8 *Dest + ); + +// +// called by low layer protocol to notify socket no more data can be +// received +// +VOID +SockNoMoreData ( + IN SOCKET *Sock + ); + +// +// called by low layer protocol to append a NetBuffer +// to rcv buffer of sock +// +VOID +SockDataRcvd ( + IN SOCKET *Sock, + IN NET_BUF *NetBuffer, + IN UINT32 UrgLen + ); + +UINT32 +SockGetFreeSpace ( + IN SOCKET *Sock, + IN UINT32 Which + ); + +SOCKET * +SockClone ( + IN SOCKET *Sock + ); + +VOID +SockRcvdErr ( + IN SOCKET *Sock, + IN EFI_STATUS Error + ); + +// +// the socket structure representing a network service access point +// +typedef struct _SOCKET { + + // + // socket description information + // + UINT32 Signature; + EFI_HANDLE SockHandle; // the virtual handle of the socket + EFI_HANDLE DriverBinding; // socket't driver binding protocol + SOCK_CONFIGURE_STATE ConfigureState; + SOCK_TYPE Type; + SOCK_STATE State; + UINT16 Flag; + NET_LOCK Lock; // the lock of socket + SOCK_BUFFER SndBuffer; // send buffer of application's data + SOCK_BUFFER RcvBuffer; // receive buffer of received data + EFI_STATUS SockError; // the error returned by low layer protocol + BOOLEAN IsDestroyed; + + // + // fields used to manage the connection request + // + UINT32 BackLog; // the limit of connection to this socket + UINT32 ConnCnt; // the current count of connections to it + SOCKET *Parent; // listening parent that accept the connection + NET_LIST_ENTRY ConnectionList; // the connections maintained by this socket + // + // the queue to buffer application's asynchronous token + // + NET_LIST_ENTRY ListenTokenList; + NET_LIST_ENTRY RcvTokenList; + NET_LIST_ENTRY SndTokenList; + NET_LIST_ENTRY ProcessingSndTokenList; + + SOCK_COMPLETION_TOKEN *ConnectionToken; // app's token to signal if connected + SOCK_COMPLETION_TOKEN *CloseToken; // app's token to signal if closed + + // + // interface for low level protocol + // + SOCK_PROTO_HANDLER ProtoHandler; // the request handler of protocol + UINT8 ProtoReserved[PROTO_RESERVED_LEN]; // Data fields reserved for protocol + union { + EFI_TCP4_PROTOCOL TcpProtocol; + EFI_UDP4_PROTOCOL UdpProtocol; + } NetProtocol; +} SOCKET; + +// +// the token structure buffered in socket layer +// +typedef struct _SOCK_TOKEN { + NET_LIST_ENTRY TokenList; // the entry to add in the token list + SOCK_COMPLETION_TOKEN *Token; // The application's token + UINT32 RemainDataLen; // unprocessed data length + SOCKET *Sock; // the poninter to the socket this token + // belongs to +} SOCK_TOKEN; + +// +// reserved data to access the NET_BUF delivered by UDP driver +// +typedef struct _UDP_RSV_DATA { + EFI_TIME TimeStamp; + EFI_UDP4_SESSION_DATA Session; +} UDP_RSV_DATA; + +// +// reserved data to access the NET_BUF delivered by TCP driver +// +typedef struct _TCP_RSV_DATA { + UINT32 UrgLen; +} TCP_RSV_DATA; + +// +// call it to creat a socket and attach it to a PCB +// +SOCKET * +SockCreateChild ( + IN SOCK_INIT_DATA *SockInitData, + IN VOID *ProtoData, + IN UINT32 Len + ); + +// +// call it to destroy a socket and its related PCB +// +EFI_STATUS +SockDestroyChild ( + IN SOCKET *Sock + ); + +// +// call it to configure a socket and its related PCB +// +EFI_STATUS +SockConfigure ( + IN SOCKET *Sock, + IN VOID *ConfigData + ); + +// +// call it to connect a socket to the peer +// +EFI_STATUS +SockConnect ( + IN SOCKET *Sock, + IN VOID *Token + ); + +// +// call it to issue an asynchronous listen token to the socket +// +EFI_STATUS +SockAccept ( + IN SOCKET *Sock, + IN VOID *Token + ); + +// +// Call it to send data using this socket +// +EFI_STATUS +SockSend ( + IN SOCKET *Sock, + IN VOID *Token + ); + +// +// Call it to receive data from this socket +// +EFI_STATUS +SockRcv ( + IN SOCKET *Sock, + IN VOID *Token + ); + +// +// Call it to flush a socket +// +EFI_STATUS +SockFlush ( + IN SOCKET *Sock + ); + +// +// Call it to close a socket in the light of policy in Token +// +EFI_STATUS +SockClose ( + IN SOCKET *Sock, + IN VOID *Token, + IN BOOLEAN OnAbort + ); + +// +// Call it to get the mode data of low layer protocol +// +EFI_STATUS +SockGetMode ( + IN SOCKET *Sock, + IN VOID *Mode + ); + +// +// call it to add this socket instance into a group +// +EFI_STATUS +SockGroup ( + IN SOCKET *Sock, + IN VOID *GroupInfo + ); + +// +// call it to add a route entry for this socket instance +// +EFI_STATUS +SockRoute ( + IN SOCKET *Sock, + IN VOID *RouteInfo + ); + +// +// Supporting function to operate on socket buffer +// +NET_BUF * +SockBufFirst ( + IN SOCK_BUFFER *Sockbuf + ); + +NET_BUF * +SockBufNext ( + IN SOCK_BUFFER *Sockbuf, + IN NET_BUF *SockEntry + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c new file mode 100644 index 0000000000..9039905be6 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c @@ -0,0 +1,680 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Tcp4Dispatcher.c + +Abstract: + + +**/ + +#include "Tcp4Main.h" + +#define TCP_COMP_VAL(Min, Max, Default, Val) \ + ((((Val) <= (Max)) && ((Val) >= (Min))) ? (Val) : (Default)) + +STATIC +EFI_STATUS +Tcp4Route ( + IN TCP_CB *Tcb, + IN TCP4_ROUTE_INFO *RouteInfo + ) +/*++ + +Routine Description: + + Add or remove a route entry in the IP route table associated + with this TCP instance. + +Arguments: + + Tcb - Pointer to the TCP_CB of this TCP instance. + RouteInfo - Pointer to the route info to be processed. + +Returns: + + EFI_SUCCESS - The operation completed successfully. + EFI_NOT_STARTED - The driver instance has not been started. + EFI_NO_MAPPING - When using the default address, configuration(DHCP, + BOOTP, RARP, etc.) is not finished yet. + EFI_OUT_OF_RESOURCES - Could not add the entry to the routing table. + EFI_NOT_FOUND - This route is not in the routing table + (when RouteInfo->DeleteRoute is TRUE). + EFI_ACCESS_DENIED - The route is already defined in the routing table + (when RouteInfo->DeleteRoute is FALSE). + +--*/ +{ + EFI_IP4_PROTOCOL *Ip; + + Ip = Tcb->IpInfo->Ip; + + ASSERT (Ip); + + return Ip->Routes ( + Ip, + RouteInfo->DeleteRoute, + RouteInfo->SubnetAddress, + RouteInfo->SubnetMask, + RouteInfo->GatewayAddress + ); + +} + + +/** + Get the operational settings of this TCP instance. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Mode Pointer to the buffer to store the operational + settings. + + @retval EFI_SUCCESS The mode data is read. + @retval EFI_NOT_STARTED No configuration data is available because this + instance hasn't been started. + +**/ +STATIC +EFI_STATUS +Tcp4GetMode ( + IN TCP_CB *Tcb, + IN TCP4_MODE_DATA *Mode + ) +{ + SOCKET *Sock; + EFI_TCP4_CONFIG_DATA *ConfigData; + EFI_TCP4_ACCESS_POINT *AccessPoint; + EFI_TCP4_OPTION *Option; + EFI_IP4_PROTOCOL *Ip; + + Sock = Tcb->Sk; + + if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp4ConfigData != NULL)) { + return EFI_NOT_STARTED; + } + + if (Mode->Tcp4State) { + *(Mode->Tcp4State) = Tcb->State; + } + + if (Mode->Tcp4ConfigData) { + + ConfigData = Mode->Tcp4ConfigData; + AccessPoint = &(ConfigData->AccessPoint); + Option = ConfigData->ControlOption; + + ConfigData->TypeOfService = Tcb->TOS; + ConfigData->TimeToLive = Tcb->TTL; + + AccessPoint->UseDefaultAddress = Tcb->UseDefaultAddr; + + EFI_IP4 (AccessPoint->StationAddress) = Tcb->LocalEnd.Ip; + AccessPoint->SubnetMask = Tcb->SubnetMask; + AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port); + + EFI_IP4 (AccessPoint->RemoteAddress) = Tcb->RemoteEnd.Ip; + AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port); + AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN); + + if (Option != NULL) { + Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk); + Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk); + Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk); + + Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ; + Option->DataRetries = Tcb->MaxRexmit; + Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ; + Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ; + Option->KeepAliveProbes = Tcb->MaxKeepAlive; + Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ; + Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ; + + Option->EnableNagle = !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE); + Option->EnableTimeStamp = !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS); + Option->EnableWindowScaling = !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS); + + Option->EnableSelectiveAck = FALSE; + Option->EnablePathMtuDiscovery = FALSE; + } + } + + Ip = Tcb->IpInfo->Ip; + ASSERT (Ip); + + return Ip->GetModeData (Ip, Mode->Ip4ModeData, Mode->MnpConfigData, Mode->SnpModeData); +} + + +/** + If AP->StationPort isn't zero, check whether the access point + is registered, else generate a random station port for this + access point. + + @param AP Pointer to the access point. + + @retval EFI_SUCCESS The check is passed or the port is assigned. + @retval EFI_INVALID_PARAMETER The non-zero station port is already used. + @retval EFI_OUT_OF_RESOURCES No port can be allocated. + +**/ +STATIC +EFI_STATUS +Tcp4Bind ( + IN EFI_TCP4_ACCESS_POINT *AP + ) +{ + BOOLEAN Cycle; + + if (0 != AP->StationPort) { + // + // check if a same endpoint is bound + // + if (TcpFindTcbByPeer (&AP->StationAddress, AP->StationPort)) { + + return EFI_INVALID_PARAMETER; + } + } else { + // + // generate a random port + // + Cycle = FALSE; + + if (TCP4_PORT_USER_RESERVED == mTcp4RandomPort) { + mTcp4RandomPort = TCP4_PORT_KNOWN; + } + + mTcp4RandomPort++; + + while (TcpFindTcbByPeer (&AP->StationAddress, mTcp4RandomPort)) { + + mTcp4RandomPort++; + + if (mTcp4RandomPort <= TCP4_PORT_KNOWN) { + + if (Cycle) { + TCP4_DEBUG_ERROR (("Tcp4Bind: no port can be allocated " + "for this pcb\n")); + + return EFI_OUT_OF_RESOURCES; + } + + mTcp4RandomPort = TCP4_PORT_KNOWN + 1; + + Cycle = TRUE; + } + + } + + AP->StationPort = mTcp4RandomPort; + } + + return EFI_SUCCESS; +} + + +/** + Flush the Tcb add its associated protocols.. + + @param Tcb Pointer to the TCP_CB to be flushed. + + @retval EFI_SUCCESS The operation is completed successfully. + +**/ +STATIC +VOID +Tcp4FlushPcb ( + IN TCP_CB *Tcb + ) +{ + SOCKET *Sock; + TCP4_PROTO_DATA *TcpProto; + + IpIoConfigIp (Tcb->IpInfo, NULL); + + Sock = Tcb->Sk; + TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved; + + if (SOCK_IS_CONFIGURED (Sock)) { + NetListRemoveEntry (&Tcb->List); + + TcpSetVariableData (TcpProto->TcpService); + } + + NetbufFreeList (&Tcb->SndQue); + NetbufFreeList (&Tcb->RcvQue); +} + +STATIC +EFI_STATUS +Tcp4AttachPcb ( + IN SOCKET *Sk + ) +{ + TCP_CB *Tcb; + TCP4_PROTO_DATA *ProtoData; + IP_IO *IpIo; + + Tcb = NetAllocateZeroPool (sizeof (TCP_CB)); + + if (Tcb == NULL) { + + TCP4_DEBUG_ERROR (("Tcp4ConfigurePcb: failed to allocate a TCB\n")); + + return EFI_OUT_OF_RESOURCES; + } + + ProtoData = (TCP4_PROTO_DATA *) Sk->ProtoReserved; + IpIo = ProtoData->TcpService->IpIo; + + // + // Create an IpInfo for this Tcb. + // + Tcb->IpInfo = IpIoAddIp (IpIo); + if (Tcb->IpInfo == NULL) { + + NetFreePool (Tcb); + return EFI_OUT_OF_RESOURCES; + } + + NetListInit (&Tcb->List); + NetListInit (&Tcb->SndQue); + NetListInit (&Tcb->RcvQue); + + Tcb->State = TCP_CLOSED; + Tcb->Sk = Sk; + ProtoData->TcpPcb = Tcb; + + return EFI_SUCCESS; +} + +STATIC +VOID +Tcp4DetachPcb ( + IN SOCKET *Sk + ) +{ + TCP4_PROTO_DATA *ProtoData; + TCP_CB *Tcb; + + ProtoData = (TCP4_PROTO_DATA *) Sk->ProtoReserved; + Tcb = ProtoData->TcpPcb; + + ASSERT (Tcb != NULL); + + Tcp4FlushPcb (Tcb); + + IpIoRemoveIp (ProtoData->TcpService->IpIo, Tcb->IpInfo); + + NetFreePool (Tcb); + + ProtoData->TcpPcb = NULL; +} + + +/** + Configure the Tcb using CfgData. + + @param Sk Pointer to the socket of this TCP instance. + @param SkTcb Pointer to the TCP_CB of this TCP instance. + @param CfgData Pointer to the TCP configuration data. + + @retval EFI_SUCCESS The operation is completed successfully. + @retval EFI_INVALID_PARAMETER A same access point has been configured in + another TCP instance. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limit. + +**/ +STATIC +EFI_STATUS +Tcp4ConfigurePcb ( + IN SOCKET *Sk, + IN EFI_TCP4_CONFIG_DATA *CfgData + ) +{ + IP_IO *IpIo; + EFI_IP4_CONFIG_DATA IpCfgData; + EFI_STATUS Status; + EFI_TCP4_OPTION *Option; + TCP4_PROTO_DATA *TcpProto; + TCP_CB *Tcb; + + ASSERT (CfgData && Sk && Sk->SockHandle); + + TcpProto = (TCP4_PROTO_DATA *) Sk->ProtoReserved; + Tcb = TcpProto->TcpPcb; + IpIo = TcpProto->TcpService->IpIo; + + ASSERT (Tcb != NULL); + + // + // Add Ip for send pkt to the peer + // + IpCfgData = mIpIoDefaultIpConfigData; + IpCfgData.DefaultProtocol = EFI_IP_PROTO_TCP; + IpCfgData.UseDefaultAddress = CfgData->AccessPoint.UseDefaultAddress; + IpCfgData.StationAddress = CfgData->AccessPoint.StationAddress; + IpCfgData.SubnetMask = CfgData->AccessPoint.SubnetMask; + IpCfgData.ReceiveTimeout = (UINT32) (-1); + + // + // Configure the IP instance this Tcb consumes. + // + Status = IpIoConfigIp (Tcb->IpInfo, &IpCfgData); + if (EFI_ERROR (Status)) { + goto OnExit; + } + + // + // Get the default address info if the instance is configured to use default address. + // + if (CfgData->AccessPoint.UseDefaultAddress) { + CfgData->AccessPoint.StationAddress = IpCfgData.StationAddress; + CfgData->AccessPoint.SubnetMask = IpCfgData.SubnetMask; + } + + // + // check if we can bind this endpoint in CfgData + // + Status = Tcp4Bind (&(CfgData->AccessPoint)); + + if (EFI_ERROR (Status)) { + TCP4_DEBUG_ERROR (("Tcp4ConfigurePcb: Bind endpoint failed " + "with %r\n", Status)); + + goto OnExit; + } + + // + // Initalize the operating information in this Tcb + // + ASSERT (Tcb->State == TCP_CLOSED && + NetListIsEmpty (&Tcb->SndQue) && + NetListIsEmpty (&Tcb->RcvQue)); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE); + Tcb->State = TCP_CLOSED; + + Tcb->SndMss = 536; + Tcb->RcvMss = TcpGetRcvMss (Sk); + + Tcb->SRtt = 0; + Tcb->Rto = 3 * TCP_TICK_HZ; + + Tcb->CWnd = Tcb->SndMss; + Tcb->Ssthresh = 0xffffffff; + + Tcb->CongestState = TCP_CONGEST_OPEN; + + Tcb->KeepAliveIdle = TCP_KEEPALIVE_IDLE_MIN; + Tcb->KeepAlivePeriod = TCP_KEEPALIVE_PERIOD; + Tcb->MaxKeepAlive = TCP_MAX_KEEPALIVE; + Tcb->MaxRexmit = TCP_MAX_LOSS; + Tcb->FinWait2Timeout = TCP_FIN_WAIT2_TIME; + Tcb->TimeWaitTimeout = TCP_TIME_WAIT_TIME; + Tcb->ConnectTimeout = TCP_CONNECT_TIME; + + // + // initialize Tcb in the light of CfgData + // + Tcb->TTL = CfgData->TimeToLive; + Tcb->TOS = CfgData->TypeOfService; + + Tcb->LocalEnd.Ip = EFI_IP4 (CfgData->AccessPoint.StationAddress); + Tcb->LocalEnd.Port = HTONS (CfgData->AccessPoint.StationPort); + Tcb->SubnetMask = CfgData->AccessPoint.SubnetMask; + + Tcb->RemoteEnd.Ip = EFI_IP4 (CfgData->AccessPoint.RemoteAddress); + Tcb->RemoteEnd.Port = HTONS (CfgData->AccessPoint.RemotePort); + + Option = CfgData->ControlOption; + + if (Option != NULL) { + SET_RCV_BUFFSIZE ( + Sk, + TCP_COMP_VAL (TCP_RCV_BUF_SIZE_MIN, + TCP_RCV_BUF_SIZE, + TCP_RCV_BUF_SIZE, + Option->ReceiveBufferSize) + ); + SET_SND_BUFFSIZE ( + Sk, + TCP_COMP_VAL (TCP_SND_BUF_SIZE_MIN, + TCP_SND_BUF_SIZE, + TCP_SND_BUF_SIZE, + Option->SendBufferSize) + ); + + SET_BACKLOG ( + Sk, + TCP_COMP_VAL (TCP_BACKLOG_MIN, + TCP_BACKLOG, + TCP_BACKLOG, + Option->MaxSynBackLog) + ); + + Tcb->MaxRexmit = (UINT16) TCP_COMP_VAL ( + TCP_MAX_LOSS_MIN, + TCP_MAX_LOSS, + TCP_MAX_LOSS, + Option->DataRetries + ); + Tcb->FinWait2Timeout = TCP_COMP_VAL ( + TCP_FIN_WAIT2_TIME, + TCP_FIN_WAIT2_TIME_MAX, + TCP_FIN_WAIT2_TIME, + Option->FinTimeout * TCP_TICK_HZ + ); + + if (Option->TimeWaitTimeout != 0) { + Tcb->TimeWaitTimeout = TCP_COMP_VAL ( + TCP_TIME_WAIT_TIME, + TCP_TIME_WAIT_TIME_MAX, + TCP_TIME_WAIT_TIME, + Option->TimeWaitTimeout * TCP_TICK_HZ + ); + } else { + Tcb->TimeWaitTimeout = 0; + } + + if (Option->KeepAliveProbes != 0) { + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE); + + Tcb->MaxKeepAlive = (UINT8) TCP_COMP_VAL ( + TCP_MAX_KEEPALIVE_MIN, + TCP_MAX_KEEPALIVE, + TCP_MAX_KEEPALIVE, + Option->KeepAliveProbes + ); + Tcb->KeepAliveIdle = TCP_COMP_VAL ( + TCP_KEEPALIVE_IDLE_MIN, + TCP_KEEPALIVE_IDLE_MAX, + TCP_KEEPALIVE_IDLE_MIN, + Option->KeepAliveTime * TCP_TICK_HZ + ); + Tcb->KeepAlivePeriod = TCP_COMP_VAL ( + TCP_KEEPALIVE_PERIOD_MIN, + TCP_KEEPALIVE_PERIOD, + TCP_KEEPALIVE_PERIOD, + Option->KeepAliveInterval * TCP_TICK_HZ + ); + } + + Tcb->ConnectTimeout = TCP_COMP_VAL ( + TCP_CONNECT_TIME_MIN, + TCP_CONNECT_TIME, + TCP_CONNECT_TIME, + Option->ConnectionTimeout * TCP_TICK_HZ + ); + + if (Option->EnableNagle == FALSE) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE); + } + + if (Option->EnableTimeStamp == FALSE) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_TS); + } + + if (Option->EnableWindowScaling == FALSE) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_WS); + } + } + + // + // update state of Tcb and socket + // + if (CfgData->AccessPoint.ActiveFlag == FALSE) { + + TcpSetState (Tcb, TCP_LISTEN); + SockSetState (Sk, SO_LISTENING); + + Sk->ConfigureState = SO_CONFIGURED_PASSIVE; + } else { + + Sk->ConfigureState = SO_CONFIGURED_ACTIVE; + } + + TcpInsertTcb (Tcb); + +OnExit: + + return Status; +} + + +/** + The procotol handler provided to the socket layer, used to + dispatch the socket level requests by calling the corresponding + TCP layer functions. + + @param Sock Pointer to the socket of this TCP instance. + @param Request The code of this operation request. + @param Data Pointer to the operation specific data passed in + together with the operation request. + + @retval EFI_SUCCESS The socket request is completed successfully. + @retval other The error status returned by the corresponding TCP + layer function. + +**/ +EFI_STATUS +Tcp4Dispatcher ( + IN SOCKET *Sock, + IN SOCK_REQUEST Request, + IN VOID *Data OPTIONAL + ) +{ + TCP_CB *Tcb; + TCP4_PROTO_DATA *ProtoData; + EFI_IP4_PROTOCOL *Ip; + + ProtoData = (TCP4_PROTO_DATA *) Sock->ProtoReserved; + Tcb = ProtoData->TcpPcb; + + switch (Request) { + case SOCK_POLL: + Ip = ProtoData->TcpService->IpIo->Ip; + Ip->Poll (Ip); + break; + + case SOCK_CONSUMED: + // + // After user received data from socket buffer, socket will + // notify TCP using this message to give it a chance to send out + // window update information + // + ASSERT (Tcb); + TcpOnAppConsume (Tcb); + break; + + case SOCK_SND: + + ASSERT (Tcb); + TcpOnAppSend (Tcb); + break; + + case SOCK_CLOSE: + + TcpOnAppClose (Tcb); + + break; + + case SOCK_ABORT: + + TcpOnAppAbort (Tcb); + + break; + + case SOCK_SNDPUSH: + Tcb->SndPsh = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk); + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH); + + break; + + case SOCK_SNDURG: + Tcb->SndUp = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk) - 1; + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG); + + break; + + case SOCK_CONNECT: + + TcpOnAppConnect (Tcb); + + break; + + case SOCK_ATTACH: + + return Tcp4AttachPcb (Sock); + + break; + + case SOCK_FLUSH: + + Tcp4FlushPcb (Tcb); + + break; + + case SOCK_DETACH: + + Tcp4DetachPcb (Sock); + + break; + + case SOCK_CONFIGURE: + + return Tcp4ConfigurePcb ( + Sock, + (EFI_TCP4_CONFIG_DATA *) Data + ); + + break; + + case SOCK_MODE: + + ASSERT (Data && Tcb); + + return Tcp4GetMode (Tcb, (TCP4_MODE_DATA *) Data); + + break; + + case SOCK_ROUTE: + + ASSERT (Data && Tcb); + + return Tcp4Route (Tcb, (TCP4_ROUTE_INFO *) Data); + + } + + return EFI_SUCCESS; + +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c new file mode 100644 index 0000000000..2927849285 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c @@ -0,0 +1,669 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Tcp4Driver.c + +Abstract: + + +**/ + +#include "Tcp4Main.h" + + +UINT16 mTcp4RandomPort; +extern EFI_COMPONENT_NAME_PROTOCOL gTcp4ComponentName; + +TCP4_HEARTBEAT_TIMER mTcp4Timer = { + NULL, + 0 +}; + +EFI_TCP4_PROTOCOL mTcp4ProtocolTemplate = { + Tcp4GetModeData, + Tcp4Configure, + Tcp4Routes, + Tcp4Connect, + Tcp4Accept, + Tcp4Transmit, + Tcp4Receive, + Tcp4Close, + Tcp4Cancel, + Tcp4Poll +}; + +SOCK_INIT_DATA mTcp4DefaultSockData = { + SOCK_STREAM, + 0, + NULL, + TCP_BACKLOG, + TCP_SND_BUF_SIZE, + TCP_RCV_BUF_SIZE, + &mTcp4ProtocolTemplate, + Tcp4Dispatcher, + NULL, +}; + +EFI_DRIVER_BINDING_PROTOCOL mTcp4DriverBinding = { + Tcp4DriverBindingSupported, + Tcp4DriverBindingStart, + Tcp4DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL mTcp4ServiceBinding = { + Tcp4ServiceBindingCreateChild, + Tcp4ServiceBindingDestroyChild +}; + + +/** + Create and start the heartbeat timer for TCP driver. + + None. + + @retval EFI_SUCCESS The timer is successfully created and started. + @retval other The timer is not created. + +**/ +STATIC +EFI_STATUS +Tcp4CreateTimer ( + VOID + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (mTcp4Timer.RefCnt == 0) { + + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + NET_TPL_TIMER, + TcpTicking, + NULL, + &mTcp4Timer.TimerEvent + ); + if (!EFI_ERROR (Status)) { + + Status = gBS->SetTimer ( + mTcp4Timer.TimerEvent, + TimerPeriodic, + (UINT64) (TICKS_PER_SECOND / TCP_TICK_HZ) + ); + } + } + + if (!EFI_ERROR (Status)) { + + mTcp4Timer.RefCnt++; + } + + return Status; +} + + +/** + Stop and destroy the heartbeat timer for TCP driver. + + None. + + @return None. + +**/ +STATIC +VOID +Tcp4DestroyTimer ( + VOID + ) +{ + ASSERT (mTcp4Timer.RefCnt > 0); + + mTcp4Timer.RefCnt--; + + if (mTcp4Timer.RefCnt > 0) { + return; + } + + gBS->SetTimer (mTcp4Timer.TimerEvent, TimerCancel, 0); + gBS->CloseEvent (mTcp4Timer.TimerEvent); + mTcp4Timer.TimerEvent = NULL; +} + +//@MT: EFI_DRIVER_ENTRY_POINT (Tcp4DriverEntryPoint) + +EFI_STATUS +EFIAPI +Tcp4DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +/*++ + +Routine Description: + + The entry point for Tcp4 driver. used to install + Tcp4 driver on the ImageHandle. + +Arguments: + + ImageHandle - The firmware allocated handle for this + driver image. + SystemTable - Pointer to the EFI system table. + +Returns: + + EFI_SUCCESS - Driver loaded. + other - Driver not loaded. + +--*/ +{ + EFI_STATUS Status; + UINT32 Seed; + + // + // Install the TCP4 Driver Binding Protocol + // + Status = NetLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &mTcp4DriverBinding, + ImageHandle, + &gTcp4ComponentName, + NULL, + NULL + ); + + // + // Initialize ISS and random port. + // + Seed = NetRandomInitSeed (); + mTcpGlobalIss = NET_RANDOM (Seed) % mTcpGlobalIss; + mTcp4RandomPort = TCP4_PORT_KNOWN + + (UINT16) (NET_RANDOM(Seed) % TCP4_PORT_KNOWN); + + return Status; +} + + +/** + Test to see if this driver supports ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Tcp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Test for the Tcp4ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + // + // Test for the Ip4 Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + + +/** + Start this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The driver is added to ControllerHandle. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to start the + driver. + @retval other The driver cannot be added to ControllerHandle. + +**/ +EFI_STATUS +EFIAPI +Tcp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + TCP4_SERVICE_DATA *TcpServiceData; + IP_IO_OPEN_DATA OpenData; + + TcpServiceData = NetAllocateZeroPool (sizeof (TCP4_SERVICE_DATA)); + + if (NULL == TcpServiceData) { + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStart: Have no enough" + " resource to create a Tcp Servcie Data!\n")); + + return EFI_OUT_OF_RESOURCES; + } + + // + // Create a new IP IO to Consume it + // + TcpServiceData->IpIo = IpIoCreate (This->DriverBindingHandle, ControllerHandle); + if (NULL == TcpServiceData->IpIo) { + + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStart: Have no enough" + " resource to create an Ip Io!\n")); + + Status = EFI_OUT_OF_RESOURCES; + goto ReleaseServiceData; + } + + // + // Configure and start IpIo. + // + NetZeroMem (&OpenData, sizeof (IP_IO_OPEN_DATA)); + + OpenData.IpConfigData = mIpIoDefaultIpConfigData; + OpenData.IpConfigData.DefaultProtocol = EFI_IP_PROTO_TCP; + + OpenData.PktRcvdNotify = Tcp4RxCallback; + Status = IpIoOpen (TcpServiceData->IpIo, &OpenData); + + if (EFI_ERROR (Status)) { + goto ReleaseServiceData; + } + + // + // Create the timer event used by TCP driver + // + Status = Tcp4CreateTimer (); + if (EFI_ERROR (Status)) { + + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStart: Create TcpTimer" + " Event failed with %r\n", Status)); + + goto ReleaseIpIo; + } + + // + // Install the Tcp4ServiceBinding Protocol on the + // controller handle + // + TcpServiceData->Tcp4ServiceBinding = mTcp4ServiceBinding; + + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + &TcpServiceData->Tcp4ServiceBinding, + NULL + ); + if (EFI_ERROR (Status)) { + + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStart: Install Tcp4 Service Binding" + " Protocol failed for %r\n", Status)); + + goto ReleaseTimer; + } + + // + // Initialize member in TcpServiceData + // + TcpServiceData->ControllerHandle = ControllerHandle; + TcpServiceData->Signature = TCP4_DRIVER_SIGNATURE; + TcpServiceData->DriverBindingHandle = This->DriverBindingHandle; + + TcpSetVariableData (TcpServiceData); + + return EFI_SUCCESS; + +ReleaseTimer: + + Tcp4DestroyTimer (); + +ReleaseIpIo: + + IpIoDestroy (TcpServiceData->IpIo); + +ReleaseServiceData: + + NetFreePool (TcpServiceData); + + return Status; +} + + +/** + Stop this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on. + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed from ControllerHandle. + @retval other This driver is not removed from ControllerHandle. + +**/ +EFI_STATUS +EFIAPI +Tcp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_HANDLE NicHandle; + EFI_SERVICE_BINDING_PROTOCOL *Tcp4ServiceBinding; + TCP4_SERVICE_DATA *TcpServiceData; + TCP_CB *TcpPcb; + SOCKET *Sock; + TCP4_PROTO_DATA *TcpProto; + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *NextEntry; + + // Find the NicHandle where Tcp4 ServiceBinding Protocol is installed. + // + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid); + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + // + // Retrieve the TCP driver Data Structure + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + (VOID **) &Tcp4ServiceBinding, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStop: Locate Tcp4 Service " + " Binding Protocol failed with %r\n", Status)); + + return Status; + } + + TcpServiceData = TCP4_FROM_THIS (Tcp4ServiceBinding); + + // + // Kill TCP driver + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mTcpRunQue) { + TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + // + // Try to destroy this child + // + Sock = TcpPcb->Sk; + TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved; + + if (TcpProto->TcpService == TcpServiceData) { + Status = SockDestroyChild (Sock); + + if (EFI_ERROR (Status)) { + + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStop: Destroy Tcp " + "instance failed with %r\n", Status)); + return Status; + } + } + } + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mTcpListenQue) { + TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + // + // Try to destroy this child + // + Sock = TcpPcb->Sk; + TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved; + + if (TcpProto->TcpService == TcpServiceData) { + Status = SockDestroyChild (TcpPcb->Sk); + if (EFI_ERROR (Status)) { + + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStop: Destroy Tcp " + "instance failed with %r\n", Status)); + return Status; + } + } + } + + // + // Uninstall TCP servicebinding protocol + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + NicHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + Tcp4ServiceBinding, + NULL + ); + if (EFI_ERROR (Status)) { + + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStop: Uninstall TCP service " + "binding protocol failed with %r\n", Status)); + return Status; + } + + // + // Destroy the IpIO consumed by TCP driver + // + Status = IpIoDestroy (TcpServiceData->IpIo); + + // + // Destroy the heartbeat timer. + // + Tcp4DestroyTimer (); + + // + // Clear the variable. + // + TcpClearVariableData (TcpServiceData); + + // + // Release the TCP service data + // + NetFreePool (TcpServiceData); + + return Status; +} + + +/** + Creates a child handle with a set of TCP4 services. + + @param This Protocol instance pointer. + @param ChildHandle Pointer to the handle of the child to create. If + it is NULL, then a new handle is created. If it is + not NULL, then the I/O services are added to the + existing child handle. + + @retval EFI_SUCCESS The child handle is created. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to create the + child. + +**/ +EFI_STATUS +EFIAPI +Tcp4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ) +{ + SOCKET *Sock; + TCP4_SERVICE_DATA *TcpServiceData; + TCP4_PROTO_DATA TcpProto; + EFI_STATUS Status; + VOID *Ip4; + EFI_TPL OldTpl; + + if (NULL == This || NULL == ChildHandle) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + TcpServiceData = TCP4_FROM_THIS (This); + TcpProto.TcpService = TcpServiceData; + TcpProto.TcpPcb = NULL; + + // + // Create a tcp instance with defualt Tcp default + // sock init data and TcpProto + // + mTcp4DefaultSockData.DriverBinding = TcpServiceData->DriverBindingHandle; + + Sock = SockCreateChild (&mTcp4DefaultSockData, &TcpProto, sizeof (TCP4_PROTO_DATA)); + if (NULL == Sock) { + TCP4_DEBUG_ERROR (("Tcp4DriverBindingCreateChild: " + "No resource to create a Tcp Child\n")); + + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + *ChildHandle = Sock->SockHandle; + + // + // Open the default Ip4 protocol of IP_IO BY_DRIVER. + // + Status = gBS->OpenProtocol ( + TcpServiceData->IpIo->ChildHandle, + &gEfiIp4ProtocolGuid, + (VOID **) &Ip4, + TcpServiceData->DriverBindingHandle, + Sock->SockHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + SockDestroyChild (Sock); + } + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + return Status; +} + + +/** + Destroys a child handle with a set of UDP4 services. + + @param This Protocol instance pointer. + @param ChildHandle Handle of the child to be destroyed. + + @retval EFI_SUCCESS The TCP4 services are removed from the child + handle. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval other The child handle is not destroyed. + +**/ +EFI_STATUS +EFIAPI +Tcp4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + EFI_TCP4_PROTOCOL *Tcp4; + SOCKET *Sock; + TCP4_PROTO_DATA *TcpProtoData; + TCP4_SERVICE_DATA *TcpServiceData; + EFI_TPL OldTpl; + + if (NULL == This || NULL == ChildHandle) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + // + // retrieve the Tcp4 protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &Tcp4, + mTcp4DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + + // + // destroy this sock and related Tcp protocol control + // block + // + Sock = SOCK_FROM_THIS (Tcp4); + TcpProtoData = (TCP4_PROTO_DATA *) Sock->ProtoReserved; + TcpServiceData = TcpProtoData->TcpService; + + Status = SockDestroyChild (Sock); + + // + // Close the Ip4 protocol. + // + gBS->CloseProtocol ( + TcpServiceData->IpIo->ChildHandle, + &gEfiIp4ProtocolGuid, + TcpServiceData->DriverBindingHandle, + ChildHandle + ); + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + return Status; +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h new file mode 100644 index 0000000000..af3444ef58 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h @@ -0,0 +1,141 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Tcp4Driver.h + +Abstract: + + +**/ + +#ifndef _TCP4_DRIVER_H_ +#define _TCP4_DRIVER_H_ + +#include +#include + +#define TCP4_DRIVER_SIGNATURE EFI_SIGNATURE_32 ('T', 'C', 'P', '4') + +#define TCP4_PORT_KNOWN 1024 +#define TCP4_PORT_USER_RESERVED 65535 + +typedef struct _TCP4_HEARTBEAT_TIMER { + EFI_EVENT TimerEvent; + INTN RefCnt; +} TCP4_HEARTBEAT_TIMER; + +typedef struct _TCP4_SERVICE_DATA { + UINT32 Signature; + EFI_HANDLE ControllerHandle; + IP_IO *IpIo; // IP Io consumed by TCP4 + EFI_SERVICE_BINDING_PROTOCOL Tcp4ServiceBinding; + EFI_HANDLE DriverBindingHandle; + CHAR16 *MacString; +} TCP4_SERVICE_DATA; + +// +// Prototype for TCP4 driver Rcv callback function registered to IP_IO +// +VOID +Tcp4RxCallback ( + IN EFI_STATUS Status, + IN ICMP_ERROR IcmpErr, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Pkt, + IN VOID *Context OPTIONAL + ); + +INTN +TcpSendIpPacket ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf, + IN UINT32 Src, + IN UINT32 Dest + ); + +EFI_STATUS +Tcp4Dispatcher ( + IN SOCKET *Sock, + IN SOCK_REQUEST Request, + IN VOID *Data OPTIONAL + ); + +typedef struct _TCP4_PROTO_DATA { + TCP4_SERVICE_DATA *TcpService; + TCP_CB *TcpPcb; +} TCP4_PROTO_DATA; + +#define TCP4_FROM_THIS(a) \ + CR ( \ + (a), \ + TCP4_SERVICE_DATA, \ + Tcp4ServiceBinding, \ + TCP4_DRIVER_SIGNATURE \ + ) + +// +// Function prototype for the driver's entry point +// +EFI_STATUS +EFIAPI +Tcp4DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +// +// Function prototypes for the Drivr Binding Protocol +// +EFI_STATUS +EFIAPI +Tcp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +Tcp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +Tcp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// Function ptototypes for the ServiceBinding Prococol +// +EFI_STATUS +EFIAPI +Tcp4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ); + +EFI_STATUS +EFIAPI +Tcp4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf new file mode 100644 index 0000000000..9baea4e388 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf @@ -0,0 +1,77 @@ +#/** @file +# Component name for module Tcp4 +# +# FIX ME! +# Copyright (c) 2006, Intel Corporation. All right reserved. +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Tcp4Dxe + FILE_GUID = 6d6963ab-906d-4a65-a7ca-bd40e5d6af4d + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = Tcp4DriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + SockImpl.c + SockInterface.c + Tcp4Proto.h + Tcp4Main.h + SockImpl.h + Tcp4Output.c + Tcp4Timer.c + Tcp4Option.h + Tcp4Dispatcher.c + Tcp4Input.c + Tcp4Misc.c + Tcp4Main.c + Socket.h + ComponentName.c + Tcp4Driver.h + Tcp4Io.c + Tcp4Driver.c + Tcp4Func.h + Tcp4Option.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + UefiLib + BaseLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + DebugLib + NetLib + IpIoLib + +[Protocols] + gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiTcp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiTcp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.msa b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.msa new file mode 100644 index 0000000000..fec1934bb7 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.msa @@ -0,0 +1,90 @@ + + + Tcp4Dxe + DXE_DRIVER + 6d6963ab-906d-4a65-a7ca-bd40e5d6af4d + 1.0 + Component name for module Tcp4 + FIX ME! + Copyright (c) 2006, Intel Corporation. All right reserved. + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF EBC + false + Tcp4Dxe + + + + DebugLib + + + UefiRuntimeServicesTableLib + + + UefiDriverEntryPoint + + + UefiBootServicesTableLib + + + BaseLib + + + UefiLib + + + + Tcp4Option.c + Tcp4Func.h + Tcp4Driver.c + Tcp4Io.c + Tcp4Driver.h + ComponentName.c + Socket.h + Tcp4Main.c + Tcp4Misc.c + Tcp4Input.c + Tcp4Dispatcher.c + Tcp4Option.h + Tcp4Timer.c + Tcp4Output.c + SockImpl.h + Tcp4Main.h + Tcp4Proto.h + SockInterface.c + SockImpl.c + + + + + + + + gEfiTcp4ProtocolGuid + + + gEfiIp4ServiceBindingProtocolGuid + + + gEfiTcp4ServiceBindingProtocolGuid + + + gEfiIp4ProtocolGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + Tcp4DriverEntryPoint + + + \ No newline at end of file diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h new file mode 100644 index 0000000000..6441c13c64 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h @@ -0,0 +1,353 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Tcp4Func.h + +Abstract: + + +**/ + +#ifndef _TCP4_FUNC_H_ +#define _TCP4_FUNC_H_ + +// +// Declaration of all the functions in TCP +// protocol. It is intended to keep tcp.h +// clear. +// + +// +// Functions in tcp.c +// +BOOLEAN +TcpFindTcbByPeer ( + IN EFI_IPv4_ADDRESS *Addr, + IN TCP_PORTNO Port + ); + +TCP_CB * +TcpLocateTcb ( + IN TCP_PORTNO LocalPort, + IN UINT32 LocalIp, + IN TCP_PORTNO RemotePort, + IN UINT32 RemoteIp, + IN BOOLEAN Syn + ); + +INTN +TcpInsertTcb ( + IN TCP_CB *Tcb + ); + +TCP_CB * +TcpCloneTcb ( + IN TCP_CB *Tcb + ); + +TCP_SEQNO +TcpGetIss ( + VOID + ); + +VOID +TcpInitTcbLocal ( + IN TCP_CB *Tcb + ); + +VOID +TcpInitTcbPeer ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg, + IN TCP_OPTION *Opt + ); + +UINT16 +TcpGetRcvMss ( + IN SOCKET *Sock + ); + +VOID +TcpSetState ( + IN TCP_CB *Tcb, + IN UINT8 State + ); + +// +// Functions in Tcp4Output.c +// +INTN +TcpSendIpPacket ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf, + IN UINT32 Src, + IN UINT32 Dst + ); + +INTN +TcpToSendData ( + IN TCP_CB *Tcb, + IN INTN Force + ); + +VOID +TcpToSendAck ( + IN TCP_CB *Tcb + ); + +VOID +TcpSendAck ( + IN TCP_CB *Tcb + ); + +INTN +TcpSendZeroProbe ( + IN TCP_CB *Tcb + ); + +INTN +TcpDeliverData ( + IN TCP_CB *Tcb + ); + +INTN +TcpSendReset ( + IN TCP_CB *Tcb, + IN TCP_HEAD *Head, + IN INT32 Len, + IN UINT32 Local, + IN UINT32 Remote + ); + +UINT32 +TcpRcvWinOld ( + IN TCP_CB *Tcb + ); + +UINT32 +TcpRcvWinNow ( + IN TCP_CB *Tcb + ); + +INTN +TcpRetransmit ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq + ); + +UINT32 +TcpDataToSend ( + IN TCP_CB *Tcb, + IN INTN Force + ); + +INTN +TcpVerifySegment ( + IN NET_BUF *Nbuf + ); + +INTN +TcpCheckSndQue ( + IN NET_LIST_ENTRY *Head + ); + +NET_BUF * +TcpGetSegmentSndQue ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ); + +NET_BUF * +TcpGetSegmentSock ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ); + +NET_BUF * +TcpGetSegment ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ); + +TCP_SEQNO +TcpGetMaxSndNxt ( + IN TCP_CB *Tcb + ); + +// +// Functions from Tcp4Input.c +// +VOID +TcpIcmpInput ( + IN NET_BUF *Nbuf, + IN ICMP_ERROR IcmpErr, + IN UINT32 Src, + IN UINT32 Dst + ); + +INTN +TcpInput ( + IN NET_BUF *Nbuf, + IN UINT32 Src, + IN UINT32 Dst + ); + +INTN +TcpSeqAcceptable ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg + ); + +VOID +TcpFastRecover ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg + ); + +VOID +TcpFastLossRecover ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg + ); + +VOID +TcpComputeRtt ( + IN TCP_CB *Tcb, + IN UINT32 Measure + ); + +INTN +TcpTrimInWnd ( + IN TCP_CB *Tcb, + IN NET_BUF *Buf + ); + +VOID +TcpQueueData ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ); + +VOID +TcpAdjustSndQue ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Ack + ); + +// +// Functions from Tcp4Misc.c +// +UINT16 +TcpChecksum ( + IN NET_BUF *Buf, + IN UINT16 HeadChecksum + ); + +TCP_SEG * +TcpFormatNetbuf ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ); + +VOID +TcpOnAppConnect ( + IN TCP_CB *Tcb + ); + +INTN +TcpOnAppConsume ( + IN TCP_CB *Tcb + ); + +VOID +TcpOnAppClose ( + IN TCP_CB *Tcb + ); + +INTN +TcpOnAppSend ( + IN TCP_CB *Tcb + ); + +VOID +TcpOnAppAbort ( + IN TCP_CB *Tcb + ); + +VOID +TcpResetConnection ( + IN TCP_CB *Tcb + ); + +// +// Functions in Tcp4Timer.c +// +VOID +TcpClose ( + IN TCP_CB *Tcb + ); + +VOID +EFIAPI +TcpTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +VOID +TcpSetTimer ( + IN TCP_CB *Tcb, + IN UINT16 Timer, + IN UINT32 TimeOut + ); + +VOID +TcpClearTimer ( + IN TCP_CB *Tcb, + IN UINT16 Timer + ); + +VOID +TcpClearAllTimer ( + IN TCP_CB *Tcb + ); + +VOID +TcpSetProbeTimer ( + IN TCP_CB *Tcb + ); + +VOID +TcpSetKeepaliveTimer ( + IN TCP_CB *Tcb + ); + +VOID +TcpBackoffRto ( + IN TCP_CB *Tcb + ); + +EFI_STATUS +TcpSetVariableData ( + IN TCP4_SERVICE_DATA *Tcp4Service + ); + +VOID +TcpClearVariableData ( + IN TCP4_SERVICE_DATA *Tcp4Service + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c new file mode 100644 index 0000000000..b636016207 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c @@ -0,0 +1,1486 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Tcp4Input.c + +Abstract: + + TCP input process routines. + + +**/ + +#include "Tcp4Main.h" + + +/** + Check whether the sequence number of the incoming segment + is acceptable. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seg Pointer to the incoming segment. + + @return 1 if the sequence number is acceptable, otherwise 0. + +**/ +INTN +TcpSeqAcceptable ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg + ) +{ + return (TCP_SEQ_LEQ (Tcb->RcvWl2, Seg->End) && + TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvWl2 + Tcb->RcvWnd)); +} + + +/** + NewReno fast recovery, RFC3782. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seg Segment that triggers the fast recovery. + + @return None. + +**/ +VOID +TcpFastRecover ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg + ) +{ + UINT32 FlightSize; + UINT32 Acked; + + // + // Step 1: Three duplicate ACKs and not in fast recovery + // + if (Tcb->CongestState != TCP_CONGEST_RECOVER) { + + // + // Step 1A: Invoking fast retransmission. + // + FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna); + + Tcb->Ssthresh = NET_MAX (FlightSize >> 1, (UINT32) (2 * Tcb->SndMss)); + Tcb->Recover = Tcb->SndNxt; + + Tcb->CongestState = TCP_CONGEST_RECOVER; + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + + // + // Step 2: Entering fast retransmission + // + TcpRetransmit (Tcb, Tcb->SndUna); + Tcb->CWnd = Tcb->Ssthresh + 3 * Tcb->SndMss; + + TCP4_DEBUG_TRACE (("TcpFastRecover: enter fast retransmission" + " for TCB %x, recover point is %d\n", Tcb, Tcb->Recover)); + return; + } + + // + // During fast recovery, execute Step 3, 4, 5 of RFC3782 + // + if (Seg->Ack == Tcb->SndUna) { + + // + // Step 3: Fast Recovery, + // If this is a duplicated ACK, increse Cwnd by SMSS. + // + + // Step 4 is skipped here only to be executed later + // by TcpToSendData + // + Tcb->CWnd += Tcb->SndMss; + TCP4_DEBUG_TRACE (("TcpFastRecover: received another" + " duplicated ACK (%d) for TCB %x\n", Seg->Ack, Tcb)); + + } else { + + // + // New data is ACKed, check whether it is a + // full ACK or partial ACK + // + if (TCP_SEQ_GEQ (Seg->Ack, Tcb->Recover)) { + + // + // Step 5 - Full ACK: + // deflate the congestion window, and exit fast recovery + // + FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna); + + Tcb->CWnd = NET_MIN (Tcb->Ssthresh, FlightSize + Tcb->SndMss); + + Tcb->CongestState = TCP_CONGEST_OPEN; + TCP4_DEBUG_TRACE (("TcpFastRecover: received a full ACK(%d)" + " for TCB %x, exit fast recovery\n", Seg->Ack, Tcb)); + + } else { + + // + // Step 5 - Partial ACK: + // fast retransmit the first unacknowledge field + // , then deflate the CWnd + // + TcpRetransmit (Tcb, Seg->Ack); + Acked = TCP_SUB_SEQ (Seg->Ack, Tcb->SndUna); + + // + // Deflate the CWnd by the amount of new data + // ACKed by SEG.ACK. If more than one SMSS data + // is ACKed, add back SMSS byte to CWnd after + // + if (Acked >= Tcb->SndMss) { + Acked -= Tcb->SndMss; + + } + + Tcb->CWnd -= Acked; + + TCP4_DEBUG_TRACE (("TcpFastRecover: received a partial" + " ACK(%d) for TCB %x\n", Seg->Ack, Tcb)); + + } + } +} + + +/** + NewReno fast loss recovery, RFC3792. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seg Segment that triggers the fast loss recovery. + + @return None. + +**/ +VOID +TcpFastLossRecover ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg + ) +{ + if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) { + + // + // New data is ACKed, check whether it is a + // full ACK or partial ACK + // + if (TCP_SEQ_GEQ (Seg->Ack, Tcb->LossRecover)) { + + // + // Full ACK: exit the loss recovery. + // + Tcb->LossTimes = 0; + Tcb->CongestState = TCP_CONGEST_OPEN; + + TCP4_DEBUG_TRACE (("TcpFastLossRecover: received a " + "full ACK(%d) for TCB %x\n", Seg->Ack, Tcb)); + + } else { + + // + // Partial ACK: + // fast retransmit the first unacknowledge field. + // + TcpRetransmit (Tcb, Seg->Ack); + TCP4_DEBUG_TRACE (("TcpFastLossRecover: received a " + "partial ACK(%d) for TCB %x\n", Seg->Ack, Tcb)); + } + } +} + + +/** + Compute the RTT as specified in RFC2988 + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Measure Currently measured RTT in heart beats. + + @return None. + +**/ +VOID +TcpComputeRtt ( + IN TCP_CB *Tcb, + IN UINT32 Measure + ) +{ + INT32 Var; + + // + // Step 2.3: Compute the RTO for subsequent RTT measurement. + // + if (Tcb->SRtt != 0) { + + Var = Tcb->SRtt - (Measure << TCP_RTT_SHIFT); + + if (Var < 0) { + Var = -Var; + } + + Tcb->RttVar = (3 * Tcb->RttVar + Var) >> 2; + Tcb->SRtt = 7 * (Tcb->SRtt >> 3) + Measure; + + } else { + // + // Step 2.2: compute the first RTT measure + // + Tcb->SRtt = Measure << TCP_RTT_SHIFT; + Tcb->RttVar = Measure << (TCP_RTT_SHIFT - 1); + } + + Tcb->Rto = (Tcb->SRtt + NET_MAX (8, 4 * Tcb->RttVar)) >> TCP_RTT_SHIFT; + + // + // Step 2.4: Limit the RTO to at least 1 second + // Step 2.5: Limit the RTO to a maxium value that + // is at least 60 second + // + if (Tcb->Rto < TCP_RTO_MIN) { + Tcb->Rto = TCP_RTO_MIN; + + } else if (Tcb->Rto > TCP_RTO_MAX) { + Tcb->Rto = TCP_RTO_MAX; + + } + + TCP4_DEBUG_TRACE (("TcpComputeRtt: new RTT for TCB %x" + " computed SRTT: %d RTTVAR: %d RTO: %d\n", + Tcb, Tcb->SRtt, Tcb->RttVar, Tcb->Rto)); + +} + + +/** + Trim the data, SYN and FIN to fit into the window defined by + Left and Right. + + @param Nbuf Buffer that contains received TCP segment without IP header. + @param Left The sequence number of the window's left edge. + @param Right The sequence number of the window's right edge. + + @return 0, the data is successfully trimmed. + +**/ +STATIC +INTN +TcpTrimSegment ( + IN NET_BUF *Nbuf, + IN TCP_SEQNO Left, + IN TCP_SEQNO Right + ) +{ + TCP_SEG *Seg; + TCP_SEQNO Urg; + UINT32 Drop; + + Seg = TCPSEG_NETBUF (Nbuf); + + // + // If the segment is completely out of window, + // truncate every thing, include SYN and FIN. + // + if (TCP_SEQ_LEQ (Seg->End, Left) || TCP_SEQ_LEQ (Right, Seg->Seq)) { + + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN); + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN); + + Seg->Seq = Seg->End; + NetbufTrim (Nbuf, Nbuf->TotalSize, NET_BUF_HEAD); + return 0; + } + + // + // Adjust the buffer header + // + if (TCP_SEQ_LT (Seg->Seq, Left)) { + + Drop = TCP_SUB_SEQ (Left, Seg->Seq); + Urg = Seg->Seq + Seg->Urg; + Seg->Seq = Left; + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN); + Drop--; + } + + // + // Adjust the urgent point + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG)) { + + if (TCP_SEQ_LT (Urg, Seg->Seq)) { + + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG); + } else { + Seg->Urg = (UINT16) TCP_SUB_SEQ (Urg, Seg->Seq); + } + } + + if (Drop) { + NetbufTrim (Nbuf, Drop, NET_BUF_HEAD); + } + } + + // + // Adjust the buffer tail + // + if (TCP_SEQ_GT (Seg->End, Right)) { + + Drop = TCP_SUB_SEQ (Seg->End, Right); + Seg->End = Right; + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN); + Drop--; + } + + if (Drop) { + NetbufTrim (Nbuf, Drop, NET_BUF_TAIL); + } + } + + ASSERT (TcpVerifySegment (Nbuf)); + return 0; +} + + +/** + Trim off the data outside the tcb's receive window. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Nbuf Pointer to the NET_BUF containing the received tcp segment. + + @return 0, the data is trimmed. + +**/ +INTN +TcpTrimInWnd ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + return TcpTrimSegment (Nbuf, Tcb->RcvNxt, Tcb->RcvWl2 + Tcb->RcvWnd); +} + + +/** + Process the data and FIN flag, check whether to deliver + data to the socket layer. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 No error occurred to deliver data. + @retval -1 Error condition occurred. Proper response is to reset the + connection. + +**/ +INTN +TcpDeliverData ( + IN TCP_CB *Tcb + ) +{ + NET_LIST_ENTRY *Entry; + NET_BUF *Nbuf; + TCP_SEQNO Seq; + TCP_SEG *Seg; + UINT32 Urgent; + + ASSERT (Tcb && Tcb->Sk); + + // + // make sure there is some data queued, + // and TCP is in a proper state + // + if (NetListIsEmpty (&Tcb->RcvQue) || !TCP_CONNECTED (Tcb->State)) { + + return 0; + } + + // + // Deliver data to the socket layer + // + Entry = Tcb->RcvQue.ForwardLink; + Seq = Tcb->RcvNxt; + + while (Entry != &Tcb->RcvQue) { + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + Seg = TCPSEG_NETBUF (Nbuf); + + ASSERT (TcpVerifySegment (Nbuf)); + ASSERT (Nbuf->Tcp == NULL); + + if (TCP_SEQ_GT (Seg->Seq, Seq)) { + break; + } + + Entry = Entry->ForwardLink; + Seq = Seg->End; + Tcb->RcvNxt = Seq; + + NetListRemoveEntry (&Nbuf->List); + + // + // RFC793 Eighth step: process FIN in sequence + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + + // + // The peer sends to us junky data after FIN, + // reset the connection. + // + if (!NetListIsEmpty (&Tcb->RcvQue)) { + TCP4_DEBUG_ERROR (("TcpDeliverData: data received after" + " FIN from peer of TCB %x, reset connection\n", Tcb)); + + NetbufFree (Nbuf); + return -1; + } + + TCP4_DEBUG_TRACE (("TcpDeliverData: processing FIN " + "from peer of TCB %x\n", Tcb)); + + switch (Tcb->State) { + case TCP_SYN_RCVD: + case TCP_ESTABLISHED: + + TcpSetState (Tcb, TCP_CLOSE_WAIT); + break; + + case TCP_FIN_WAIT_1: + + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_CLOSING); + break; + } + + // + // fall through + // + case TCP_FIN_WAIT_2: + + TcpSetState (Tcb, TCP_TIME_WAIT); + TcpClearAllTimer (Tcb); + + if (Tcb->TimeWaitTimeout != 0) { + + TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout); + } else { + + TCP4_DEBUG_WARN (("Connection closed immediately " + "because app disables TIME_WAIT timer for %x\n", Tcb)); + + TcpSendAck (Tcb); + TcpClose (Tcb); + } + break; + + case TCP_CLOSE_WAIT: + case TCP_CLOSING: + case TCP_LAST_ACK: + case TCP_TIME_WAIT: + // + // The peer sends to us junk FIN byte. Discard + // the buffer then reset the connection + // + NetbufFree (Nbuf); + return -1; + break; + } + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + + Seg->End--; + } + + // + // Don't delay the ack if PUSH flag is on. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_PSH)) { + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + } + + if (Nbuf->TotalSize) { + Urgent = 0; + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) && + TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvUp)) { + + if (TCP_SEQ_LEQ (Seg->End, Tcb->RcvUp)) { + Urgent = Nbuf->TotalSize; + } else { + Urgent = TCP_SUB_SEQ (Tcb->RcvUp, Seg->Seq) + 1; + } + } + + SockDataRcvd (Tcb->Sk, Nbuf, Urgent); + } + + if (TCP_FIN_RCVD (Tcb->State)) { + + SockNoMoreData (Tcb->Sk); + } + + NetbufFree (Nbuf); + } + + return 0; +} + + +/** + Store the data into the reassemble queue. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Nbuf Pointer to the buffer containing the data to be queued. + + @return None. + +**/ +VOID +TcpQueueData ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + TCP_SEG *Seg; + NET_LIST_ENTRY *Head; + NET_LIST_ENTRY *Prev; + NET_LIST_ENTRY *Cur; + NET_BUF *Node; + + ASSERT (Tcb && Nbuf && (Nbuf->Tcp == NULL)); + + NET_GET_REF (Nbuf); + + Seg = TCPSEG_NETBUF (Nbuf); + Head = &Tcb->RcvQue; + + // + // Fast path to process normal case. That is, + // no out-of-order segments are received. + // + if (NetListIsEmpty (Head)) { + + NetListInsertTail (Head, &Nbuf->List); + return ; + } + + // + // Find the point to insert the buffer + // + for (Prev = Head, Cur = Head->ForwardLink; + Cur != Head; + Prev = Cur, Cur = Cur->ForwardLink) { + + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + + if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->Seq)) { + break; + } + } + + // + // Check whether the current segment overlaps with the + // previous segment. + // + if (Prev != Head) { + Node = NET_LIST_USER_STRUCT (Prev, NET_BUF, List); + + if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->End)) { + + if (TCP_SEQ_LEQ (Seg->End, TCPSEG_NETBUF (Node)->End)) { + + NetbufFree (Nbuf); + return ; + } + + TcpTrimSegment (Nbuf, TCPSEG_NETBUF (Node)->End, Seg->End); + } + } + + NetListInsertHead (Prev, &Nbuf->List); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + + // + // Check the segments after the insert point. + // + while (Cur != Head) { + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + + if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->End, Seg->End)) { + + Cur = Cur->ForwardLink; + + NetListRemoveEntry (&Node->List); + NetbufFree (Node); + continue; + } + + if (TCP_SEQ_LT (TCPSEG_NETBUF (Node)->Seq, Seg->End)) { + + if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->Seq, Seg->Seq)) { + + NetListRemoveEntry (&Nbuf->List); + NetbufFree (Nbuf); + return ; + } + + TcpTrimSegment (Nbuf, Seg->Seq, TCPSEG_NETBUF (Node)->Seq); + break; + } + + Cur = Cur->ForwardLink; + } +} + + +/** + Ajust the send queue or the retransmit queue. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Ack The acknowledge seuqence number of the received segment. + + @return None. + +**/ +VOID +TcpAdjustSndQue ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Ack + ) +{ + NET_LIST_ENTRY *Head; + NET_LIST_ENTRY *Cur; + NET_BUF *Node; + TCP_SEG *Seg; + + Head = &Tcb->SndQue; + Cur = Head->ForwardLink; + + while (Cur != Head) { + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + Seg = TCPSEG_NETBUF (Node); + + if (TCP_SEQ_GEQ (Seg->Seq, Ack)) { + break; + } + + // + // Remove completely ACKed segments + // + if (TCP_SEQ_LEQ (Seg->End, Ack)) { + Cur = Cur->ForwardLink; + + NetListRemoveEntry (&Node->List); + NetbufFree (Node); + continue; + } + + TcpTrimSegment (Node, Ack, Seg->End); + break; + } +} + + +/** + Process the received TCP segments. + + @param Nbuf Buffer that contains received TCP segment without IP header. + @param Src Source address of the segment, or the peer's IP address. + @param Dst Destination address of the segment, or the local end's IP + address. + + @retval 0 Segment is processed successfully. It is either accepted or + discarded. But no connection is reset by the segment. + @retval -1 A connection is reset by the segment. + +**/ +INTN +TcpInput ( + IN NET_BUF *Nbuf, + IN UINT32 Src, + IN UINT32 Dst + ) +{ + TCP_CB *Tcb; + TCP_CB *Parent; + TCP_OPTION Option; + TCP_HEAD *Head; + INT32 Len; + TCP_SEG *Seg; + TCP_SEQNO Right; + TCP_SEQNO Urg; + + NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE); + + Parent = NULL; + Tcb = NULL; + + Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL); + Len = Nbuf->TotalSize - (Head->HeadLen << 2); + + if ((Head->HeadLen < 5) || (Len < 0) || + TcpChecksum (Nbuf, NetPseudoHeadChecksum (Src, Dst, 6, 0))) { + + TCP4_DEBUG_TRACE (("TcpInput: received an mal-formated packet\n")); + goto DISCARD; + } + + if (TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)) { + Len++; + } + + if (TCP_FLG_ON (Head->Flag, TCP_FLG_FIN)) { + Len++; + } + + Tcb = TcpLocateTcb ( + Head->DstPort, + Dst, + Head->SrcPort, + Src, + (BOOLEAN) TCP_FLG_ON (Head->Flag, TCP_FLG_SYN) + ); + + if ((Tcb == NULL) || (Tcb->State == TCP_CLOSED)) { + TCP4_DEBUG_TRACE (("TcpInput: send reset because no TCB find\n")); + + Tcb = NULL; + goto SEND_RESET; + } + + Seg = TcpFormatNetbuf (Tcb, Nbuf); + + // + // RFC1122 recommended reaction to illegal option + // (in fact, an illegal option length) is reset. + // + if (TcpParseOption (Nbuf->Tcp, &Option) == -1) { + TCP4_DEBUG_ERROR (("TcpInput: reset the peer because" + " of mal-format option for Tcb %x\n", Tcb)); + + goto SEND_RESET; + } + + // + // From now on, the segment is headless + // + NetbufTrim (Nbuf, (Head->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + // + // TODO: add fast path process here + // + + // + // Process the segment in LISTEN state. + // + if (Tcb->State == TCP_LISTEN) { + // + // First step: Check RST + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + TCP4_DEBUG_WARN (("TcpInput: discard a reset segment " + "for TCB %x in listening\n", Tcb)); + + goto DISCARD; + } + + // + // Second step: Check ACK. + // Any ACK sent to TCP in LISTEN is reseted. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + TCP4_DEBUG_WARN (("TcpInput: send reset because of" + " segment with ACK for TCB %x in listening\n", Tcb)); + + goto SEND_RESET; + } + + // + // Third step: Check SYN + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + // + // create a child TCB to handle the data + // + Parent = Tcb; + + Tcb = TcpCloneTcb (Parent); + if (Tcb == NULL) { + TCP4_DEBUG_ERROR (("TcpInput: discard a segment because" + "failed to clone a child for TCB%x\n", Tcb)); + + goto DISCARD; + } + + TCP4_DEBUG_TRACE (("TcpInput: create a child for TCB %x" + " in listening\n", Tcb)); + + // + // init the TCB structure + // + Tcb->LocalEnd.Ip = Dst; + Tcb->LocalEnd.Port = Head->DstPort; + Tcb->RemoteEnd.Ip = Src; + Tcb->RemoteEnd.Port = Head->SrcPort; + + TcpInitTcbLocal (Tcb); + TcpInitTcbPeer (Tcb, Seg, &Option); + + TcpSetState (Tcb, TCP_SYN_RCVD); + TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout); + TcpTrimInWnd (Tcb, Nbuf); + + goto StepSix; + } + + goto DISCARD; + + } else if (Tcb->State == TCP_SYN_SENT) { + // + // First step: Check ACK bit + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK) && (Seg->Ack != Tcb->Iss + 1)) { + + TCP4_DEBUG_WARN (("TcpInput: send reset because of " + "wrong ACK received for TCB %x in SYN_SENT\n", Tcb)); + + goto SEND_RESET; + } + + // + // Second step: Check RST bit + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + + TCP4_DEBUG_WARN (("TcpInput: connection reset by" + " peer for TCB%x in SYN_SENT\n", Tcb)); + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET); + goto DROP_CONNECTION; + } else { + + TCP4_DEBUG_WARN (("TcpInput: discard a reset segment " + "because of no ACK for TCB%x in SYN_SENT\n", Tcb)); + + goto DISCARD; + } + } + + // + // Third step: Check security and precedence. Skipped + // + + // + // Fourth step: Check SYN. Pay attention to sitimulatous open + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + + TcpInitTcbPeer (Tcb, Seg, &Option); + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + + Tcb->SndUna = Seg->Ack; + } + + TcpClearTimer (Tcb, TCP_TIMER_REXMIT); + + if (TCP_SEQ_GT (Tcb->SndUna, Tcb->Iss)) { + + TcpSetState (Tcb, TCP_ESTABLISHED); + + TcpClearTimer (Tcb, TCP_TIMER_CONNECT); + TcpDeliverData (Tcb); + + if ((Tcb->CongestState == TCP_CONGEST_OPEN) && + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { + + TcpComputeRtt (Tcb, Tcb->RttMeasure); + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + } + + TcpTrimInWnd (Tcb, Nbuf); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + + TCP4_DEBUG_TRACE (("TcpInput: connection established" + " for TCB %x in SYN_SENT\n", Tcb)); + + goto StepSix; + } else { + // + // Received a SYN segment without ACK, simultanous open. + // + TcpSetState (Tcb, TCP_SYN_RCVD); + + ASSERT (Tcb->SndNxt == Tcb->Iss + 1); + TcpAdjustSndQue (Tcb, Tcb->SndNxt); + + TcpTrimInWnd (Tcb, Nbuf); + + TCP4_DEBUG_WARN (("TcpInput: simultanous open " + "for TCB %x in SYN_SENT\n", Tcb)); + + goto StepSix; + } + } + + goto DISCARD; + } + + // + // Process segment in SYN_RCVD or TCP_CONNECTED states + // + + // + // First step: Check whether SEG.SEQ is acceptable + // + if (!TcpSeqAcceptable (Tcb, Seg)) { + TCP4_DEBUG_WARN (("TcpInput: sequence acceptance" + " test failed for segment of TCB %x\n", Tcb)); + + if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + TcpSendAck (Tcb); + } + + goto DISCARD; + } + + if ((TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2)) && + (Tcb->RcvWl2 == Seg->End) && + !TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN | TCP_FLG_FIN)) { + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + } + + // + // Second step: Check the RST + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + + TCP4_DEBUG_WARN (("TcpInput: connection reset for TCB %x\n", Tcb)); + + if (Tcb->State == TCP_SYN_RCVD) { + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_REFUSED); + + // + // This TCB comes from either a LISTEN TCB, + // or active open TCB with simultanous open. + // Do NOT signal user CONNECTION refused + // if it comes from a LISTEN TCB. + // + } else if ((Tcb->State == TCP_ESTABLISHED) || + (Tcb->State == TCP_FIN_WAIT_1) || + (Tcb->State == TCP_FIN_WAIT_2) || + (Tcb->State == TCP_CLOSE_WAIT) + ) { + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET); + + } else { + // + // TODO: set socket error to CLOSED + // + } + + goto DROP_CONNECTION; + } + + // + // Trim the data and flags. + // + TcpTrimInWnd (Tcb, Nbuf); + + // + // Third step: Check security and precedence, Ignored + // + + // + // Fourth step: Check the SYN bit. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + + TCP4_DEBUG_WARN (("TcpInput: connection reset " + "because received extra SYN for TCB %x\n", Tcb)); + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET); + goto RESET_THEN_DROP; + } + + // + // Fifth step: Check the ACK + // + if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + TCP4_DEBUG_WARN (("TcpInput: segment discard because" + " of no ACK for connected TCB %x\n", Tcb)); + + goto DISCARD; + + } + + if (Tcb->State == TCP_SYN_RCVD) { + + if (TCP_SEQ_LT (Tcb->SndUna, Seg->Ack) && + TCP_SEQ_LEQ (Seg->Ack, Tcb->SndNxt)) { + + Tcb->SndWnd = Seg->Wnd; + Tcb->SndWndMax = NET_MAX (Tcb->SndWnd, Tcb->SndWndMax); + Tcb->SndWl1 = Seg->Seq; + Tcb->SndWl2 = Seg->Ack; + TcpSetState (Tcb, TCP_ESTABLISHED); + + TcpClearTimer (Tcb, TCP_TIMER_CONNECT); + TcpDeliverData (Tcb); + + TCP4_DEBUG_TRACE (("TcpInput: connection established " + " for TCB %x in SYN_RCVD\n", Tcb)); + + // + // Continue the process as ESTABLISHED state + // + } else { + TCP4_DEBUG_WARN (("TcpInput: send reset because of" + " wrong ACK for TCB %x in SYN_RCVD\n", Tcb)); + + goto SEND_RESET; + } + } + + if (TCP_SEQ_LT (Seg->Ack, Tcb->SndUna)) { + + TCP4_DEBUG_WARN (("TcpInput: ignore the out-of-data" + " ACK for connected TCB %x\n", Tcb)); + + goto StepSix; + + } else if (TCP_SEQ_GT (Seg->Ack, Tcb->SndNxt)) { + + TCP4_DEBUG_WARN (("TcpInput: discard segment for " + "future ACK for connected TCB %x\n", Tcb)); + + TcpSendAck (Tcb); + goto DISCARD; + } + + // + // From now on: SND.UNA <= SEG.ACK <= SND.NXT. + // + if (TCP_FLG_ON (Option.Flag, TCP_OPTION_RCVD_TS)) { + // + // update TsRecent as specified in page 16 RFC1323. + // RcvWl2 equals to the variable "LastAckSent" + // defined there. + // + if (TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvWl2) && + TCP_SEQ_LT (Tcb->RcvWl2, Seg->End)) { + + Tcb->TsRecent = Option.TSVal; + Tcb->TsRecentAge = mTcpTick; + } + + TcpComputeRtt (Tcb, TCP_SUB_TIME (mTcpTick, Option.TSEcr)); + + } else if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { + + ASSERT (Tcb->CongestState == TCP_CONGEST_OPEN); + + TcpComputeRtt (Tcb, Tcb->RttMeasure); + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + } + + if (Seg->Ack == Tcb->SndNxt) { + + TcpClearTimer (Tcb, TCP_TIMER_REXMIT); + } else { + + TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto); + } + + // + // Count duplicate acks. + // + if ((Seg->Ack == Tcb->SndUna) && + (Tcb->SndUna != Tcb->SndNxt) && + (Seg->Wnd == Tcb->SndWnd) && + (0 == Len)) { + + Tcb->DupAck++; + } else { + + Tcb->DupAck = 0; + } + + // + // Congestion avoidance, fast recovery and fast retransmission. + // + if (((Tcb->CongestState == TCP_CONGEST_OPEN) && (Tcb->DupAck < 3)) || + (Tcb->CongestState == TCP_CONGEST_LOSS)) { + + if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) { + + if (Tcb->CWnd < Tcb->Ssthresh) { + + Tcb->CWnd += Tcb->SndMss; + } else { + + Tcb->CWnd += NET_MAX (Tcb->SndMss * Tcb->SndMss / Tcb->CWnd, 1); + } + + Tcb->CWnd = NET_MIN (Tcb->CWnd, TCP_MAX_WIN << Tcb->SndWndScale); + } + + if (Tcb->CongestState == TCP_CONGEST_LOSS) { + TcpFastLossRecover (Tcb, Seg); + } + } else { + + TcpFastRecover (Tcb, Seg); + } + + if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) { + + TcpAdjustSndQue (Tcb, Seg->Ack); + Tcb->SndUna = Seg->Ack; + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) && + (TCP_SEQ_LT (Tcb->SndUp, Seg->Ack))) { + + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG); + } + } + + // + // Update window info + // + if (TCP_SEQ_LT (Tcb->SndWl1, Seg->Seq) || + ((Tcb->SndWl1 == Seg->Seq) && TCP_SEQ_LEQ (Tcb->SndWl2, Seg->Ack))) { + + Right = Seg->Ack + Seg->Wnd; + + if (TCP_SEQ_LT (Right, Tcb->SndWl2 + Tcb->SndWnd)) { + + if ((Tcb->SndWl1 == Seg->Seq) && + (Tcb->SndWl2 == Seg->Ack) && + (Len == 0)) { + + goto NO_UPDATE; + } + + TCP4_DEBUG_WARN (("TcpInput: peer shrinks the" + " window for connected TCB %x\n", Tcb)); + + if ((Tcb->CongestState == TCP_CONGEST_RECOVER) && + (TCP_SEQ_LT (Right, Tcb->Recover))) { + + Tcb->Recover = Right; + } + + if ((Tcb->CongestState == TCP_CONGEST_LOSS) && + (TCP_SEQ_LT (Right, Tcb->LossRecover))) { + + Tcb->LossRecover = Right; + } + + if (TCP_SEQ_LT (Right, Tcb->SndNxt)) { + + Tcb->SndNxt = Right; + + if (Right == Tcb->SndUna) { + + TcpClearTimer (Tcb, TCP_TIMER_REXMIT); + TcpSetProbeTimer (Tcb); + } + } + } + + Tcb->SndWnd = Seg->Wnd; + Tcb->SndWndMax = NET_MAX (Tcb->SndWnd, Tcb->SndWndMax); + Tcb->SndWl1 = Seg->Seq; + Tcb->SndWl2 = Seg->Ack; + } + +NO_UPDATE: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT) && + (Tcb->SndUna == Tcb->SndNxt)) { + + TCP4_DEBUG_TRACE (("TcpInput: local FIN is ACKed by" + " peer for connected TCB %x\n", Tcb)); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED); + } + + // + // Transit the state if proper. + // + switch (Tcb->State) { + case TCP_FIN_WAIT_1: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_FIN_WAIT_2); + + TcpClearAllTimer (Tcb); + TcpSetTimer (Tcb, TCP_TIMER_FINWAIT2, Tcb->FinWait2Timeout); + } + + case TCP_FIN_WAIT_2: + + break; + + case TCP_CLOSE_WAIT: + break; + + case TCP_CLOSING: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_TIME_WAIT); + + TcpClearAllTimer (Tcb); + + if (Tcb->TimeWaitTimeout != 0) { + + TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout); + } else { + + TCP4_DEBUG_WARN (("Connection closed immediately " + "because app disables TIME_WAIT timer for %x\n", Tcb)); + + TcpClose (Tcb); + } + } + break; + + case TCP_LAST_ACK: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_CLOSED); + } + + break; + + case TCP_TIME_WAIT: + + TcpSendAck (Tcb); + + if (Tcb->TimeWaitTimeout != 0) { + + TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout); + } else { + + TCP4_DEBUG_WARN (("Connection closed immediately " + "because app disables TIME_WAIT timer for %x\n", Tcb)); + + TcpClose (Tcb); + } + break; + } + + // + // Sixth step: Check the URG bit.update the Urg point + // if in TCP_CAN_RECV, otherwise, leave the RcvUp intact. + // +StepSix: + + Tcb->Idle = 0; + TcpSetKeepaliveTimer (Tcb); + + if (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_PROBE)) { + + TcpClearTimer (Tcb, TCP_TIMER_PROBE); + } + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG) && + !TCP_FIN_RCVD (Tcb->State)) { + + TCP4_DEBUG_TRACE (("TcpInput: received urgent data " + "from peer for connected TCB %x\n", Tcb)); + + Urg = Seg->Seq + Seg->Urg; + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) && + TCP_SEQ_GT (Urg, Tcb->RcvUp)) { + + Tcb->RcvUp = Urg; + } else { + + Tcb->RcvUp = Urg; + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG); + } + } + + // + // Seventh step: Process the segment data + // + if (Seg->End != Seg->Seq) { + + if (TCP_FIN_RCVD (Tcb->State)) { + + TCP4_DEBUG_WARN (("TcpInput: connection reset because" + " data is lost for connected TCB %x\n", Tcb)); + + goto RESET_THEN_DROP; + } + + if (TCP_LOCAL_CLOSED (Tcb->State) && (Nbuf->TotalSize != 0)) { + TCP4_DEBUG_WARN (("TcpInput: connection reset because" + " data is lost for connected TCB %x\n", Tcb)); + + goto RESET_THEN_DROP; + } + + TcpQueueData (Tcb, Nbuf); + if (TcpDeliverData (Tcb) == -1) { + goto RESET_THEN_DROP; + } + + if (!NetListIsEmpty (&Tcb->RcvQue)) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + } + } + + // + // Eighth step: check the FIN. + // This step is moved to TcpDeliverData. FIN will be + // processed in sequence there. Check the comments in + // the beginning of the file header for information. + // + + // + // Tcb is a new child of the listening Parent, + // commit it. + // + if (Parent) { + Tcb->Parent = Parent; + TcpInsertTcb (Tcb); + } + + if ((Tcb->State != TCP_CLOSED) && + (!TcpToSendData (Tcb, 0)) && + (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || Nbuf->TotalSize)) { + + TcpToSendAck (Tcb); + } + + NetbufFree (Nbuf); + return 0; + +RESET_THEN_DROP: + TcpSendReset (Tcb, Head, Len, Dst, Src); + +DROP_CONNECTION: + ASSERT (Tcb && Tcb->Sk); + + NetbufFree (Nbuf); + TcpClose (Tcb); + + return -1; + +SEND_RESET: + + TcpSendReset (Tcb, Head, Len, Dst, Src); + +DISCARD: + + // + // Tcb is a child of Parent, and it doesn't survive + // + TCP4_DEBUG_WARN (("Tcp4Input: Discard a packet\n")); + NetbufFree (Nbuf); + + if (Parent && Tcb) { + + ASSERT (Tcb->Sk); + TcpClose (Tcb); + } + + return 0; +} + + +/** + Process the received ICMP error messages for TCP. + + @param Nbuf Buffer that contains part of the TCP segment without IP header + truncated from the ICMP error packet. + @param IcmpErr The ICMP error code interpreted from ICMP error packet. + @param Src Source address of the ICMP error message. + @param Dst Destination address of the ICMP error message. + + @return None. + +**/ +VOID +TcpIcmpInput ( + IN NET_BUF *Nbuf, + IN ICMP_ERROR IcmpErr, + IN UINT32 Src, + IN UINT32 Dst + ) +{ + TCP_HEAD *Head; + TCP_CB *Tcb; + TCP_SEQNO Seq; + EFI_STATUS IcmpErrStatus; + BOOLEAN IcmpErrIsHard; + BOOLEAN IcmpErrNotify; + + Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL); + Tcb = TcpLocateTcb ( + Head->DstPort, + Dst, + Head->SrcPort, + Src, + FALSE + ); + if (Tcb == NULL || Tcb->State == TCP_CLOSED) { + + goto CLEAN_EXIT; + } + + // + // Validate the sequence number. + // + Seq = NTOHL (Head->Seq); + if (!(TCP_SEQ_LEQ (Tcb->SndUna, Seq) && TCP_SEQ_LT (Seq, Tcb->SndNxt))) { + + goto CLEAN_EXIT; + } + + IcmpErrStatus = IpIoGetIcmpErrStatus (IcmpErr, &IcmpErrIsHard, &IcmpErrNotify); + + if (IcmpErrNotify) { + + SOCK_ERROR (Tcb->Sk, IcmpErrStatus); + } + + if (IcmpErrIsHard) { + + TcpClose (Tcb); + } + +CLEAN_EXIT: + NetbufFree (Nbuf); +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c new file mode 100644 index 0000000000..2f7a49fcb8 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c @@ -0,0 +1,117 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Tcp4Io.c + +Abstract: + + I/O interfaces between TCP and IpIo. + + +**/ + + +#include "Tcp4Main.h" + + +/** + Packet receive callback function provided to IP_IO, used to call + the proper function to handle the packet received by IP. + + @param Status Status of the received packet. + @param IcmpErr ICMP error number. + @param NetSession Pointer to the net session of this packet. + @param Pkt Pointer to the recieved packet. + @param Context Pointer to the context configured in IpIoOpen(), not used + now. + + @return None + +**/ +VOID +Tcp4RxCallback ( + IN EFI_STATUS Status, + IN ICMP_ERROR IcmpErr, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Pkt, + IN VOID *Context OPTIONAL + ) +{ + if (EFI_SUCCESS == Status) { + TcpInput (Pkt, NetSession->Source, NetSession->Dest); + } else { + TcpIcmpInput (Pkt, IcmpErr, NetSession->Source, NetSession->Dest); + } +} + + +/** + Send the segment to IP via IpIo function. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Nbuf Pointer to the TCP segment to be sent. + @param Src Source address of the TCP segment. + @param Dest Destination address of the TCP segment. + + @retval 0 The segment was sent out successfully. + @retval -1 The segment was failed to send. + +**/ +INTN +TcpSendIpPacket ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf, + IN UINT32 Src, + IN UINT32 Dest + ) +{ + EFI_STATUS Status; + IP_IO *IpIo; + IP_IO_OVERRIDE Override; + SOCKET *Sock; + VOID *IpSender; + TCP4_PROTO_DATA *TcpProto; + + if (NULL == Tcb) { + + IpIo = NULL; + IpSender = IpIoFindSender (&IpIo, Src); + + if (IpSender == NULL) { + TCP4_DEBUG_WARN (("TcpSendIpPacket: No appropriate IpSender.\n")); + return -1; + } + } else { + + Sock = Tcb->Sk; + TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved; + IpIo = TcpProto->TcpService->IpIo; + IpSender = Tcb->IpInfo; + } + + Override.TypeOfService = 0; + Override.TimeToLive = 255; + Override.DoNotFragment = FALSE; + Override.Protocol = EFI_IP_PROTO_TCP; + EFI_IP4 (Override.GatewayAddress) = 0; + EFI_IP4 (Override.SourceAddress) = Src; + + Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, Dest, &Override); + + if (EFI_ERROR (Status)) { + TCP4_DEBUG_ERROR (("TcpSendIpPacket: return %r error\n", Status)); + return -1; + } + + return 0; +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c new file mode 100644 index 0000000000..8a8cd8a9e3 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c @@ -0,0 +1,564 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Tcp4Main.c + +Abstract: + + Implementation of TCP4 protocol services. + + +**/ + +#include "Tcp4Main.h" + + +/** + Check the integrity of the data buffer. + + @param DataLen The total length of the data buffer. + @param FragmentCount The fragment count of the fragment table. + @param FragmentTable Pointer to the fragment table of the data + buffer. + + @retval EFI_SUCCESS The integrity check is passed. + @retval EFI_INVALID_PARAMETER The integrity check is failed. + +**/ +STATIC +EFI_STATUS +Tcp4ChkDataBuf ( + IN UINT32 DataLen, + IN UINT32 FragmentCount, + IN EFI_TCP4_FRAGMENT_DATA *FragmentTable + ) +{ + UINT32 Index; + + UINT32 Len; + + for (Index = 0, Len = 0; Index < FragmentCount; Index++) { + Len = Len + FragmentTable[Index].FragmentLength; + } + + if (DataLen != Len) { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + + +/** + Get the current operational status. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance. + @param Tcp4State Pointer to the buffer to receive the current TCP + state. + @param Tcp4ConfigData Pointer to the buffer to receive the current TCP + configuration. + @param Ip4ModeData Pointer to the buffer to receive the current + IPv4 configuration. + @param MnpConfigData Pointer to the buffer to receive the current MNP + configuration data indirectly used by the TCPv4 + Instance. + @param SnpModeData Pointer to the buffer to receive the current SNP + configuration data indirectly used by the TCPv4 + Instance. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this + instance hasn't been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +Tcp4GetModeData ( + IN CONST EFI_TCP4_PROTOCOL * This, + OUT EFI_TCP4_CONNECTION_STATE * Tcp4State OPTIONAL, + OUT EFI_TCP4_CONFIG_DATA * Tcp4ConfigData OPTIONAL, + OUT EFI_IP4_MODE_DATA * Ip4ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA * MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE * SnpModeData OPTIONAL + ) +{ + TCP4_MODE_DATA TcpMode; + SOCKET *Sock; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + TcpMode.Tcp4State = Tcp4State; + TcpMode.Tcp4ConfigData = Tcp4ConfigData; + TcpMode.Ip4ModeData = Ip4ModeData; + TcpMode.MnpConfigData = MnpConfigData; + TcpMode.SnpModeData = SnpModeData; + + return SockGetMode (Sock, &TcpMode); +} + + +/** + Initialize or brutally reset the operational parameters for + this EFI TCPv4 instance. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance. + @param TcpConfigData Pointer to the configure data to configure the + instance. + + @retval EFI_SUCCESS The operational settings are set, changed, or + reset successfully. + @retval EFI_NO_MAPPING When using a default address, configuration + (through DHCP, BOOTP, RARP, etc.) is not + finished. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_ACCESS_DENIED Configuring TCP instance when it is already + configured. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval EFI_UNSUPPORTED One or more of the control options are not + supported in the implementation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + +**/ +EFI_STATUS +EFIAPI +Tcp4Configure ( + IN EFI_TCP4_PROTOCOL * This, + IN EFI_TCP4_CONFIG_DATA * TcpConfigData OPTIONAL + ) +{ + EFI_TCP4_OPTION *Option; + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + // + // Tcp protocol related parameter check will be conducted here + // + if (NULL != TcpConfigData) { + if ((EFI_IP4 (TcpConfigData->AccessPoint.RemoteAddress) != 0) && + !Ip4IsUnicast (EFI_NTOHL (TcpConfigData->AccessPoint.RemoteAddress), 0)) { + return EFI_INVALID_PARAMETER; + } + + if (!TcpConfigData->AccessPoint.UseDefaultAddress) { + if (!Ip4IsUnicast (EFI_NTOHL (TcpConfigData->AccessPoint.StationAddress), 0) || + !IP4_IS_VALID_NETMASK (EFI_NTOHL (TcpConfigData->AccessPoint.SubnetMask)) + ) { + return EFI_INVALID_PARAMETER; + } + } + + if (TcpConfigData->AccessPoint.ActiveFlag && + (0 == TcpConfigData->AccessPoint.RemotePort || + (EFI_IP4 (TcpConfigData->AccessPoint.RemoteAddress) == 0)) + ) { + return EFI_INVALID_PARAMETER; + } + + Option = TcpConfigData->ControlOption; + if ((NULL != Option) && + (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) { + return EFI_UNSUPPORTED; + } + } + + Sock = SOCK_FROM_THIS (This); + + if (NULL == TcpConfigData) { + return SockFlush (Sock); + } + + Status = SockConfigure (Sock, TcpConfigData); + + if (EFI_NO_MAPPING == Status) { + Sock->ConfigureState = SO_NO_MAPPING; + } + + return Status; +} + + +/** + Add or delete routing entries. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance. + @param DeleteRoute If TRUE, delete the specified route from routing + table; if FALSE, add the specified route to + routing table. + @param SubnetAddress The destination network. + @param SubnetMask The subnet mask for the destination network. + @param GatewayAddress The gateway address for this route. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance has not been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (through DHCP, BOOTP, RARP, etc.) is not + finished. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to add the + entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table. + @retval EFI_ACCESS_DENIED This route is already in the routing table. + @retval EFI_UNSUPPORTED The TCP driver does not support this operation. + +**/ +EFI_STATUS +EFIAPI +Tcp4Routes ( + IN EFI_TCP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ) +{ + SOCKET *Sock; + TCP4_ROUTE_INFO RouteInfo; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + RouteInfo.DeleteRoute = DeleteRoute; + RouteInfo.SubnetAddress = SubnetAddress; + RouteInfo.SubnetMask = SubnetMask; + RouteInfo.GatewayAddress = GatewayAddress; + + return SockRoute (Sock, &RouteInfo); +} + + +/** + Initiate a nonblocking TCP connection request for an active TCP instance. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance + @param ConnectionToken Pointer to the connection token to return when + the TCP three way handshake finishes. + + @retval EFI_SUCCESS The connection request is successfully + initiated. + @retval EFI_NOT_STARTED This EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED The instance is not configured as an active one + or it is not in Tcp4StateClosed state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resource to + initiate the active open. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp4Connect ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken + ) +{ + SOCKET *Sock; + + if (NULL == This || + NULL == ConnectionToken || + NULL == ConnectionToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockConnect (Sock, ConnectionToken); +} + + +/** + Listen on the passive instance to accept an incoming connection request. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance + @param ListenToken Pointer to the listen token to return when + operation finishes. + + @retval EFI_SUCCESS The listen token has been queued successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED The instatnce is not a passive one or it is not + in Tcp4StateListen state or a same listen token + has already existed in the listen token queue of + this TCP instance. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish + the operation. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp4Accept ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_LISTEN_TOKEN *ListenToken + ) +{ + SOCKET *Sock; + + if (NULL == This || + NULL == ListenToken || + NULL == ListenToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockAccept (Sock, ListenToken); +} + + +/** + Queues outgoing data into the transmit queue + + @param This Pointer to the EFI_TCP4_PROTOCOL instance + @param Token Pointer to the completion token to queue to the + transmit queue + + @retval EFI_SUCCESS The data has been queued for transmission + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + * A transmit completion token with the same + Token-> CompletionToken.Event was already in the + transmission queue. * The current instance is in + Tcp4StateClosed state * The current instance is + a passive one and it is in Tcp4StateListen + state. * User has called Close() to disconnect + this connection. + @retval EFI_NOT_READY The completion token could not be queued because + the transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of + resource shortage. + @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or + address. + +**/ +EFI_STATUS +EFIAPI +Tcp4Transmit ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This || + NULL == Token || + NULL == Token->CompletionToken.Event || + NULL == Token->Packet.TxData || + 0 == Token->Packet.TxData->FragmentCount || + 0 == Token->Packet.TxData->DataLength + ) { + return EFI_INVALID_PARAMETER; + } + + Status = Tcp4ChkDataBuf ( + Token->Packet.TxData->DataLength, + Token->Packet.TxData->FragmentCount, + Token->Packet.TxData->FragmentTable + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Sock = SOCK_FROM_THIS (This); + + return SockSend (Sock, Token); + +} + + +/** + Place an asynchronous receive request into the receiving queue. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance. + @param Token Pointer to a token that is associated with the + receive data descriptor. + + @retval EFI_SUCCESS The receive completion token was cached + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued + due to a lack of system resources. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + * A receive completion token with the same + Token->CompletionToken.Event was already in the + receive queue. * The current instance is in + Tcp4StateClosed state. * The current instance is + a passive one and it is in Tcp4StateListen + state. * User has called Close() to disconnect + this connection. + @retval EFI_CONNECTION_FIN The communication peer has closed the connection + and there is no any buffered data in the receive + buffer of this instance. + @retval EFI_NOT_READY The receive request could not be queued because + the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +Tcp4Receive ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This || + NULL == Token || + NULL == Token->CompletionToken.Event || + NULL == Token->Packet.RxData || + 0 == Token->Packet.RxData->FragmentCount || + 0 == Token->Packet.RxData->DataLength + ) { + return EFI_INVALID_PARAMETER; + } + + Status = Tcp4ChkDataBuf ( + Token->Packet.RxData->DataLength, + Token->Packet.RxData->FragmentCount, + Token->Packet.RxData->FragmentTable + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Sock = SOCK_FROM_THIS (This); + + return SockRcv (Sock, Token); + +} + + +/** + Disconnecting a TCP connection gracefully or reset a TCP connection. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance + @param CloseToken Pointer to the close token to return when + operation finishes. + + @retval EFI_SUCCESS The operation completed successfully + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: * + Configure() has been called with TcpConfigData + set to NULL and this function has not returned. + * Previous Close() call on this instance has not + finished. + @retval EFI_INVALID_PARAMETER One ore more parameters are invalid + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the + operation + @retval EFI_DEVICE_ERROR Any unexpected and not belonged to above + category error. + +**/ +EFI_STATUS +EFIAPI +Tcp4Close ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CLOSE_TOKEN *CloseToken + ) +{ + SOCKET *Sock; + + if (NULL == This || + NULL == CloseToken || + NULL == CloseToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockClose (Sock, CloseToken, CloseToken->AbortOnClose); +} + + +/** + Abort an asynchronous connection, listen, transmission or receive request. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance. + @param Token Pointer to a token that has been issued by + Connect(), Accept(), Transmit() or Receive(). If + NULL, all pending tokens issued by above four + functions will be aborted. + + @retval EFI_UNSUPPORTED The operation is not supported in current + implementation. + +**/ +EFI_STATUS +EFIAPI +Tcp4Cancel ( + IN EFI_TCP4_PROTOCOL * This, + IN EFI_TCP4_COMPLETION_TOKEN * Token OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Poll to receive incoming data and transmit outgoing segments. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data was processed. + @retval EFI_TIMEOUT Data was dropped out of the transmission or + receive queue. Consider increasing the polling + rate. + +**/ +EFI_STATUS +EFIAPI +Tcp4Poll ( + IN EFI_TCP4_PROTOCOL *This + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h new file mode 100644 index 0000000000..dba46aa80a --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h @@ -0,0 +1,176 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Tcp4Main.h + +Abstract: + + +**/ + +#ifndef _TCP4_MAIN_H_ +#define _TCP4_MAIN_H_ + +#include "Socket.h" + +#include "Tcp4Proto.h" +#include "Tcp4Driver.h" + + +extern UINT16 mTcp4RandomPort; + +// +// Driver Produced Protocol Prototypes +// +//@MT:#include EFI_PROTOCOL_PRODUCER (Tcp4) + +#define TCP4_DEBUG_ERROR(PrintArg) NET_DEBUG_ERROR ("Tcp", PrintArg) +#define TCP4_DEBUG_WARN(PrintArg) NET_DEBUG_WARNING ("Tcp", PrintArg) +#define TCP4_DEBUG_TRACE(PrintArg) NET_DEBUG_TRACE ("Tcp", PrintArg) + +// +// Function prototype for the Tcp4 socket request handler +// +EFI_STATUS +Tcp4Dispatcher ( + IN SOCKET *Sock, + IN SOCK_REQUEST Request, + IN VOID *Data OPTIONAL + ); + +typedef struct _TCP4_MODE_DATA { + EFI_TCP4_CONNECTION_STATE *Tcp4State; + EFI_TCP4_CONFIG_DATA *Tcp4ConfigData; + EFI_IP4_MODE_DATA *Ip4ModeData; + EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData; + EFI_SIMPLE_NETWORK_MODE *SnpModeData; +} TCP4_MODE_DATA; + +typedef struct _TCP4_ROUTE_INFO { + BOOLEAN DeleteRoute; + EFI_IPv4_ADDRESS *SubnetAddress; + EFI_IPv4_ADDRESS *SubnetMask; + EFI_IPv4_ADDRESS *GatewayAddress; +} TCP4_ROUTE_INFO; + +// +// Get the mode data of a TCP instance +// +EFI_STATUS +EFIAPI +Tcp4GetModeData ( + IN CONST EFI_TCP4_PROTOCOL * This, + OUT EFI_TCP4_CONNECTION_STATE * Tcp4State OPTIONAL, + OUT EFI_TCP4_CONFIG_DATA * Tcp4ConfigData OPTIONAL, + OUT EFI_IP4_MODE_DATA * Ip4ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA * MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE * SnpModeData OPTIONAL + ); + +// +// Initialize or reset a TCP instance +// +EFI_STATUS +EFIAPI +Tcp4Configure ( + IN EFI_TCP4_PROTOCOL * This, + IN EFI_TCP4_CONFIG_DATA * TcpConfigData OPTIONAL + ); + +// +// Add a route entry to the route table +// +EFI_STATUS +EFIAPI +Tcp4Routes ( + IN EFI_TCP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ); + +// +// Issue an asynchronous connection establishment +// request to the peer +// +EFI_STATUS +EFIAPI +Tcp4Connect ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken + ); + +// +// Issue an asynchronous listent token to accept an +// incoming connection reques +// +EFI_STATUS +EFIAPI +Tcp4Accept ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_LISTEN_TOKEN *ListenToken + ); + +// +// Issue an asynchronous IO token to transmit some data +// through this TCP instance +// +EFI_STATUS +EFIAPI +Tcp4Transmit ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ); + +// +// Issue an asynchronous IO token to receive some data +// through this TCP instance +// +EFI_STATUS +Tcp4Receive ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ); + +// +// Issue an asynchronous CloseToken to close a TCP +// connection represented by instance +// +EFI_STATUS +EFIAPI +Tcp4Close ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CLOSE_TOKEN *CloseToken + ); + +// +// cancle an connect, listent or IO token +// +EFI_STATUS +EFIAPI +Tcp4Cancel ( + IN EFI_TCP4_PROTOCOL * This, + IN EFI_TCP4_COMPLETION_TOKEN * Token OPTIONAL + ); + +// +// poll data from NIC for receive +// +EFI_STATUS +EFIAPI +Tcp4Poll ( + IN EFI_TCP4_PROTOCOL *This + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c new file mode 100644 index 0000000000..7f1f141bc8 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c @@ -0,0 +1,1091 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Tcp4Misc.c + +Abstract: + + Misc support routines for tcp. + + +**/ + + +#include "Tcp4Main.h" + +NET_LIST_ENTRY mTcpRunQue = { + &mTcpRunQue, + &mTcpRunQue +}; + +NET_LIST_ENTRY mTcpListenQue = { + &mTcpListenQue, + &mTcpListenQue +}; + +TCP_SEQNO mTcpGlobalIss = 0x4d7e980b; + +STATIC CHAR16 *mTcpStateName[] = { + L"TCP_CLOSED", + L"TCP_LISTEN", + L"TCP_SYN_SENT", + L"TCP_SYN_RCVD", + L"TCP_ESTABLISHED", + L"TCP_FIN_WAIT_1", + L"TCP_FIN_WAIT_2", + L"TCP_CLOSING", + L"TCP_TIME_WAIT", + L"TCP_CLOSE_WAIT", + L"TCP_LAST_ACK" +}; + + +/** + Initialize the Tcb local related members. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None + +**/ +VOID +TcpInitTcbLocal ( + IN TCP_CB *Tcb + ) +{ + // + // Compute the checksum of the fixed parts of pseudo header + // + Tcb->HeadSum = NetPseudoHeadChecksum ( + Tcb->LocalEnd.Ip, + Tcb->RemoteEnd.Ip, + 0x06, + 0 + ); + + Tcb->Iss = TcpGetIss (); + Tcb->SndUna = Tcb->Iss; + Tcb->SndNxt = Tcb->Iss; + + Tcb->SndWl2 = Tcb->Iss; + Tcb->SndWnd = 536; + + Tcb->RcvWnd = GET_RCV_BUFFSIZE (Tcb->Sk); + + // + // Fisrt window size is never scaled + // + Tcb->RcvWndScale = 0; +} + + +/** + Initialize the peer related members. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seg Pointer to the segment that contains the peer's + intial info. + @param Opt Pointer to the options announced by the peer. + + @return None + +**/ +VOID +TcpInitTcbPeer ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg, + IN TCP_OPTION *Opt + ) +{ + UINT16 RcvMss; + + ASSERT (Tcb && Seg && Opt); + ASSERT (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)); + + Tcb->SndWnd = Seg->Wnd; + Tcb->SndWndMax = Tcb->SndWnd; + Tcb->SndWl1 = Seg->Seq; + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + Tcb->SndWl2 = Seg->Ack; + } else { + Tcb->SndWl2 = Tcb->Iss + 1; + } + + if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_MSS)) { + Tcb->SndMss = NET_MAX (64, Opt->Mss); + + RcvMss = TcpGetRcvMss (Tcb->Sk); + if (Tcb->SndMss > RcvMss) { + Tcb->SndMss = RcvMss; + } + + } else { + // + // One end doesn't support MSS option, use default. + // + Tcb->RcvMss = 536; + } + + Tcb->CWnd = Tcb->SndMss; + + Tcb->Irs = Seg->Seq; + Tcb->RcvNxt = Tcb->Irs + 1; + + Tcb->RcvWl2 = Tcb->RcvNxt; + + if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_WS) && + !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)) { + + Tcb->SndWndScale = Opt->WndScale; + + Tcb->RcvWndScale = TcpComputeScale (Tcb); + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS); + + } else { + // + // One end doesn't support window scale option. use zero. + // + Tcb->RcvWndScale = 0; + } + + if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_TS) && + !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)) { + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_TS); + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS); + + // + // Compute the effective SndMss per RFC1122 + // section 4.2.2.6. If timestamp option is + // enabled, it will always occupy 12 bytes. + // + Tcb->SndMss -= TCP_OPTION_TS_ALIGNED_LEN; + } +} + + +/** + Locate a listen TCB that matchs the Local and Remote. + + @param Local Pointer to the local (IP, Port). + @param Remote Pointer to the remote (IP, Port). + + @return Pointer to the TCP_CB with the least number of wildcard, if NULL no match is found. + +**/ +STATIC +TCP_CB * +TcpLocateListenTcb ( + IN TCP_PEER *Local, + IN TCP_PEER *Remote + ) +{ + NET_LIST_ENTRY *Entry; + TCP_CB *Node; + TCP_CB *Match; + INTN Last; + INTN Cur; + + Last = 4; + Match = NULL; + + NET_LIST_FOR_EACH (Entry, &mTcpListenQue) { + Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if ((Local->Port != Node->LocalEnd.Port) || + !TCP_PEER_MATCH (Remote, &Node->RemoteEnd) || + !TCP_PEER_MATCH (Local, &Node->LocalEnd) + ) { + + continue; + } + + // + // Compute the number of wildcard + // + Cur = 0; + if (Node->RemoteEnd.Ip == 0) { + Cur++; + } + + if (Node->RemoteEnd.Port == 0) { + Cur++; + } + + if (Node->LocalEnd.Ip == 0) { + Cur++; + } + + if (Cur < Last) { + if (Cur == 0) { + return Node; + } + + Last = Cur; + Match = Node; + } + } + + return Match; +} + + +/** + Try to find one Tcb whose equals to . + + @param Addr Pointer to the IP address needs to match. + @param Port The port number needs to match. + + @return The Tcb which matches the paire exists or not. + +**/ +BOOLEAN +TcpFindTcbByPeer ( + IN EFI_IPv4_ADDRESS *Addr, + IN TCP_PORTNO Port + ) +{ + TCP_PORTNO LocalPort; + NET_LIST_ENTRY *Entry; + TCP_CB *Tcb; + + ASSERT ((Addr != NULL) && (Port != 0)); + + LocalPort = HTONS (Port); + + NET_LIST_FOR_EACH (Entry, &mTcpListenQue) { + Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if ((EFI_IP4 (*Addr) == Tcb->LocalEnd.Ip) && + (LocalPort == Tcb->LocalEnd.Port)) { + + return TRUE; + } + } + + NET_LIST_FOR_EACH (Entry, &mTcpRunQue) { + Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if (((EFI_IP4 (*Addr) == Tcb->LocalEnd.Ip)) && + (LocalPort == Tcb->LocalEnd.Port)) { + + return TRUE; + } + } + + return FALSE; +} + + +/** + Locate the TCP_CB related to the socket pair. + + @param LocalPort The local port number. + @param LocalIp The local IP address. + @param RemotePort The remote port number. + @param RemoteIp The remote IP address. + @param Syn Whether to search the listen sockets, if TRUE, the + listen sockets are searched. + + @return Pointer to the related TCP_CB, if NULL no match is found. + +**/ +TCP_CB * +TcpLocateTcb ( + IN TCP_PORTNO LocalPort, + IN UINT32 LocalIp, + IN TCP_PORTNO RemotePort, + IN UINT32 RemoteIp, + IN BOOLEAN Syn + ) +{ + TCP_PEER Local; + TCP_PEER Remote; + NET_LIST_ENTRY *Entry; + TCP_CB *Tcb; + + Local.Port = LocalPort; + Local.Ip = LocalIp; + + Remote.Port = RemotePort; + Remote.Ip = RemoteIp; + + // + // First check for exact match. + // + NET_LIST_FOR_EACH (Entry, &mTcpRunQue) { + Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if (TCP_PEER_EQUAL (&Remote, &Tcb->RemoteEnd) && + TCP_PEER_EQUAL (&Local, &Tcb->LocalEnd)) { + + NetListRemoveEntry (&Tcb->List); + NetListInsertHead (&mTcpRunQue, &Tcb->List); + + return Tcb; + } + } + + // + // Only check listen queue when SYN flag is on + // + if (Syn) { + return TcpLocateListenTcb (&Local, &Remote); + } + + return NULL; +} + + +/** + Insert a Tcb into the proper queue. + + @param Tcb Pointer to the TCP_CB to be inserted. + + @retval 0 The Tcb is inserted successfully. + @retval -1 Error condition occurred. + +**/ +INTN +TcpInsertTcb ( + IN TCP_CB *Tcb + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Head; + TCP_CB *Node; + TCP4_PROTO_DATA *TcpProto; + + ASSERT ( + Tcb && + ( + (Tcb->State == TCP_LISTEN) || + (Tcb->State == TCP_SYN_SENT) || + (Tcb->State == TCP_SYN_RCVD) || + (Tcb->State == TCP_CLOSED) + ) + ); + + if (Tcb->LocalEnd.Port == 0) { + return -1; + } + + Head = &mTcpRunQue; + + if (Tcb->State == TCP_LISTEN) { + Head = &mTcpListenQue; + } + + // + // Check that Tcb isn't already on the list. + // + NET_LIST_FOR_EACH (Entry, Head) { + Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if (TCP_PEER_EQUAL (&Tcb->LocalEnd, &Node->LocalEnd) && + TCP_PEER_EQUAL (&Tcb->RemoteEnd, &Node->RemoteEnd)) { + + return -1; + } + } + + NetListInsertHead (Head, &Tcb->List); + + TcpProto = (TCP4_PROTO_DATA *) Tcb->Sk->ProtoReserved; + TcpSetVariableData (TcpProto->TcpService); + + return 0; +} + + +/** + Clone a TCP_CB from Tcb. + + @param Tcb Pointer to the TCP_CB to be cloned. + + @return Pointer to the new cloned TCP_CB, if NULL error condition occurred. + +**/ +TCP_CB * +TcpCloneTcb ( + IN TCP_CB *Tcb + ) +{ + TCP_CB *Clone; + + Clone = NetAllocatePool (sizeof (TCP_CB)); + + if (Clone == NULL) { + return NULL; + + } + + NetCopyMem (Clone, Tcb, sizeof (TCP_CB)); + + // + // Increate the reference count of the shared IpInfo. + // + NET_GET_REF (Tcb->IpInfo); + + NetListInit (&Clone->List); + NetListInit (&Clone->SndQue); + NetListInit (&Clone->RcvQue); + + Clone->Sk = SockClone (Tcb->Sk); + if (Clone->Sk == NULL) { + TCP4_DEBUG_ERROR (("TcpCloneTcb: failed to clone a sock\n")); + NetFreePool (Clone); + return NULL; + } + + ((TCP4_PROTO_DATA *) (Clone->Sk->ProtoReserved))->TcpPcb = Clone; + + return Clone; +} + + +/** + Compute an ISS to be used by a new connection. + + None + + @return The result ISS. + +**/ +TCP_SEQNO +TcpGetIss ( + VOID + ) +{ + mTcpGlobalIss += 2048; + return mTcpGlobalIss; +} + + +/** + Get the local mss. + + None + + @return The mss size. + +**/ +UINT16 +TcpGetRcvMss ( + IN SOCKET *Sock + ) +{ + EFI_SIMPLE_NETWORK_MODE SnpMode; + TCP4_PROTO_DATA *TcpProto; + EFI_IP4_PROTOCOL *Ip; + + ASSERT (Sock); + + TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved; + Ip = TcpProto->TcpService->IpIo->Ip; + ASSERT (Ip); + + Ip->GetModeData (Ip, NULL, NULL, &SnpMode); + + return (UINT16) (SnpMode.MaxPacketSize - 40); +} + + +/** + Set the Tcb's state. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param State The state to be set. + + @return None + +**/ +VOID +TcpSetState ( + IN TCP_CB *Tcb, + IN UINT8 State + ) +{ + TCP4_DEBUG_TRACE ( + ("Tcb (%x) state %s --> %s\n", + Tcb, + mTcpStateName[Tcb->State], + mTcpStateName[State]) + ); + + Tcb->State = State; + + switch (State) { + case TCP_ESTABLISHED: + + SockConnEstablished (Tcb->Sk); + break; + + case TCP_CLOSED: + + SockConnClosed (Tcb->Sk); + + break; + } +} + + +/** + Compute the TCP segment's checksum. + + @param Nbuf Pointer to the buffer that contains the TCP + segment. + @param HeadSum The checksum value of the fixed part of pseudo + header. + + @return The checksum value. + +**/ +UINT16 +TcpChecksum ( + IN NET_BUF *Nbuf, + IN UINT16 HeadSum + ) +{ + UINT16 Checksum; + + Checksum = NetbufChecksum (Nbuf); + Checksum = NetAddChecksum (Checksum, HeadSum); + + Checksum = NetAddChecksum ( + Checksum, + HTONS ((UINT16) Nbuf->TotalSize) + ); + + return ~Checksum; +} + + +/** + Translate the information from the head of the received TCP + segment Nbuf contains and fill it into a TCP_SEG structure. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Nbuf Pointer to the buffer contains the TCP segment. + + @return Pointer to the TCP_SEG that contains the translated TCP head information. + +**/ +TCP_SEG * +TcpFormatNetbuf ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + TCP_SEG *Seg; + TCP_HEAD *Head; + + Seg = TCPSEG_NETBUF (Nbuf); + Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL); + Nbuf->Tcp = Head; + + Seg->Seq = NTOHL (Head->Seq); + Seg->Ack = NTOHL (Head->Ack); + Seg->End = Seg->Seq + (Nbuf->TotalSize - (Head->HeadLen << 2)); + + Seg->Urg = NTOHS (Head->Urg); + Seg->Wnd = (NTOHS (Head->Wnd) << Tcb->SndWndScale); + Seg->Flag = Head->Flag; + + // + // SYN and FIN flag occupy one sequence space each. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + // + // RFC requires that initial window not be scaled + // + Seg->Wnd = NTOHS (Head->Wnd); + Seg->End++; + } + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + Seg->End++; + } + + return Seg; +} + + +/** + Reset the connection related with Tcb. + + @param Tcb Pointer to the TCP_CB of the connection to be + reset. + + @return None + +**/ +VOID +TcpResetConnection ( + IN TCP_CB *Tcb + ) +{ + NET_BUF *Nbuf; + TCP_HEAD *Nhead; + + Nbuf = NetbufAlloc (TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return ; + } + + Nhead = (TCP_HEAD *) NetbufAllocSpace ( + Nbuf, + sizeof (TCP_HEAD), + NET_BUF_TAIL + ); + + ASSERT (Nhead != NULL); + + Nbuf->Tcp = Nhead; + + Nhead->Flag = TCP_FLG_RST; + Nhead->Seq = HTONL (Tcb->SndNxt); + Nhead->Ack = HTONL (Tcb->RcvNxt); + Nhead->SrcPort = Tcb->LocalEnd.Port; + Nhead->DstPort = Tcb->RemoteEnd.Port; + Nhead->HeadLen = (sizeof (TCP_HEAD) >> 2); + Nhead->Res = 0; + Nhead->Wnd = HTONS (0xFFFF); + Nhead->Checksum = 0; + Nhead->Urg = 0; + Nhead->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum); + + TcpSendIpPacket (Tcb, Nbuf, Tcb->LocalEnd.Ip, Tcb->RemoteEnd.Ip); + + NetbufFree (Nbuf); +} + + +/** + Initialize an active connection, + + @param Tcb Pointer to the TCP_CB that wants to initiate a + connection. + + @return None + +**/ +VOID +TcpOnAppConnect ( + IN TCP_CB *Tcb + ) +{ + TcpInitTcbLocal (Tcb); + TcpSetState (Tcb, TCP_SYN_SENT); + + TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout); + TcpToSendData (Tcb, 1); +} + + +/** + Initiate the connection close procedure, called when + applications want to close the connection. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpOnAppClose ( + IN TCP_CB *Tcb + ) +{ + ASSERT (Tcb); + + if (!NetListIsEmpty (&Tcb->RcvQue) || GET_RCV_DATASIZE (Tcb->Sk)) { + + TCP4_DEBUG_WARN (("TcpOnAppClose: connection reset " + "because data is lost for TCB %x\n", Tcb)); + + TcpResetConnection (Tcb); + TcpClose (Tcb); + return; + } + + switch (Tcb->State) { + case TCP_CLOSED: + case TCP_LISTEN: + case TCP_SYN_SENT: + TcpSetState (Tcb, TCP_CLOSED); + break; + + case TCP_SYN_RCVD: + case TCP_ESTABLISHED: + TcpSetState (Tcb, TCP_FIN_WAIT_1); + break; + + case TCP_CLOSE_WAIT: + TcpSetState (Tcb, TCP_LAST_ACK); + break; + } + + TcpToSendData (Tcb, 1); +} + + +/** + Check whether the application's newly delivered data + can be sent out. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 Whether the data is sent out or is buffered for + further sending. + @retval -1 The Tcb is not in a state that data is permitted to + be sent out. + +**/ +INTN +TcpOnAppSend ( + IN TCP_CB *Tcb + ) +{ + + switch (Tcb->State) { + case TCP_CLOSED: + return -1; + break; + + case TCP_LISTEN: + return -1; + break; + + case TCP_SYN_SENT: + case TCP_SYN_RCVD: + return 0; + break; + + case TCP_ESTABLISHED: + case TCP_CLOSE_WAIT: + TcpToSendData (Tcb, 0); + return 0; + break; + + case TCP_FIN_WAIT_1: + case TCP_FIN_WAIT_2: + case TCP_CLOSING: + case TCP_LAST_ACK: + case TCP_TIME_WAIT: + return -1; + break; + } + + return 0; +} + + +/** + Application has consumed some data, check whether + to send a window updata ack or a delayed ack. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + +**/ +INTN +TcpOnAppConsume ( + IN TCP_CB *Tcb + ) +{ + + switch (Tcb->State) { + case TCP_CLOSED: + return -1; + break; + + case TCP_LISTEN: + return -1; + break; + + case TCP_SYN_SENT: + case TCP_SYN_RCVD: + return 0; + break; + + case TCP_ESTABLISHED: + if (TcpRcvWinNow (Tcb) > TcpRcvWinOld (Tcb)) { + + if (TcpRcvWinOld (Tcb) < Tcb->RcvMss) { + + TCP4_DEBUG_TRACE (("TcpOnAppConsume: send a window" + " update for a window closed Tcb(%x)\n", Tcb)); + + TcpSendAck (Tcb); + } else if (Tcb->DelayedAck == 0) { + + TCP4_DEBUG_TRACE (("TcpOnAppConsume: scheduled a delayed" + " ACK to update window for Tcb(%x)\n", Tcb)); + + Tcb->DelayedAck = 1; + } + } + + break; + + case TCP_CLOSE_WAIT: + return 0; + break; + + case TCP_FIN_WAIT_1: + case TCP_FIN_WAIT_2: + case TCP_CLOSING: + case TCP_LAST_ACK: + case TCP_TIME_WAIT: + return -1; + break; + } + + return -1; +} + + +/** + Abort the connection by sending a reset segment, called + when the application wants to abort the connection. + + @param Tcb Pointer to the TCP_CB of the TCP instance. + + @return None. + +**/ +VOID +TcpOnAppAbort ( + IN TCP_CB *Tcb + ) +{ + TCP4_DEBUG_WARN (("TcpOnAppAbort: connection reset " + "issued by application for TCB %x\n", Tcb)); + + switch (Tcb->State) { + case TCP_SYN_RCVD: + case TCP_ESTABLISHED: + case TCP_FIN_WAIT_1: + case TCP_FIN_WAIT_2: + case TCP_CLOSE_WAIT: + TcpResetConnection (Tcb); + break; + } + + TcpSetState (Tcb, TCP_CLOSED); +} + + +/** + Set the Tdp4 variable data. + + @param Tcp4Service Tcp4 service data. + + @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable. + @retval other Set variable failed. + +**/ +EFI_STATUS +TcpSetVariableData ( + IN TCP4_SERVICE_DATA *Tcp4Service + ) +{ + UINT32 NumConfiguredInstance; + NET_LIST_ENTRY *Entry; + TCP_CB *TcpPcb; + TCP4_PROTO_DATA *TcpProto; + UINTN VariableDataSize; + EFI_TCP4_VARIABLE_DATA *Tcp4VariableData; + EFI_TCP4_SERVICE_POINT *Tcp4ServicePoint; + CHAR16 *NewMacString; + EFI_STATUS Status; + + NumConfiguredInstance = 0; + + // + // Go through the running queue to count the instances. + // + NET_LIST_FOR_EACH (Entry, &mTcpRunQue) { + TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved; + + if (TcpProto->TcpService == Tcp4Service) { + // + // This tcp instance belongs to the Tcp4Service. + // + NumConfiguredInstance++; + } + } + + // + // Go through the listening queue to count the instances. + // + NET_LIST_FOR_EACH (Entry, &mTcpListenQue) { + TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved; + + if (TcpProto->TcpService == Tcp4Service) { + // + // This tcp instance belongs to the Tcp4Service. + // + NumConfiguredInstance++; + } + } + + // + // Calculate the size of the Tcp4VariableData. As there may be no Tcp4 child, + // we should add extra buffer for the service points only if the number of configured + // children is more than 1. + // + VariableDataSize = sizeof (EFI_TCP4_VARIABLE_DATA); + + if (NumConfiguredInstance > 1) { + VariableDataSize += sizeof (EFI_TCP4_SERVICE_POINT) * (NumConfiguredInstance - 1); + } + + Tcp4VariableData = NetAllocatePool (VariableDataSize); + if (Tcp4VariableData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Tcp4VariableData->DriverHandle = Tcp4Service->DriverBindingHandle; + Tcp4VariableData->ServiceCount = NumConfiguredInstance; + + Tcp4ServicePoint = &Tcp4VariableData->Services[0]; + + // + // Go through the running queue to fill the service points. + // + NET_LIST_FOR_EACH (Entry, &mTcpRunQue) { + TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved; + + if (TcpProto->TcpService == Tcp4Service) { + // + // This tcp instance belongs to the Tcp4Service. + // + Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle; + EFI_IP4 (Tcp4ServicePoint->LocalAddress) = TcpPcb->LocalEnd.Ip; + Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port); + EFI_IP4 (Tcp4ServicePoint->RemoteAddress) = TcpPcb->RemoteEnd.Ip; + Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port); + + Tcp4ServicePoint++; + } + } + + // + // Go through the listening queue to fill the service points. + // + NET_LIST_FOR_EACH (Entry, &mTcpListenQue) { + TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved; + + if (TcpProto->TcpService == Tcp4Service) { + // + // This tcp instance belongs to the Tcp4Service. + // + Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle; + EFI_IP4 (Tcp4ServicePoint->LocalAddress) = TcpPcb->LocalEnd.Ip; + Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port); + EFI_IP4 (Tcp4ServicePoint->RemoteAddress) = TcpPcb->RemoteEnd.Ip; + Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port); + + Tcp4ServicePoint++; + } + } + + // + // Get the mac string. + // + Status = NetLibGetMacString ( + Tcp4Service->ControllerHandle, + Tcp4Service->DriverBindingHandle, + &NewMacString + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + if (Tcp4Service->MacString != NULL) { + // + // The variable is set already, we're going to update it. + // + if (StrCmp (Tcp4Service->MacString, NewMacString) != 0) { + // + // The mac address is changed, delete the previous variable first. + // + gRT->SetVariable ( + Tcp4Service->MacString, + &gEfiTcp4ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + NULL + ); + } + + NetFreePool (Tcp4Service->MacString); + } + + Tcp4Service->MacString = NewMacString; + + Status = gRT->SetVariable ( + Tcp4Service->MacString, + &gEfiTcp4ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + VariableDataSize, + (VOID *) Tcp4VariableData + ); + +ON_ERROR: + + NetFreePool (Tcp4VariableData); + + return Status; +} + + +/** + Clear the variable and free the resource. + + @param Tcp4Service Tcp4 service data. + + @return None. + +**/ +VOID +TcpClearVariableData ( + IN TCP4_SERVICE_DATA *Tcp4Service + ) +{ + ASSERT (Tcp4Service->MacString != NULL); + + gRT->SetVariable ( + Tcp4Service->MacString, + &gEfiTcp4ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + NULL + ); + + NetFreePool (Tcp4Service->MacString); + Tcp4Service->MacString = NULL; +} + diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.c new file mode 100644 index 0000000000..d430a2e10d --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.c @@ -0,0 +1,380 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Tcp4Option.c + +Abstract: + + Routines to process TCP option. + + +**/ + +#include "Tcp4Main.h" + +STATIC +UINT16 +TcpGetUint16 ( + IN UINT8 *Buf + ) +{ + UINT16 Value; + NetCopyMem (&Value, Buf, sizeof (UINT16)); + return NTOHS (Value); +} + +STATIC +VOID +TcpPutUint16 ( + IN UINT8 *Buf, + IN UINT16 Data + ) +{ + Data = HTONS (Data); + NetCopyMem (Buf, &Data, sizeof (UINT16)); +} + +STATIC +UINT32 +TcpGetUint32 ( + IN UINT8 *Buf + ) +{ + UINT32 Value; + NetCopyMem (&Value, Buf, sizeof (UINT32)); + return NTOHL (Value); +} + +STATIC +VOID +TcpPutUint32 ( + IN UINT8 *Buf, + IN UINT32 Data + ) +{ + Data = HTONL (Data); + NetCopyMem (Buf, &Data, sizeof (UINT32)); +} + + +/** + Compute the window scale value according to the given + buffer size. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @retval UINT8 The scale value. + +**/ +UINT8 +TcpComputeScale ( + IN TCP_CB *Tcb + ) +{ + UINT8 Scale; + UINT32 BufSize; + + ASSERT (Tcb && Tcb->Sk); + + BufSize = GET_RCV_BUFFSIZE (Tcb->Sk); + + Scale = 0; + while ((Scale < TCP_OPTION_MAX_WS) && + ((UINT32) (TCP_OPTION_MAX_WIN << Scale) < BufSize)) { + + Scale++; + } + + return Scale; +} + + +/** + Build the TCP option in three-way handshake. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Nbuf Pointer to the buffer to store the options. + + @return The total length of the TCP option field. + +**/ +UINT16 +TcpSynBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + char *Data; + UINT16 Len; + + ASSERT (Tcb && Nbuf && !Nbuf->Tcp); + + Len = 0; + + // + // Add timestamp option if not disabled by application + // and it is the first SYN segment or the peer has sent + // us its timestamp. + // + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS) && + (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) || + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS))) { + + Data = NetbufAllocSpace ( + Nbuf, + TCP_OPTION_TS_ALIGNED_LEN, + NET_BUF_HEAD + ); + + ASSERT (Data); + Len += TCP_OPTION_TS_ALIGNED_LEN; + + TcpPutUint32 (Data, TCP_OPTION_TS_FAST); + TcpPutUint32 (Data + 4, mTcpTick); + TcpPutUint32 (Data + 8, 0); + } + + // + // Build window scale option, only when are configured + // to send WS option, and either we are doing active + // open or we have received WS option from peer. + // + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS) && + (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) || + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS))) { + + Data = NetbufAllocSpace ( + Nbuf, + TCP_OPTION_WS_ALIGNED_LEN, + NET_BUF_HEAD + ); + + ASSERT (Data); + + Len += TCP_OPTION_WS_ALIGNED_LEN; + TcpPutUint32 (Data, TCP_OPTION_WS_FAST | TcpComputeScale (Tcb)); + } + + // + // Build MSS option + // + Data = NetbufAllocSpace (Nbuf, TCP_OPTION_MSS_LEN, 1); + ASSERT (Data); + + Len += TCP_OPTION_MSS_LEN; + TcpPutUint32 (Data, TCP_OPTION_MSS_FAST | Tcb->RcvMss); + + return Len; +} + + +/** + Build the TCP option in synchronized states. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Nbuf Pointer to the buffer to store the options. + + @return The total length of the TCP option field. + +**/ +UINT16 +TcpBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + char *Data; + UINT16 Len; + + ASSERT (Tcb && Nbuf && !Nbuf->Tcp); + Len = 0; + + // + // Build Timestamp option + // + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_TS) && + !TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_RST)) { + + Data = NetbufAllocSpace ( + Nbuf, + TCP_OPTION_TS_ALIGNED_LEN, + NET_BUF_HEAD + ); + + ASSERT (Data); + Len += TCP_OPTION_TS_ALIGNED_LEN; + + TcpPutUint32 (Data, TCP_OPTION_TS_FAST); + TcpPutUint32 (Data + 4, mTcpTick); + TcpPutUint32 (Data + 8, Tcb->TsRecent); + } + + return Len; +} + + +/** + Parse the supported options. + + @param Tcp Pointer to the TCP_CB of this TCP instance. + @param Option Pointer to the TCP_OPTION used to store the successfully pasrsed + options. + + @retval 0 The options are successfully pasrsed. + @retval -1 Ilegal option was found. + +**/ +INTN +TcpParseOption ( + IN TCP_HEAD *Tcp, + IN TCP_OPTION *Option + ) +{ + UINT8 *Head; + UINT8 TotalLen; + UINT8 Cur; + UINT8 Type; + UINT8 Len; + + ASSERT (Tcp && Option); + + Option->Flag = 0; + + TotalLen = (Tcp->HeadLen << 2) - sizeof (TCP_HEAD); + if (TotalLen <= 0) { + return 0; + } + + Head = (UINT8 *) (Tcp + 1); + + // + // Fast process of timestamp option + // + if ((TotalLen == TCP_OPTION_TS_ALIGNED_LEN) && + (TcpGetUint32 (Head) == TCP_OPTION_TS_FAST)) { + + Option->TSVal = TcpGetUint32 (Head + 4); + Option->TSEcr = TcpGetUint32 (Head + 8); + Option->Flag = TCP_OPTION_RCVD_TS; + + return 0; + } + + // + // Slow path to process the options. + // + Cur = 0; + + while (Cur < TotalLen) { + Type = Head[Cur]; + + switch (Type) { + case TCP_OPTION_MSS: + Len = Head[Cur + 1]; + + if ((Len != TCP_OPTION_MSS_LEN) || + (TotalLen - Cur < TCP_OPTION_MSS_LEN)) { + + return -1; + } + + Option->Mss = TcpGetUint16 (&Head[Cur + 2]); + TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_MSS); + + Cur += TCP_OPTION_MSS_LEN; + break; + + case TCP_OPTION_WS: + Len = Head[Cur + 1]; + + if ((Len != TCP_OPTION_WS_LEN) || + (TotalLen - Cur < TCP_OPTION_WS_LEN)) { + + return -1; + } + + Option->WndScale = NET_MIN (14, Head[Cur + 2]); + TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_WS); + + Cur += TCP_OPTION_WS_LEN; + break; + + case TCP_OPTION_TS: + Len = Head[Cur + 1]; + + if ((Len != TCP_OPTION_TS_LEN) || + (TotalLen - Cur < TCP_OPTION_TS_LEN)) { + + return -1; + } + + Option->TSVal = TcpGetUint32 (&Head[Cur + 2]); + Option->TSEcr = TcpGetUint32 (&Head[Cur + 6]); + TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_TS); + + Cur += TCP_OPTION_TS_LEN; + break; + + case TCP_OPTION_NOP: + Cur++; + break; + + case TCP_OPTION_EOP: + Cur = TotalLen; + break; + + default: + Len = Head[Cur + 1]; + + if (TotalLen - Cur < Len || Len < 2) { + return -1; + } + + Cur = Cur + Len; + break; + } + + } + + return 0; +} + + +/** + Check the segment against PAWS. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param TSVal The timestamp value. + + @retval 1 The segment passed the PAWS check. + @retval 0 The segment failed to pass the PAWS check. + +**/ +UINT32 +TcpPawsOK ( + IN TCP_CB *Tcb, + IN UINT32 TSVal + ) +{ + // + // PAWS as defined in RFC1323, buggy... + // + if (TCP_TIME_LT (TSVal, Tcb->TsRecent) && + TCP_TIME_LT (Tcb->TsRecentAge + TCP_PAWS_24DAY, mTcpTick)) { + + return 0; + + } + + return 1; +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.h new file mode 100644 index 0000000000..8074cb5564 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.h @@ -0,0 +1,107 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Tcp4Option.h + +Abstract: + + +**/ + +#ifndef _TCP4_OPTION_H_ +#define _TCP4_OPTION_H_ + +// +// The structure to store the parse option value. +// ParseOption only parse the options, don't process them. +// +typedef struct s_TCP_OPTION { + UINT8 Flag; // flag such as TCP_OPTION_RCVD_MSS + UINT8 WndScale; // the WndScale received + UINT16 Mss; // the Mss received + UINT32 TSVal; // the TSVal field in a timestamp option + UINT32 TSEcr; // the TSEcr field in a timestamp option +} TCP_OPTION; + +enum { + + // + // supported TCP option type and their length + // + TCP_OPTION_EOP = 0, // End Of oPtion + TCP_OPTION_NOP = 1, // No-Option. + TCP_OPTION_MSS = 2, // Maximum Segment Size + TCP_OPTION_WS = 3, // Window scale + TCP_OPTION_TS = 8, // Timestamp + TCP_OPTION_MSS_LEN = 4, // length of MSS option + TCP_OPTION_WS_LEN = 3, // length of window scale option + TCP_OPTION_TS_LEN = 10, // length of timestamp option + TCP_OPTION_WS_ALIGNED_LEN = 4, // length of window scale option, aligned + TCP_OPTION_TS_ALIGNED_LEN = 12, // length of timestamp option, aligned + + // + // recommend format of timestamp window scale + // option for fast process. + // + TCP_OPTION_TS_FAST = ((TCP_OPTION_NOP << 24) | + (TCP_OPTION_NOP << 16) | + (TCP_OPTION_TS << 8) | + TCP_OPTION_TS_LEN), + + TCP_OPTION_WS_FAST = ((TCP_OPTION_NOP << 24) | + (TCP_OPTION_WS << 16) | + (TCP_OPTION_WS_LEN << 8)), + + TCP_OPTION_MSS_FAST = ((TCP_OPTION_MSS << 24) | + (TCP_OPTION_MSS_LEN << 16)), + + // + // Other misc definations + // + TCP_OPTION_MAX_WS = 14, // Maxium window scale value + TCP_OPTION_MAX_WIN = 0xffff, // max window size in TCP header + TCP_OPTION_RCVD_MSS = 0x01, + TCP_OPTION_RCVD_WS = 0x02, + TCP_OPTION_RCVD_TS = 0x04, +}; + +UINT8 +TcpComputeScale ( + IN TCP_CB *Tcb + ); + +UINT16 +TcpSynBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Buf + ); + +UINT16 +TcpBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Buf + ); + +INTN +TcpParseOption ( + IN TCP_HEAD *Tcp, + IN TCP_OPTION *Option + ); + +UINT32 +TcpPawsOK ( + IN TCP_CB *Tcb, + IN UINT32 TSVal + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c new file mode 100644 index 0000000000..f2987e769c --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c @@ -0,0 +1,1218 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Tcp4Output.c + +Abstract: + + TCP output process routines. + + +**/ + +#include "Tcp4Main.h" + +STATIC UINT8 mTcpOutFlag[] = { + 0, // TCP_CLOSED + 0, // TCP_LISTEN + TCP_FLG_SYN, // TCP_SYN_SENT + TCP_FLG_SYN | TCP_FLG_ACK, // TCP_SYN_RCVD + TCP_FLG_ACK, // TCP_ESTABLISHED + TCP_FLG_FIN | TCP_FLG_ACK, // TCP_FIN_WAIT_1 + TCP_FLG_ACK, // TCP_FIN_WAIT_2 + TCP_FLG_ACK | TCP_FLG_FIN, // TCP_CLOSING + TCP_FLG_ACK, // TCP_TIME_WAIT + TCP_FLG_ACK, // TCP_CLOSE_WAIT + TCP_FLG_FIN | TCP_FLG_ACK // TCP_LAST_ACK +}; + + +/** + Compute the sequence space left in the old receive window. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return The sequence space left in the old receive window. + +**/ +UINT32 +TcpRcvWinOld ( + IN TCP_CB *Tcb + ) +{ + UINT32 OldWin; + + OldWin = 0; + + if (TCP_SEQ_GT (Tcb->RcvWl2 + Tcb->RcvWnd, Tcb->RcvNxt)) { + + OldWin = TCP_SUB_SEQ ( + Tcb->RcvWl2 + Tcb->RcvWnd, + Tcb->RcvNxt + ); + } + + return OldWin; +} + + +/** + Compute the current receive window. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return The size of the current receive window, in bytes. + +**/ +UINT32 +TcpRcvWinNow ( + IN TCP_CB *Tcb + ) +{ + SOCKET *Sk; + UINT32 Win; + UINT32 Increase; + UINT32 OldWin; + + Sk = Tcb->Sk; + ASSERT (Sk); + + OldWin = TcpRcvWinOld (Tcb); + + Win = SockGetFreeSpace (Sk, SOCK_RCV_BUF); + + Increase = 0; + if (Win > OldWin) { + Increase = Win - OldWin; + } + + // + // Receiver's SWS: don't advertise a bigger window + // unless it can be increased by at least one Mss or + // half of the receive buffer. + // + if ((Increase > Tcb->SndMss) || + (2 * Increase >= GET_RCV_BUFFSIZE (Sk))) { + + return Win; + } + + return OldWin; +} + + +/** + Compute the value to fill in the window size field + of the outgoing segment. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Syn The flag to indicate whether the outgoing segment is a SYN + segment. + + @return The value of the local receive window size used to fill the outing segment. + +**/ +STATIC +UINT16 +TcpComputeWnd ( + IN TCP_CB *Tcb, + IN BOOLEAN Syn + ) +{ + UINT32 Wnd; + + // + // RFC requires that initial window not be scaled + // + if (Syn) { + + Wnd = GET_RCV_BUFFSIZE (Tcb->Sk); + } else { + + Wnd = TcpRcvWinNow (Tcb); + + Tcb->RcvWnd = Wnd; + } + + Wnd = NET_MIN (Wnd >> Tcb->RcvWndScale, 0xffff); + return NTOHS ((UINT16) Wnd); +} + + +/** + Get the maximum SndNxt. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return The sequence number of the maximum SndNxt. + +**/ +TCP_SEQNO +TcpGetMaxSndNxt ( + IN TCP_CB *Tcb + ) +{ + NET_LIST_ENTRY *Entry; + NET_BUF *Nbuf; + + if (NetListIsEmpty (&Tcb->SndQue)) { + return Tcb->SndNxt; + } + + Entry = Tcb->SndQue.BackLink; + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + + ASSERT (TCP_SEQ_GEQ (TCPSEG_NETBUF (Nbuf)->End, Tcb->SndNxt)); + return TCPSEG_NETBUF (Nbuf)->End; +} + + +/** + Compute how much data to send. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Force Whether to ignore the sender's SWS avoidance algorithm and send + out data by force. + + @return The length of the data can be sent, if 0, no data can be sent. + +**/ +UINT32 +TcpDataToSend ( + IN TCP_CB *Tcb, + IN INTN Force + ) +{ + SOCKET *Sk; + UINT32 Win; + UINT32 Len; + UINT32 Left; + UINT32 Limit; + + Sk = Tcb->Sk; + ASSERT (Sk); + + // + // TCP should NOT send data beyond the send window + // and congestion window. The right edge of send + // window is defined as SND.WL2 + SND.WND. The right + // edge of congestion window is defined as SND.UNA + + // CWND. + // + Win = 0; + Limit = Tcb->SndWl2 + Tcb->SndWnd; + + if (TCP_SEQ_GT (Limit, Tcb->SndUna + Tcb->CWnd)) { + + Limit = Tcb->SndUna + Tcb->CWnd; + } + + if (TCP_SEQ_GT (Limit, Tcb->SndNxt)) { + Win = TCP_SUB_SEQ (Limit, Tcb->SndNxt); + } + + // + // The data to send contains two parts: the data on the + // socket send queue, and the data on the TCB's send + // buffer. The later can be non-zero if the peer shrinks + // its advertised window. + // + Left = GET_SND_DATASIZE (Sk) + + TCP_SUB_SEQ (TcpGetMaxSndNxt (Tcb), Tcb->SndNxt); + + Len = NET_MIN (Win, Left); + + if (Len > Tcb->SndMss) { + Len = Tcb->SndMss; + } + + if (Force || (Len == 0 && Left == 0)) { + return Len; + } + + if (Len == 0 && Left != 0) { + goto SetPersistTimer; + } + + // + // Sender's SWS avoidance: Don't send a small segment unless + // a)A full-sized segment can be sent, + // b)at least one-half of the maximum sized windows that + // the other end has ever advertised. + // c)It can send everything it has and either it isn't + // expecting an ACK or the Nagle algorithm is disabled. + // + if ((Len == Tcb->SndMss) || (2 * Len >= Tcb->SndWndMax)) { + + return Len; + } + + if ((Len == Left) && + ((Tcb->SndNxt == Tcb->SndUna) || + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE))) { + + return Len; + } + + // + // RFC1122 suggests to set a timer when SWSA forbids TCP + // sending more data, and combine it with probe timer. + // +SetPersistTimer: + if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) { + + TCP4_DEBUG_WARN ( + ("TcpDataToSend: enter persistent state for TCB %x\n", + Tcb) + ); + + TcpSetProbeTimer (Tcb); + } + + return 0; +} + + +/** + Build the TCP header of the TCP segment and transmit the + segment by IP. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Nbuf Pointer to the buffer containing the segment to be sent out. + + @retval 0 The segment is sent out successfully. + @retval other Error condition occurred. + +**/ +STATIC +INTN +TcpTransmitSegment ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + UINT16 Len; + TCP_HEAD *Head; + TCP_SEG *Seg; + BOOLEAN Syn; + UINT32 DataLen; + + ASSERT (Nbuf && (Nbuf->Tcp == NULL) && TcpVerifySegment (Nbuf)); + + DataLen = Nbuf->TotalSize; + + Seg = TCPSEG_NETBUF (Nbuf); + Syn = TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN); + + if (Syn) { + + Len = TcpSynBuildOption (Tcb, Nbuf); + } else { + + Len = TcpBuildOption (Tcb, Nbuf); + } + + ASSERT ((Len % 4 == 0) && (Len <= 40)); + + Len += sizeof (TCP_HEAD); + + Head = (TCP_HEAD *) NetbufAllocSpace ( + Nbuf, + sizeof (TCP_HEAD), + NET_BUF_HEAD + ); + + ASSERT (Head != NULL); + + Nbuf->Tcp = Head; + + Head->SrcPort = Tcb->LocalEnd.Port; + Head->DstPort = Tcb->RemoteEnd.Port; + Head->Seq = NTOHL (Seg->Seq); + Head->Ack = NTOHL (Tcb->RcvNxt); + Head->HeadLen = (UINT8) (Len >> 2); + Head->Res = 0; + Head->Wnd = TcpComputeWnd (Tcb, Syn); + Head->Checksum = 0; + + // + // Check whether to set the PSH flag. + // + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_PSH); + + if (DataLen != 0) { + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_PSH) && + TCP_SEQ_BETWEEN (Seg->Seq, Tcb->SndPsh, Seg->End)) { + + TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH); + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH); + + } else if ((Seg->End == Tcb->SndNxt) && + (GET_SND_DATASIZE (Tcb->Sk) == 0)) { + + TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH); + } + } + + // + // Check whether to set the URG flag and the urgent pointer. + // + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG); + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) && + TCP_SEQ_LEQ (Seg->Seq, Tcb->SndUp)) { + + TCP_SET_FLG (Seg->Flag, TCP_FLG_URG); + + if (TCP_SEQ_LT (Tcb->SndUp, Seg->End)) { + + Seg->Urg = (UINT16) TCP_SUB_SEQ (Tcb->SndUp, Seg->Seq); + } else { + + Seg->Urg = (UINT16) NET_MIN ( + TCP_SUB_SEQ (Tcb->SndUp, + Seg->Seq), + 0xffff + ); + } + } + + Head->Flag = Seg->Flag; + Head->Urg = NTOHS (Seg->Urg); + Head->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum); + + // + // update the TCP session's control information + // + Tcb->RcvWl2 = Tcb->RcvNxt; + if (Syn) { + Tcb->RcvWnd = NTOHS (Head->Wnd); + } + + // + // clear delayedack flag + // + Tcb->DelayedAck = 0; + + return TcpSendIpPacket (Tcb, Nbuf, Tcb->LocalEnd.Ip, Tcb->RemoteEnd.Ip); +} + + +/** + Get a segment from the Tcb's SndQue. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seq The sequence number of the segment. + @param Len The maximum length of the segment. + + @return Pointer to the segment, if NULL some error occurred. + +**/ +NET_BUF * +TcpGetSegmentSndQue ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ) +{ + NET_LIST_ENTRY *Head; + NET_LIST_ENTRY *Cur; + NET_BUF *Node; + TCP_SEG *Seg; + NET_BUF *Nbuf; + TCP_SEQNO End; + UINT8 *Data; + UINT8 Flag; + INT32 Offset; + INT32 CopyLen; + + ASSERT (Tcb && TCP_SEQ_LEQ (Seq, Tcb->SndNxt) && (Len > 0)); + + // + // Find the segment that contains the Seq. + // + Head = &Tcb->SndQue; + + Node = NULL; + Seg = NULL; + + NET_LIST_FOR_EACH (Cur, Head) { + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + Seg = TCPSEG_NETBUF (Node); + + if (TCP_SEQ_LT (Seq, Seg->End) && TCP_SEQ_LEQ (Seg->Seq, Seq)) { + + break; + } + } + + ASSERT (Cur != Head); + + // + // Return the buffer if it can be returned without + // adjustment: + // + if ((Seg->Seq == Seq) && + TCP_SEQ_LEQ (Seg->End, Seg->Seq + Len) && + !NET_BUF_SHARED (Node)) { + + NET_GET_REF (Node); + return Node; + } + + // + // Create a new buffer and copy data there. + // + Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return NULL; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + Flag = Seg->Flag; + End = Seg->End; + + if (TCP_SEQ_LT (Seq + Len, Seg->End)) { + End = Seq + Len; + } + + CopyLen = TCP_SUB_SEQ (End, Seq); + Offset = TCP_SUB_SEQ (Seq, Seg->Seq); + + // + // If SYN is set and out of the range, clear the flag. + // Becuase the sequence of the first byte is SEG.SEQ+1, + // adjust Offset by -1. If SYN is in the range, copy + // one byte less. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + + if (TCP_SEQ_LT (Seg->Seq, Seq)) { + + TCP_CLEAR_FLG (Flag, TCP_FLG_SYN); + Offset--; + } else { + + CopyLen--; + } + } + + // + // If FIN is set and in the range, copy one byte less, + // and if it is out of the range, clear the flag. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + + if (Seg->End == End) { + + CopyLen--; + } else { + + TCP_CLEAR_FLG (Flag, TCP_FLG_FIN); + } + } + + ASSERT (CopyLen >= 0); + + // + // copy data to the segment + // + if (CopyLen) { + Data = NetbufAllocSpace (Nbuf, CopyLen, NET_BUF_TAIL); + ASSERT (Data); + + if ((INT32) NetbufCopy (Node, Offset, CopyLen, Data) != CopyLen) { + goto OnError; + } + } + + NetCopyMem (TCPSEG_NETBUF (Nbuf), Seg, sizeof (TCP_SEG)); + + TCPSEG_NETBUF (Nbuf)->Seq = Seq; + TCPSEG_NETBUF (Nbuf)->End = End; + TCPSEG_NETBUF (Nbuf)->Flag = Flag; + + return Nbuf; + +OnError: + NetbufFree (Nbuf); + return NULL; +} + + +/** + Get a segment from the Tcb's socket buffer. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seq The sequence number of the segment. + @param Len The maximum length of the segment. + + @return Pointer to the segment, if NULL some error occurred. + +**/ +NET_BUF * +TcpGetSegmentSock ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ) +{ + NET_BUF *Nbuf; + UINT8 *Data; + UINT32 DataGet; + + ASSERT (Tcb && Tcb->Sk); + + Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD); + + if (Nbuf == NULL) { + TCP4_DEBUG_ERROR (("TcpGetSegmentSock: failed to allocate " + "a netbuf for TCB %x\n",Tcb)); + + return NULL; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + DataGet = 0; + + if (Len) { + // + // copy data to the segment. + // + Data = NetbufAllocSpace (Nbuf, Len, NET_BUF_TAIL); + ASSERT (Data); + + DataGet = SockGetDataToSend (Tcb->Sk, 0, Len, Data); + } + + NET_GET_REF (Nbuf); + + TCPSEG_NETBUF (Nbuf)->Seq = Seq; + TCPSEG_NETBUF (Nbuf)->End = Seq + Len; + + NetListInsertTail (&(Tcb->SndQue), &(Nbuf->List)); + + if (DataGet != 0) { + + SockDataSent (Tcb->Sk, DataGet); + } + + return Nbuf; +} + + +/** + Get a segment starting from sequence Seq of a maximum + length of Len. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seq The sequence number of the segment. + @param Len The maximum length of the segment. + + @return Pointer to the segment, if NULL some error occurred. + +**/ +NET_BUF * +TcpGetSegment ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ) +{ + NET_BUF *Nbuf; + + ASSERT (Tcb); + + // + // Compare the SndNxt with the max sequence number sent. + // + if ((Len != 0) && TCP_SEQ_LT (Seq, TcpGetMaxSndNxt (Tcb))) { + + Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len); + } else { + + Nbuf = TcpGetSegmentSock (Tcb, Seq, Len); + } + + ASSERT (TcpVerifySegment (Nbuf)); + return Nbuf; +} + + +/** + Retransmit the segment from sequence Seq. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seq The sequence number of the segment to be retransmitted. + + @retval 0 Retransmission succeeded. + @retval -1 Error condition occurred. + +**/ +INTN +TcpRetransmit ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq + ) +{ + NET_BUF *Nbuf; + UINT32 Len; + + // + // Compute the maxium length of retransmission. It is + // limited by three factors: + // 1. Less than SndMss + // 2. must in the current send window + // 3. will not change the boundaries of queued segments. + // + if (TCP_SEQ_LT (Tcb->SndWl2 + Tcb->SndWnd, Seq)) { + TCP4_DEBUG_WARN (("TcpRetransmit: retransmission cancelled " + "because send window too small for TCB %x\n", Tcb)); + + return 0; + } + + Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq); + Len = NET_MIN (Len, Tcb->SndMss); + + Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len); + if (Nbuf == NULL) { + return -1; + } + + ASSERT (TcpVerifySegment (Nbuf)); + + if (TcpTransmitSegment (Tcb, Nbuf) != 0) { + goto OnError; + } + + // + // The retransmitted buffer may be on the SndQue, + // trim TCP head because all the buffer on SndQue + // are headless. + // + ASSERT (Nbuf->Tcp); + NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + NetbufFree (Nbuf); + return 0; + +OnError: + if (Nbuf != NULL) { + NetbufFree (Nbuf); + } + + return -1; +} + + +/** + Check whether to send data/SYN/FIN and piggy back an ACK. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Force Whether to ignore the sender's SWS avoidance algorithm and send + out data by force. + + @return The number of bytes sent. + +**/ +INTN +TcpToSendData ( + IN TCP_CB *Tcb, + IN INTN Force + ) +{ + UINT32 Len; + INTN Sent; + UINT8 Flag; + NET_BUF *Nbuf; + TCP_SEG *Seg; + TCP_SEQNO Seq; + TCP_SEQNO End; + + ASSERT (Tcb && Tcb->Sk && (Tcb->State != TCP_LISTEN)); + + Sent = 0; + + if ((Tcb->State == TCP_CLOSED) || + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) { + + return 0; + } + +SEND_AGAIN: + // + // compute how much data can be sent + // + Len = TcpDataToSend (Tcb, Force); + Seq = Tcb->SndNxt; + + Flag = mTcpOutFlag[Tcb->State]; + + if (Flag & TCP_FLG_SYN) { + + Seq = Tcb->Iss; + Len = 0; + } + + // + // only send a segment without data if SYN or + // FIN is set. + // + if ((Len == 0) && !(Flag & (TCP_FLG_SYN | TCP_FLG_FIN))) { + return Sent; + } + + Nbuf = TcpGetSegment (Tcb, Seq, Len); + + if (Nbuf == NULL) { + TCP4_DEBUG_ERROR ( + ("TcpToSendData: failed to get a segment for TCB %x\n", + Tcb) + ); + + goto OnError; + } + + Seg = TCPSEG_NETBUF (Nbuf); + + // + // Set the TcpSeg in Nbuf. + // + Len = Nbuf->TotalSize; + End = Seq + Len; + if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) { + End++; + } + + if (Flag & TCP_FLG_FIN) { + // + // Send FIN if all data is sent, and FIN is + // in the window + // + if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) && + (GET_SND_DATASIZE (Tcb->Sk) == 0) && + TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2) + ) { + + TCP4_DEBUG_TRACE (("TcpToSendData: send FIN " + "to peer for TCB %x in state %d\n", Tcb, Tcb->State)); + + End++; + } else { + TCP_CLEAR_FLG (Flag, TCP_FLG_FIN); + } + } + + Seg->Seq = Seq; + Seg->End = End; + Seg->Flag = Flag; + + ASSERT (TcpVerifySegment (Nbuf)); + ASSERT (TcpCheckSndQue (&Tcb->SndQue)); + + // + // don't send an empty segment here. + // + if (Seg->End == Seg->Seq) { + TCP4_DEBUG_WARN (("TcpToSendData: created a empty" + " segment for TCB %x, free it now\n", Tcb)); + + NetbufFree (Nbuf); + return Sent; + } + + if (TcpTransmitSegment (Tcb, Nbuf) != 0) { + // + // TODO: double check this + // + NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + if (Flag & TCP_FLG_FIN) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT); + } + + goto OnError; + } + + Sent += TCP_SUB_SEQ (End, Seq); + + // + // All the buffer in the SndQue is headless + // + ASSERT (Nbuf->Tcp); + + NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + NetbufFree (Nbuf); + + // + // update status in TCB + // + Tcb->DelayedAck = 0; + + if (Flag & TCP_FLG_FIN) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT); + } + + if (TCP_SEQ_GT (End, Tcb->SndNxt)) { + Tcb->SndNxt = End; + } + + if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) { + TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto); + } + + // + // Enable RTT measurement only if not in retransmit. + // Karn's algorithm reqires not to update RTT when in loss. + // + if ((Tcb->CongestState == TCP_CONGEST_OPEN) && + !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { + + TCP4_DEBUG_TRACE (("TcpToSendData: set RTT measure " + "sequence %d for TCB %x\n", Seq, Tcb)); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + Tcb->RttSeq = Seq; + Tcb->RttMeasure = 0; + } + + if (Len == Tcb->SndMss) { + goto SEND_AGAIN; + } + + return Sent; + +OnError: + if (Nbuf != NULL) { + NetbufFree (Nbuf); + } + + return Sent; +} + + +/** + Send an ACK immediately. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpSendAck ( + IN TCP_CB *Tcb + ) +{ + NET_BUF *Nbuf; + TCP_SEG *Seg; + + Nbuf = NetbufAlloc (TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + Seg = TCPSEG_NETBUF (Nbuf); + Seg->Seq = Tcb->SndNxt; + Seg->End = Tcb->SndNxt; + Seg->Flag = TCP_FLG_ACK; + + if (TcpTransmitSegment (Tcb, Nbuf) == 0) { + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + Tcb->DelayedAck = 0; + } + + NetbufFree (Nbuf); +} + + +/** + Send a zero probe segment. It can be used by keepalive + and zero window probe. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 The zero probe segment was sent out successfully. + @retval other Error condition occurred. + +**/ +INTN +TcpSendZeroProbe ( + IN TCP_CB *Tcb + ) +{ + NET_BUF *Nbuf; + TCP_SEG *Seg; + INTN Result; + + Nbuf = NetbufAlloc (TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return -1; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + // + // SndNxt-1 is out of window. The peer should respond + // with an ACK. + // + Seg = TCPSEG_NETBUF (Nbuf); + Seg->Seq = Tcb->SndNxt - 1; + Seg->End = Tcb->SndNxt - 1; + Seg->Flag = TCP_FLG_ACK; + + Result = TcpTransmitSegment (Tcb, Nbuf); + NetbufFree (Nbuf); + + return Result; +} + + +/** + Check whether to send an ACK or delayed ACK. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpToSendAck ( + IN TCP_CB *Tcb + ) +{ + // + // Generally, TCP should send a delayed ACK unless: + // 1. ACK at least every other FULL sized segment received, + // 2. Packets received out of order + // 3. Receiving window is open + // + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || + (Tcb->DelayedAck >= 1) || + (TcpRcvWinNow (Tcb) > TcpRcvWinOld (Tcb)) + ) { + TcpSendAck (Tcb); + return; + } + + TCP4_DEBUG_TRACE (("TcpToSendAck: scheduled a delayed" + " ACK for TCB %x\n", Tcb)); + + // + // schedule a delayed ACK + // + Tcb->DelayedAck++; +} + + +/** + Send a RESET segment in response to the segment received. + + @param Tcb Pointer to the TCP_CB of this TCP instance, may be NULL. + @param Head TCP header of the segment that triggers the reset. + @param Len Length of the segment that triggers the reset. + @param Local Local IP address. + @param Remote Remote peer's IP address. + + @retval 0 A reset is sent or no need to send it. + @retval -1 No reset is sent. + +**/ +INTN +TcpSendReset ( + IN TCP_CB *Tcb, + IN TCP_HEAD *Head, + IN INT32 Len, + IN UINT32 Local, + IN UINT32 Remote + ) +{ + NET_BUF *Nbuf; + TCP_HEAD *Nhead; + UINT16 HeadSum; + + // + // Don't respond to a Reset with reset + // + if (Head->Flag & TCP_FLG_RST) { + return 0; + } + + Nbuf = NetbufAlloc (TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return -1; + } + + Nhead = (TCP_HEAD *) NetbufAllocSpace ( + Nbuf, + sizeof (TCP_HEAD), + NET_BUF_TAIL + ); + + ASSERT (Nhead != NULL); + + Nbuf->Tcp = Nhead; + Nhead->Flag = TCP_FLG_RST; + + // + // Derive Seq/ACK from the segment if no TCB + // associated with it, otherwise from the Tcb + // + if (Tcb == NULL) { + + if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) { + Nhead->Seq = Head->Ack; + Nhead->Ack = 0; + } else { + Nhead->Seq = 0; + TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK); + Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len); + } + } else { + + Nhead->Seq = HTONL (Tcb->SndNxt); + Nhead->Ack = HTONL (Tcb->RcvNxt); + TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK); + } + + Nhead->SrcPort = Head->DstPort; + Nhead->DstPort = Head->SrcPort; + Nhead->HeadLen = (sizeof (TCP_HEAD) >> 2); + Nhead->Res = 0; + Nhead->Wnd = HTONS (0xFFFF); + Nhead->Checksum = 0; + Nhead->Urg = 0; + + HeadSum = NetPseudoHeadChecksum (Local, Remote, 6, 0); + Nhead->Checksum = TcpChecksum (Nbuf, HeadSum); + + TcpSendIpPacket (Tcb, Nbuf, Local, Remote); + + NetbufFree (Nbuf); + return 0; +} + + +/** + Verify that the segment is in good shape. + + @param Nbuf Buffer that contains the segment to be checked. + + @retval 0 The segment is broken. + @retval 1 The segment is in good shape. + +**/ +INTN +TcpVerifySegment ( + IN NET_BUF *Nbuf + ) +{ + TCP_HEAD *Head; + TCP_SEG *Seg; + UINT32 Len; + + if (Nbuf == NULL) { + return 1; + } + + NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE); + + Seg = TCPSEG_NETBUF (Nbuf); + Len = Nbuf->TotalSize; + Head = Nbuf->Tcp; + + if (Head != NULL) { + if (Head->Flag != Seg->Flag) { + return 0; + } + + Len -= (Head->HeadLen << 2); + } + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + Len++; + } + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + Len++; + } + + if (Seg->Seq + Len != Seg->End) { + return 0; + } + + return 1; +} + + +/** + Verify that all the segments in SndQue are in good shape. + + @param Head Pointer to the head node of the SndQue. + + @retval 0 At least one segment is broken. + @retval 1 All segments in the specific queue are in good shape. + +**/ +INTN +TcpCheckSndQue ( + IN NET_LIST_ENTRY *Head + ) +{ + NET_LIST_ENTRY *Entry; + NET_BUF *Nbuf; + TCP_SEQNO Seq; + + if (NetListIsEmpty (Head)) { + return 1; + } + // + // Initialize the Seq + // + Entry = Head->ForwardLink; + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + Seq = TCPSEG_NETBUF (Nbuf)->Seq; + + NET_LIST_FOR_EACH (Entry, Head) { + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + + if (TcpVerifySegment (Nbuf) == 0) { + return 0; + } + + // + // All the node in the SndQue should has: + // SEG.SEQ = LAST_SEG.END + // + if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) { + return 0; + } + + Seq = TCPSEG_NETBUF (Nbuf)->End; + } + + return 1; +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h new file mode 100644 index 0000000000..03daa6f04e --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h @@ -0,0 +1,355 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Tcp4Proto.h + +Abstract: + + +**/ + +#ifndef _TCP4_PROTO_H_ +#define _TCP4_PROTO_H_ + +typedef struct _TCP_CB TCP_CB; + +#include "Tcp4Driver.h" +#include "Socket.h" +#include "Tcp4Option.h" + +// +// tcp states, Don't change their order, it is used as +// index to mTcpOutFlag and other macros +// +enum { + TCP_CLOSED = 0, + TCP_LISTEN, + TCP_SYN_SENT, + TCP_SYN_RCVD, + TCP_ESTABLISHED, + TCP_FIN_WAIT_1, + TCP_FIN_WAIT_2, + TCP_CLOSING, + TCP_TIME_WAIT, + TCP_CLOSE_WAIT, + TCP_LAST_ACK, +}; + +// +// flags in the TCP header +// +enum { + + TCP_FLG_FIN = 0x01, + TCP_FLG_SYN = 0x02, + TCP_FLG_RST = 0x04, + TCP_FLG_PSH = 0x08, + TCP_FLG_ACK = 0x10, + TCP_FLG_URG = 0x20, + TCP_FLG_FLAG = 0x3F, // mask for all the flags +}; + +enum { + + // + // TCP error status + // + TCP_CONNECT_REFUSED = -1, + TCP_CONNECT_RESET = -2, + TCP_CONNECT_CLOSED = -3, + + // + // Current congestion status as suggested by RFC3782. + // + TCP_CONGEST_RECOVER = 1, // during the NewReno fast recovery + TCP_CONGEST_LOSS = 2, // retxmit because of retxmit time out + TCP_CONGEST_OPEN = 3, // TCP is opening its congestion window + + // + // TCP control flags + // + TCP_CTRL_NO_NAGLE = 0x0001, // disable Nagle algorithm + TCP_CTRL_NO_KEEPALIVE = 0x0002, // disable keepalive timer + TCP_CTRL_NO_WS = 0x0004, // disable window scale option + TCP_CTRL_RCVD_WS = 0x0008, // rcvd a wnd scale option in syn + TCP_CTRL_NO_TS = 0x0010, // disable Timestamp option + TCP_CTRL_RCVD_TS = 0x0020, // rcvd a Timestamp option in syn + TCP_CTRL_SND_TS = 0x0040, // Send Timestamp option to remote + TCP_CTRL_SND_URG = 0x0080, // in urgent send mode + TCP_CTRL_RCVD_URG = 0x0100, // in urgent receive mode + TCP_CTRL_SND_PSH = 0x0200, // in PUSH send mode + TCP_CTRL_FIN_SENT = 0x0400, // FIN is sent + TCP_CTRL_FIN_ACKED = 0x0800, // FIN is ACKed. + TCP_CTRL_TIMER_ON = 0x1000, // At least one of the timer is on + TCP_CTRL_RTT_ON = 0x2000, // The RTT measurement is on + TCP_CTRL_ACK_NOW = 0x4000, // Send the ACK now, don't delay + + // + // Timer related values + // + TCP_TIMER_CONNECT = 0, // Connection establishment timer + TCP_TIMER_REXMIT = 1, // retransmit timer + TCP_TIMER_PROBE = 2, // Window probe timer + TCP_TIMER_KEEPALIVE = 3, // Keepalive timer + TCP_TIMER_FINWAIT2 = 4, // FIN_WAIT_2 timer + TCP_TIMER_2MSL = 5, // TIME_WAIT tiemr + TCP_TIMER_NUMBER = 6, // the total number of TCP timer. + TCP_TICK = 200, // every TCP tick is 200ms + TCP_TICK_HZ = 5, // the frequence of TCP tick + TCP_RTT_SHIFT = 3, // SRTT & RTTVAR scaled by 8 + TCP_RTO_MIN = TCP_TICK_HZ, // the minium value of RTO + TCP_RTO_MAX = TCP_TICK_HZ *60, // the maxium value of RTO + TCP_FOLD_RTT = 4, // timeout threshod to fold RTT + + // + // default values for some timers + // + TCP_MAX_LOSS = 12, // default max times to retxmit + TCP_KEEPALIVE_IDLE_MIN = TCP_TICK_HZ *60 *60 *2, // First keep alive + TCP_KEEPALIVE_PERIOD = TCP_TICK_HZ *60, + TCP_MAX_KEEPALIVE = 8, + TCP_FIN_WAIT2_TIME = 2 *TCP_TICK_HZ, // * 60, + TCP_TIME_WAIT_TIME = 2 *TCP_TICK_HZ, + TCP_PAWS_24DAY = 24 *24 *60 *60 *TCP_TICK_HZ, + TCP_CONNECT_TIME = 75 *TCP_TICK_HZ, + + // + // The header space to be reserved before TCP data to accomodate : + // 60byte IP head + 60byte TCP head + link layer head + // + TCP_MAX_HEAD = 192, + + // + // value ranges for some control option + // + TCP_RCV_BUF_SIZE = 2 *1024 *1024, + TCP_RCV_BUF_SIZE_MIN = 8 *1024, + TCP_SND_BUF_SIZE = 2 *1024 *1024, + TCP_SND_BUF_SIZE_MIN = 8 *1024, + TCP_BACKLOG = 10, + TCP_BACKLOG_MIN = 5, + TCP_MAX_LOSS_MIN = 6, + TCP_CONNECT_TIME_MIN = 60 *TCP_TICK_HZ, + TCP_MAX_KEEPALIVE_MIN = 4, + TCP_KEEPALIVE_IDLE_MAX = TCP_TICK_HZ *60 *60 *4, + TCP_KEEPALIVE_PERIOD_MIN= TCP_TICK_HZ *30, + TCP_FIN_WAIT2_TIME_MAX = 4 *TCP_TICK_HZ, + TCP_TIME_WAIT_TIME_MAX = 60 *TCP_TICK_HZ, +}; + +typedef struct _TCP_SEG { + TCP_SEQNO Seq; // Starting sequence number + TCP_SEQNO End; // The sequence of the last byte + 1, + // include SYN/FIN. End-Seq = SEG.LEN + TCP_SEQNO Ack; // ACK fild in the segment + UINT8 Flag; // TCP header flags + UINT16 Urg; // Valid if URG flag is set. + UINT32 Wnd; // TCP window size field +} TCP_SEG; + +typedef struct _TCP_PEER { + UINT32 Ip; // Network byte order + TCP_PORTNO Port; // Network byte order +} TCP_PEER; + +// +// tcp control block, it includes various states +// +typedef struct _TCP_CB { + NET_LIST_ENTRY List; + TCP_CB *Parent; + + SOCKET *Sk; + TCP_PEER LocalEnd; + TCP_PEER RemoteEnd; + + NET_LIST_ENTRY SndQue; // retxmission queue + NET_LIST_ENTRY RcvQue; // reassemble queue + UINT32 CtrlFlag; // control flags, such as NO_NAGLE + INT32 Error; // soft error status,TCP_CONNECT_RESET... + + // + // RFC793 and RFC1122 defined variables + // + UINT8 State; // TCP state, such as SYN_SENT, LISTEN + UINT8 DelayedAck; // number of delayed ACKs + UINT16 HeadSum; // checksum of the fixed parts of pesudo + // header: Src IP, Dst IP, 0, Protocol, + // not include the TCP length. + + TCP_SEQNO Iss; // Initial Sending Sequence + TCP_SEQNO SndUna; // first unacknowledged data + TCP_SEQNO SndNxt; // next data sequence to send. + TCP_SEQNO SndPsh; // Send PUSH point + TCP_SEQNO SndUp; // Send urgent point + UINT32 SndWnd; // Window advertised by the remote peer + UINT32 SndWndMax; // max send window advertised by the peer + TCP_SEQNO SndWl1; // Seq number used for last window update + TCP_SEQNO SndWl2; // ack no of last window update + UINT16 SndMss; // Max send segment size + TCP_SEQNO RcvNxt; // Next sequence no to receive + UINT32 RcvWnd; // Window advertised by the local peer + TCP_SEQNO RcvWl2; // The RcvNxt (or ACK) of last window update. + // It is necessary because of delayed ACK + + TCP_SEQNO RcvUp; // urgent point; + TCP_SEQNO Irs; // Initial Receiving Sequence + UINT16 RcvMss; // Max receive segment size + UINT16 EnabledTimer; // which timer is currently enabled + UINT32 Timer[TCP_TIMER_NUMBER]; // when the timer will expire + INT32 NextExpire; // count down offset for the nearest timer + UINT32 Idle; // How long the connection is in idle + UINT32 ProbeTime; // the time out value for current window prober + + // + // RFC1323 defined variables, about window scale, + // timestamp and PAWS + // + UINT8 SndWndScale; // Wndscale received from the peer + UINT8 RcvWndScale; // Wndscale used to scale local buffer + UINT32 TsRecent; // TsRecent to echo to the remote peer + UINT32 TsRecentAge; // When this TsRecent is updated + + // TCP_SEQNO LastAckSent; + // It isn't necessary to add LastAckSent here, + // since it is the same as RcvWl2 + + // + // RFC2988 defined variables. about RTT measurement + // + TCP_SEQNO RttSeq; // the seq of measured segment now + UINT32 RttMeasure; // currently measured RTT in heart beats + UINT32 SRtt; // Smoothed RTT, scaled by 8 + UINT32 RttVar; // RTT variance, scaled by 8 + UINT32 Rto; // Current RTO, not scaled + + // + // RFC2581, and 3782 variables. + // Congestion control + NewReno fast recovery. + // + UINT32 CWnd; // Sender's congestion window + UINT32 Ssthresh; // Slow start threshold. + TCP_SEQNO Recover; // recover point for NewReno + UINT16 DupAck; // number of duplicate ACKs + UINT8 CongestState; // the current congestion state(RFC3782) + UINT8 LossTimes; // number of retxmit timeouts in a row + TCP_SEQNO LossRecover; // recover point for retxmit + + // + // configuration parameters, for EFI_TCP4_PROTOCOL specification + // + UINT32 KeepAliveIdle; // idle time before sending first probe + UINT32 KeepAlivePeriod; // interval for subsequent keep alive probe + UINT8 MaxKeepAlive; // Maxium keep alive probe times. + UINT8 KeepAliveProbes; // the number of keep alive probe. + UINT16 MaxRexmit; // The maxium number of retxmit before abort + UINT32 FinWait2Timeout; // The FIN_WAIT_2 time out + UINT32 TimeWaitTimeout; // The TIME_WAIT time out + UINT32 ConnectTimeout; + + // + // configuration for tcp provided by user + // + BOOLEAN UseDefaultAddr; + UINT8 TOS; + UINT8 TTL; + EFI_IPv4_ADDRESS SubnetMask; + + // + // pointer reference to Ip used to send pkt + // + IP_IO_IP_INFO *IpInfo; +} TCP_CB; + +extern NET_LIST_ENTRY mTcpRunQue; +extern NET_LIST_ENTRY mTcpListenQue; +extern TCP_SEQNO mTcpGlobalIss; +extern UINT32 mTcpTick; + +// +// TCP_CONNECTED: both ends have synchronized their ISN. +// +#define TCP_CONNECTED(state) ((state) > TCP_SYN_RCVD) + +#define TCP_FIN_RCVD(State) \ + (((State) == TCP_CLOSE_WAIT) || \ + ((State) == TCP_LAST_ACK) || \ + ((State) == TCP_CLOSING) || \ + ((State) == TCP_TIME_WAIT)) + +#define TCP_LOCAL_CLOSED(State) \ + (((State) == TCP_FIN_WAIT_1) || \ + ((State) == TCP_FIN_WAIT_2) || \ + ((State) == TCP_CLOSING) || \ + ((State) == TCP_TIME_WAIT) || \ + ((State) == TCP_LAST_ACK)) + +// +// Get the TCP_SEG point from a net buffer's ProtoData +// +#define TCPSEG_NETBUF(NBuf) ((TCP_SEG *) ((NBuf)->ProtoData)) + +// +// macros to compare sequence no +// +#define TCP_SEQ_LT(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) < 0) +#define TCP_SEQ_LEQ(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) <= 0) +#define TCP_SEQ_GT(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) < 0) +#define TCP_SEQ_GEQ(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) <= 0) + +// +// TCP_SEQ_BETWEEN return whether b <= m <= e +// +#define TCP_SEQ_BETWEEN(b, m, e) ((e) - (b) >= (m) - (b)) + +// +// TCP_SUB_SEQ returns Seq1 - Seq2. Make sure Seq1 >= Seq2 +// +#define TCP_SUB_SEQ(Seq1, Seq2) ((UINT32) ((Seq1) - (Seq2))) + +#define TCP_FLG_ON(Value, Flag) ((BOOLEAN) (((Value) & (Flag)) != 0)) +#define TCP_SET_FLG(Value, Flag) ((Value) |= (Flag)) +#define TCP_CLEAR_FLG(Value, Flag) ((Value) &= ~(Flag)) + +// +// test whether two peers are equal +// +#define TCP_PEER_EQUAL(Pa, Pb) \ + (((Pa)->Ip == (Pb)->Ip) && ((Pa)->Port == (Pb)->Port)) + +// +// test whether Pa matches Pb, or Pa is more specific +// than pb. Zero means wildcard. +// +#define TCP_PEER_MATCH(Pa, Pb) \ + ((((Pb)->Ip == 0) || ((Pb)->Ip == (Pa)->Ip)) && \ + (((Pb)->Port == 0) || ((Pb)->Port == (Pa)->Port))) + +#define TCP_TIMER_ON(Flag, Timer) ((Flag) & (1 << (Timer))) +#define TCP_SET_TIMER(Flag, Timer) ((Flag) |= (1 << (Timer))) +#define TCP_CLEAR_TIMER(Flag, Timer) ((Flag) &= ~(1 << (Timer))) + +#define TCP_TIME_LT(Ta, Tb) ((INT32) ((Ta) - (Tb)) < 0) +#define TCP_TIME_LEQ(Ta, Tb) ((INT32) ((Ta) - (Tb)) <= 0) +#define TCP_SUB_TIME(Ta, Tb) ((UINT32) ((Ta) - (Tb))) + +#define TCP_MAX_WIN 0xFFFFU + +typedef +VOID +(*TCP_TIMER_HANDLER) ( + IN TCP_CB * Tcb + ); + +#include "Tcp4Func.h" +#endif diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c new file mode 100644 index 0000000000..20679b4f95 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c @@ -0,0 +1,582 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Tcp4Timer.c + +Abstract: + + TCP timer related functions. + + +**/ + +#include "Tcp4Main.h" + +UINT32 mTcpTick = 1000; + +STATIC +VOID +TcpConnectTimeout ( + IN TCP_CB *Tcb + ); + +STATIC +VOID +TcpRexmitTimeout ( + IN TCP_CB *Tcb + ); + +STATIC +VOID +TcpProbeTimeout ( + IN TCP_CB *Tcb + ); + +STATIC +VOID +TcpKeepaliveTimeout ( + IN TCP_CB *Tcb + ); + +STATIC +VOID +TcpFinwait2Timeout ( + IN TCP_CB *Tcb + ); + +STATIC +VOID +Tcp2MSLTimeout ( + IN TCP_CB *Tcb + ); + +TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = { + TcpConnectTimeout, + TcpRexmitTimeout, + TcpProbeTimeout, + TcpKeepaliveTimeout, + TcpFinwait2Timeout, + Tcp2MSLTimeout, +}; + + +/** + Close the TCP connection. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpClose ( + IN TCP_CB *Tcb + ) +{ + NetbufFreeList (&Tcb->SndQue); + NetbufFreeList (&Tcb->RcvQue); + + TcpSetState (Tcb, TCP_CLOSED); +} + + +/** + Connect timeout handler. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +STATIC +VOID +TcpConnectTimeout ( + IN TCP_CB *Tcb + ) +{ + if (!TCP_CONNECTED (Tcb->State)) { + TCP4_DEBUG_ERROR (("TcpConnectTimeout: connection closed " + "because conenction timer timeout for TCB %x\n", Tcb)); + + if (EFI_ABORTED == Tcb->Sk->SockError) { + SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); + } + + if (TCP_SYN_RCVD == Tcb->State) { + TCP4_DEBUG_WARN (("TcpConnectTimeout: send reset because " + "connection timer timeout for TCB %x\n", Tcb)); + + TcpResetConnection (Tcb); + + } + + TcpClose (Tcb); + } +} + + +/** + Timeout handler for TCP retransmission timer. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +STATIC +VOID +TcpRexmitTimeout ( + IN TCP_CB *Tcb + ) +{ + UINT32 FlightSize; + + TCP4_DEBUG_WARN (("TcpRexmitTimeout: transmission " + "timeout for TCB %x\n", Tcb)); + + // + // Set the congestion window. FlightSize is the + // amount of data that has been sent but not + // yet ACKed. + // + FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna); + Tcb->Ssthresh = NET_MAX ((UINT32) (2 * Tcb->SndMss), FlightSize / 2); + + Tcb->CWnd = Tcb->SndMss; + Tcb->LossRecover = Tcb->SndNxt; + + Tcb->LossTimes++; + if (Tcb->LossTimes > Tcb->MaxRexmit && + !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) { + + TCP4_DEBUG_ERROR (("TcpRexmitTimeout: connection closed " + "because too many timeouts for TCB %x\n", Tcb)); + + if (EFI_ABORTED == Tcb->Sk->SockError) { + SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); + } + + TcpClose (Tcb); + return ; + } + + TcpBackoffRto (Tcb); + TcpRetransmit (Tcb, Tcb->SndUna); + TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto); + + Tcb->CongestState = TCP_CONGEST_LOSS; + + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); +} + + +/** + Timeout handler for window probe timer. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +STATIC +VOID +TcpProbeTimeout ( + IN TCP_CB *Tcb + ) +{ + // + // This is the timer for sender's SWSA. RFC1122 requires + // a timer set for sender's SWSA, and suggest combine it + // with window probe timer. If data is sent, don't set + // the probe timer, since retransmit timer is on. + // + if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) { + + ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)); + return ; + } + + TcpSendZeroProbe (Tcb); + TcpSetProbeTimer (Tcb); +} + + +/** + Timeout handler for keepalive timer. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +STATIC +VOID +TcpKeepaliveTimeout ( + IN TCP_CB *Tcb + ) +{ + Tcb->KeepAliveProbes++; + + // + // Too many Keep-alive probes, drop the connection + // + if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) { + + if (EFI_ABORTED == Tcb->Sk->SockError) { + SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); + } + + TcpClose (Tcb); + return ; + } + + TcpSendZeroProbe (Tcb); + TcpSetKeepaliveTimer (Tcb); +} + + +/** + Timeout handler for FIN_WAIT_2 timer. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +STATIC +VOID +TcpFinwait2Timeout ( + IN TCP_CB *Tcb + ) +{ + TCP4_DEBUG_WARN (("TcpFinwait2Timeout: connection closed " + "because FIN_WAIT2 timer timeouts for TCB %x\n", Tcb)); + + TcpClose (Tcb); +} + + +/** + Timeout handler for 2MSL timer. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +STATIC +VOID +Tcp2MSLTimeout ( + IN TCP_CB *Tcb + ) +{ + TCP4_DEBUG_WARN (("Tcp2MSLTimeout: connection closed " + "because TIME_WAIT timer timeouts for TCB %x\n", Tcb)); + + TcpClose (Tcb); +} + + +/** + Update the timer status and the next expire time + according to the timers to expire in a specific + future time slot. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +STATIC +VOID +TcpUpdateTimer ( + IN TCP_CB *Tcb + ) +{ + UINT16 Index; + + // + // Don't use a too large value to init NextExpire + // since mTcpTick wraps around as sequence no does. + // + Tcb->NextExpire = 65535; + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON); + + for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) { + + if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && + TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire)) { + + Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick); + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON); + } + } +} + + +/** + Enable a TCP timer. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Timer The index of the timer to be enabled. + @param TimeOut The timeout value of this timer. + + @return None. + +**/ +VOID +TcpSetTimer ( + IN TCP_CB *Tcb, + IN UINT16 Timer, + IN UINT32 TimeOut + ) +{ + TCP_SET_TIMER (Tcb->EnabledTimer, Timer); + Tcb->Timer[Timer] = mTcpTick + TimeOut; + + TcpUpdateTimer (Tcb); +} + + +/** + Clear one TCP timer. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Timer The index of the timer to be cleared. + + @return None. + +**/ +VOID +TcpClearTimer ( + IN TCP_CB *Tcb, + IN UINT16 Timer + ) +{ + TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer); + TcpUpdateTimer (Tcb); +} + + +/** + Clear all TCP timers. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpClearAllTimer ( + IN TCP_CB *Tcb + ) +{ + Tcb->EnabledTimer = 0; + TcpUpdateTimer (Tcb); +} + + +/** + Enable the window prober timer and set the timeout value. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpSetProbeTimer ( + IN TCP_CB *Tcb + ) +{ + if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_PROBE)) { + Tcb->ProbeTime = Tcb->Rto; + + } else { + Tcb->ProbeTime <<= 1; + } + + if (Tcb->ProbeTime < TCP_RTO_MIN) { + + Tcb->ProbeTime = TCP_RTO_MIN; + } else if (Tcb->ProbeTime > TCP_RTO_MAX) { + + Tcb->ProbeTime = TCP_RTO_MAX; + } + + TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime); +} + + +/** + Enable the keepalive timer and set the timeout value. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpSetKeepaliveTimer ( + IN TCP_CB *Tcb + ) +{ + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) { + return ; + + } + + // + // Set the timer to KeepAliveIdle if either + // 1. the keepalive timer is off + // 2. The keepalive timer is on, but the idle + // is less than KeepAliveIdle, that means the + // connection is alive since our last probe. + // + if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) || + (Tcb->Idle < Tcb->KeepAliveIdle)) { + + TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle); + Tcb->KeepAliveProbes = 0; + + } else { + + TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod); + } +} + + +/** + Backoff the RTO. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpBackoffRto ( + IN TCP_CB *Tcb + ) +{ + // + // Fold the RTT estimate if too many times, the estimate + // may be wrong, fold it. So the next time a valid + // measurement is sampled, we can start fresh. + // + if ((Tcb->LossTimes >= TCP_FOLD_RTT) && Tcb->SRtt) { + Tcb->RttVar += Tcb->SRtt >> 2; + Tcb->SRtt = 0; + } + + Tcb->Rto <<= 1; + + if (Tcb->Rto < TCP_RTO_MIN) { + + Tcb->Rto = TCP_RTO_MIN; + } else if (Tcb->Rto > TCP_RTO_MAX) { + + Tcb->Rto = TCP_RTO_MAX; + } +} + + +/** + Heart beat timer handler. + + @param Event Timer event signaled, ignored. + @param Context Context of the timer event, ignored. + + @return None. + +**/ +VOID +EFIAPI +TcpTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Next; + TCP_CB *Tcb; + INT16 Index; + + mTcpTick++; + mTcpGlobalIss += 100; + + // + // Don't use LIST_FOR_EACH, which isn't delete safe. + // + for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) { + + Next = Entry->ForwardLink; + + Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if (Tcb->State == TCP_CLOSED) { + continue; + } + // + // The connection is doing RTT measurement. + // + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { + Tcb->RttMeasure++; + } + + Tcb->Idle++; + + if (Tcb->DelayedAck) { + TcpSendAck (Tcb); + } + + // + // No timer is active or no timer expired + // + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) || + ((--Tcb->NextExpire) > 0)) { + + continue; + } + + // + // Call the timeout handler for each expired timer. + // + for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) { + + if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && + TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) { + // + // disable the timer before calling the handler + // in case the handler enables it again. + // + TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index); + mTcpTimerHandler[Index](Tcb); + + // + // The Tcb may have been deleted by the timer, or + // no other timer is set. + // + if ((Next->BackLink != Entry) || + (Tcb->EnabledTimer == 0)) { + + goto NextConnection; + } + } + } + + TcpUpdateTimer (Tcb); +NextConnection: + ; + } +} diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/Udp4Dxe/ComponentName.c new file mode 100644 index 0000000000..b934765207 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Udp4Dxe/ComponentName.c @@ -0,0 +1,169 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + ComponentName.c + +Abstract: + + +**/ + + +#include "Udp4Impl.h" + +// +// EFI Component Name Functions +// +EFI_STATUS +EFIAPI +UdpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +UdpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +EFI_COMPONENT_NAME_PROTOCOL gUdp4ComponentName = { + UdpComponentNameGetDriverName, + UdpComponentNameGetControllerName, + "eng" +}; + +static EFI_UNICODE_STRING_TABLE mUdpDriverNameTable[] = { + { + "eng", + L"UDP Network Service Driver" + }, + { + NULL, + NULL + } +}; + +EFI_STATUS +EFIAPI +UdpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +/*++ + +Routine Description: + + Retrieves a Unicode string that is the user readable name of the EFI Driver. + +Arguments: + + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + Language - A pointer to a three character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + DriverName - A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + +Returns: + + EFI_SUCCES - The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - DriverName is NULL. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return LookupUnicodeString ( + Language, + gUdp4ComponentName.SupportedLanguages, + mUdpDriverNameTable, + DriverName + ); +} + +EFI_STATUS +EFIAPI +UdpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +/*++ + +Routine Description: + + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + +Arguments: + + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + ControllerHandle - The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + ChildHandle - The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + Language - A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + ControllerName - A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language from the point of view of the driver specified + by This. + +Returns: + + EFI_SUCCESS - The Unicode string for the user readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - ControllerName is NULL. + EFI_UNSUPPORTED - The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return EFI_UNSUPPORTED; +} + diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c new file mode 100644 index 0000000000..7b47ef44a9 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c @@ -0,0 +1,526 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Udp4Driver.c + +Abstract: + + +**/ + + +#include "Udp4Impl.h" + +EFI_DRIVER_BINDING_PROTOCOL gUdp4DriverBinding = { + Udp4DriverBindingSupported, + Udp4DriverBindingStart, + Udp4DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL mUdp4ServiceBinding = { + Udp4ServiceBindingCreateChild, + Udp4ServiceBindingDestroyChild +}; + + +/** + Test to see if this driver supports ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCES This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + +**/ +EFI_STATUS +EFIAPI +Udp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Test for the Udp4ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + // + // Test for the Ip4 Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + + +/** + Start this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +Udp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + UDP4_SERVICE_DATA *Udp4Service; + + // + // Allocate Private Context Data Structure. + // + Udp4Service = NetAllocatePool (sizeof (UDP4_SERVICE_DATA)); + if (Udp4Service == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Udp4CreateService (Udp4Service, This->DriverBindingHandle, ControllerHandle); + if (EFI_ERROR (Status)) { + goto FREE_SERVICE; + } + + // + // Install the Udp4ServiceBindingProtocol on the ControllerHandle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + &Udp4Service->ServiceBinding, + NULL + ); + if (EFI_ERROR (Status)) { + goto CLEAN_SERVICE; + } + + Udp4SetVariableData (Udp4Service); + + return Status; + +CLEAN_SERVICE: + + Udp4CleanService (Udp4Service); + +FREE_SERVICE: + + NetFreePool (Udp4Service); + + return Status; +} + + +/** + Stop this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCES This driver is removed ControllerHandle. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +Udp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_HANDLE NicHandle; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UDP4_SERVICE_DATA *Udp4Service; + UDP4_INSTANCE_DATA *Instance; + + // + // Find the NicHandle where UDP4 ServiceBinding Protocol is installed. + // + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid); + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + // + // Retrieve the UDP4 ServiceBinding Protocol. + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Udp4Service = UDP4_SERVICE_DATA_FROM_THIS (ServiceBinding); + + // + // Uninstall the UDP4 ServiceBinding Protocol. + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + NicHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + &Udp4Service->ServiceBinding, + NULL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + while (!NetListIsEmpty (&Udp4Service->ChildrenList)) { + // + // Destroy all instances. + // + Instance = NET_LIST_HEAD (&Udp4Service->ChildrenList, UDP4_INSTANCE_DATA, Link); + + ServiceBinding->DestroyChild (ServiceBinding, Instance->ChildHandle); + } + + Udp4ClearVariableData (Udp4Service); + + Udp4CleanService (Udp4Service); + + NetFreePool (Udp4Service); + + return EFI_SUCCESS; +} + + +/** + Creates a child handle with a set of I/O services. + + @param This Protocol instance pointer. + @param ChildHandle Pointer to the handle of the child to create. If + it is NULL, then a new handle is created. If it + is not NULL, then the I/O services are added to + the existing child handle. + + @retval EFI_SUCCES The child handle was created with the I/O services + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child + @retval other The child handle was not created + +**/ +EFI_STATUS +EFIAPI +Udp4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ) +{ + EFI_STATUS Status; + UDP4_SERVICE_DATA *Udp4Service; + UDP4_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + VOID *Ip4; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Udp4Service = UDP4_SERVICE_DATA_FROM_THIS (This); + + // + // Allocate the instance private data structure. + // + Instance = NetAllocatePool (sizeof (UDP4_INSTANCE_DATA)); + if (Instance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Udp4InitInstance (Udp4Service, Instance); + + // + // Add an IpInfo for this instance. + // + Instance->IpInfo = IpIoAddIp (Udp4Service->IpIo); + if (Instance->IpInfo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto FREE_INSTANCE; + } + + // + // Install the Udp4Protocol for this instance. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiUdp4ProtocolGuid, + &Instance->Udp4Proto, + NULL + ); + if (EFI_ERROR (Status)) { + goto REMOVE_IPINFO; + } + + Instance->ChildHandle = *ChildHandle; + + // + // Open the default Ip4 protocol in the IP_IO BY_CHILD. + // + Status = gBS->OpenProtocol ( + Udp4Service->IpIo->ChildHandle, + &gEfiIp4ProtocolGuid, + (VOID **) &Ip4, + gUdp4DriverBinding.DriverBindingHandle, + Instance->ChildHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto UNINSTALL_PROTOCOL; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + // + // Link this instance into the service context data and increase the ChildrenNumber. + // + NetListInsertTail (&Udp4Service->ChildrenList, &Instance->Link); + Udp4Service->ChildrenNumber++; + + NET_RESTORE_TPL (OldTpl); + + return Status; + +UNINSTALL_PROTOCOL: + + gBS->UninstallMultipleProtocolInterfaces ( + Instance->ChildHandle, + &gEfiUdp4ProtocolGuid, + &Instance->Udp4Proto, + NULL + ); + +REMOVE_IPINFO: + + IpIoRemoveIp (Udp4Service->IpIo, Instance->IpInfo); + +FREE_INSTANCE: + + Udp4CleanInstance (Instance); + + NetFreePool (Instance); + + return Status; +} + + +/** + Destroys a child handle with a set of I/O services. + + @param This Protocol instance pointer. + @param ChildHandle Handle of the child to destroy + + @retval EFI_SUCCES The I/O services were removed from the child + handle + @retval EFI_UNSUPPORTED The child handle does not support the I/O services + that are being removed + @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle. + @retval EFI_ACCESS_DENIED The child handle could not be destroyed because + its I/O services are being used. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Udp4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + UDP4_SERVICE_DATA *Udp4Service; + EFI_UDP4_PROTOCOL *Udp4Proto; + UDP4_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Udp4Service = UDP4_SERVICE_DATA_FROM_THIS (This); + + // + // Try to get the Udp4 protocol from the ChildHandle. + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiUdp4ProtocolGuid, + (VOID **) &Udp4Proto, + gUdp4DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Instance = UDP4_INSTANCE_DATA_FROM_THIS (Udp4Proto); + + if (Instance->Destroyed) { + return EFI_SUCCESS; + } + + // + // Use the Destroyed flag to avoid the re-entering of the following code. + // + Instance->Destroyed = TRUE; + + // + // Close the Ip4 protocol. + // + gBS->CloseProtocol ( + Udp4Service->IpIo->ChildHandle, + &gEfiIp4ProtocolGuid, + gUdp4DriverBinding.DriverBindingHandle, + Instance->ChildHandle + ); + + // + // Uninstall the Udp4Protocol previously installed on the ChildHandle. + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiUdp4ProtocolGuid, + (VOID *) &Instance->Udp4Proto, + NULL + ); + if (EFI_ERROR (Status)) { + Instance->Destroyed = FALSE; + return Status; + } + + // + // Reset the configuration in case the instance's consumer forgets to do this. + // + Udp4Proto->Configure (Udp4Proto, NULL); + + // + // Remove the IpInfo this instance consumes. + // + IpIoRemoveIp (Udp4Service->IpIo, Instance->IpInfo); + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + // + // Remove this instance from the service context data's ChildrenList. + // + NetListRemoveEntry (&Instance->Link); + Udp4Service->ChildrenNumber--; + + // + // Clean the instance. + // + Udp4CleanInstance (Instance); + + NET_RESTORE_TPL (OldTpl); + + NetFreePool (Instance); + + return EFI_SUCCESS; +} + +//@MT: EFI_DRIVER_ENTRY_POINT (Udp4DriverEntryPoint) + +EFI_STATUS +EFIAPI +Udp4DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +/*++ + +Routine Description: + + The entry point for Udp4 driver which installs the driver binding + and component name protocol on its ImageHandle. + +Arguments: + + ImageHandle - The image handle of the driver. + SystemTable - The system table. + +Returns: + + EFI_SUCCESS - if the driver binding and component name protocols are + successfully installed, otherwise if failed. + +--*/ +{ + EFI_STATUS Status; + + // + // Install the Udp4DriverBinding and Udp4ComponentName protocols. + // + Status = NetLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &gUdp4DriverBinding, + ImageHandle, + &gUdp4ComponentName, + NULL, + NULL + ); + if (!EFI_ERROR (Status)) { + // + // Initialize the UDP random port. + // + mUdp4RandomPort = ((UINT16) NetRandomInitSeed ()) % UDP4_PORT_KNOWN + UDP4_PORT_KNOWN; + } + + return Status; +} + diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.h b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.h new file mode 100644 index 0000000000..59acb44452 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.h @@ -0,0 +1,70 @@ +/** @file + +Copyright (c) 2006, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Udp4Driver.h + +Abstract: + + +**/ + +#ifndef _UDP4_DRIVER_H_ +#define _UDP4_DRIVER_H_ + +#include +#include +#include +#include +#include + +EFI_STATUS +EFIAPI +Udp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +Udp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +Udp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +EFI_STATUS +EFIAPI +Udp4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ); + +EFI_STATUS +EFIAPI +Udp4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif + diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf new file mode 100644 index 0000000000..3f97393dc3 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf @@ -0,0 +1,66 @@ +#/** @file +# Component name for module Udp4 +# +# FIX ME! +# Copyright (c) 2006, Intel Corporation. All right reserved. +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Udp4 + FILE_GUID = 6d6963ab-906d-4a65-a7ca-bd40e5d6af2b + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = Udp4DriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + Udp4Impl.h + Udp4Main.c + ComponentName.c + Udp4Impl.c + Udp4Driver.h + Udp4Driver.c + CommonHeader.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + UefiLib + BaseLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + DebugLib + IpIoLib + NetLib + + +[Protocols] + gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiUdp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiUdp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.msa b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.msa new file mode 100644 index 0000000000..1415458270 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.msa @@ -0,0 +1,77 @@ + + + Udp4 + DXE_DRIVER + 6d6963ab-906d-4a65-a7ca-bd40e5d6af2b + 1.0 + Component name for module Udp4 + FIX ME! + Copyright (c) 2006, Intel Corporation. All right reserved. + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF EBC + false + Udp4 + + + + DebugLib + + + UefiRuntimeServicesTableLib + + + UefiDriverEntryPoint + + + UefiBootServicesTableLib + + + BaseLib + + + UefiLib + + + + Udp4Driver.c + Udp4Driver.h + Udp4Impl.c + ComponentName.c + Udp4Main.c + Udp4Impl.h + + + + + + + + gEfiUdp4ProtocolGuid + + + gEfiIp4ServiceBindingProtocolGuid + + + gEfiUdp4ServiceBindingProtocolGuid + + + gEfiIp4ProtocolGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + Udp4DriverEntryPoint + + + \ No newline at end of file diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c new file mode 100644 index 0000000000..dd7522e91f --- /dev/null +++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c @@ -0,0 +1,1951 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Udp4Impl.c + +Abstract: + + The implementation of the Udp4 protocol. + + +**/ + + +#include "Udp4Impl.h" + +UINT16 mUdp4RandomPort; + +STATIC +VOID +EFIAPI +Udp4CheckTimeout ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +STATIC +BOOLEAN +Udp4FindInstanceByPort ( + IN NET_LIST_ENTRY *InstanceList, + IN EFI_IPv4_ADDRESS *Address, + IN UINT16 Port + ); + +STATIC +VOID +Udp4DgramSent ( + IN EFI_STATUS Status, + IN VOID *Context, + IN VOID *Sender, + IN VOID *NotifyData + ); + +STATIC +VOID +Udp4DgramRcvd ( + IN EFI_STATUS Status, + IN ICMP_ERROR IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet, + IN VOID *Context + ); + +STATIC +EFI_STATUS +Udp4CancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ); + +STATIC +BOOLEAN +Udp4MatchDgram ( + IN UDP4_INSTANCE_DATA *Instance, + IN EFI_UDP4_SESSION_DATA *Udp4Session + ); + +STATIC +VOID +EFIAPI +Udp4RecycleRxDataWrap ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +STATIC +UDP4_RXDATA_WRAP * +Udp4WrapRxData ( + IN UDP4_INSTANCE_DATA *Instance, + IN NET_BUF *Packet, + IN EFI_UDP4_RECEIVE_DATA *RxData + ); + +STATIC +UINTN +Udp4EnqueueDgram ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN NET_BUF *Packet, + IN EFI_UDP4_RECEIVE_DATA *RxData + ); + +STATIC +VOID +Udp4DeliverDgram ( + IN UDP4_SERVICE_DATA *Udp4Service + ); + +STATIC +VOID +Udp4Demultiplex ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet + ); + +STATIC +VOID +Udp4IcmpHandler ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN ICMP_ERROR IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet + ); + +STATIC +VOID +Udp4SendPortUnreach ( + IN IP_IO *IpIo, + IN EFI_NET_SESSION_DATA *NetSession, + IN VOID *Udp4Header + ); + + +/** + Create the Udp service context data. + + @param Udp4Service Pointer to the UDP4_SERVICE_DATA. + @param ImageHandle The image handle of this udp4 driver. + @param ControllerHandle The controller handle this udp4 driver binds on. + + @retval EFI_SUCCESS The udp4 service context data is created and + initialized. + @retval EFI_OUT_OF_RESOURCES Cannot allocate memory. + +**/ +EFI_STATUS +Udp4CreateService ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN EFI_HANDLE ImageHandle, + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_STATUS Status; + IP_IO_OPEN_DATA OpenData; + + Udp4Service->Signature = UDP4_SERVICE_DATA_SIGNATURE; + Udp4Service->ServiceBinding = mUdp4ServiceBinding; + Udp4Service->ImageHandle = ImageHandle; + Udp4Service->ControllerHandle = ControllerHandle; + Udp4Service->ChildrenNumber = 0; + + NetListInit (&Udp4Service->ChildrenList); + + // + // Create the IpIo for this service context. + // + Udp4Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle); + if (Udp4Service->IpIo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Set the OpenData used to open the IpIo. + // + OpenData.IpConfigData = mIpIoDefaultIpConfigData; + OpenData.IpConfigData.AcceptBroadcast = TRUE; + OpenData.RcvdContext = (VOID *) Udp4Service; + OpenData.SndContext = NULL; + OpenData.PktRcvdNotify = Udp4DgramRcvd; + OpenData.PktSentNotify = Udp4DgramSent; + + // + // Configure and start the IpIo. + // + Status = IpIoOpen (Udp4Service->IpIo, &OpenData); + if (EFI_ERROR (Status)) { + goto RELEASE_IPIO; + } + + // + // Create the event for Udp timeout checking. + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + NET_TPL_FAST_TIMER, + Udp4CheckTimeout, + Udp4Service, + &Udp4Service->TimeoutEvent + ); + if (EFI_ERROR (Status)) { + goto RELEASE_IPIO; + } + + // + // Start the timeout timer event. + // + Status = gBS->SetTimer ( + Udp4Service->TimeoutEvent, + TimerPeriodic, + UDP4_TIMEOUT_INTERVAL + ); + if (EFI_ERROR (Status)) { + goto RELEASE_ALL; + } + + Udp4Service->MacString = NULL; + + return EFI_SUCCESS; + +RELEASE_ALL: + + gBS->CloseEvent (Udp4Service->TimeoutEvent); + +RELEASE_IPIO: + + IpIoDestroy (Udp4Service->IpIo); + + return Status; +} + + +/** + Clean the Udp service context data. + + @param Udp4Service Pointer to the UDP4_SERVICE_DATA. + + @return None. + +**/ +VOID +Udp4CleanService ( + IN UDP4_SERVICE_DATA *Udp4Service + ) +{ + // + // Cancel the TimeoutEvent timer. + // + gBS->SetTimer (Udp4Service->TimeoutEvent, TimerCancel, 0); + + // + // Close the TimeoutEvent timer. + // + gBS->CloseEvent (Udp4Service->TimeoutEvent); + + // + // Destroy the IpIo. + // + IpIoDestroy (Udp4Service->IpIo); +} + + +/** + This function checks and timeouts the I/O datagrams holding by the corresponding + service context. + + @param Event The event this function registered to. + @param Conext The context data registered during the creation of + the Event. + + @return None. + +**/ +STATIC +VOID +EFIAPI +Udp4CheckTimeout ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UDP4_SERVICE_DATA *Udp4Service; + NET_LIST_ENTRY *Entry; + UDP4_INSTANCE_DATA *Instance; + NET_LIST_ENTRY *WrapEntry; + NET_LIST_ENTRY *NextEntry; + UDP4_RXDATA_WRAP *Wrap; + + Udp4Service = (UDP4_SERVICE_DATA *) Context; + NET_CHECK_SIGNATURE (Udp4Service, UDP4_SERVICE_DATA_SIGNATURE); + + NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) { + // + // Iterate all the instances belonging to this service context. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link); + NET_CHECK_SIGNATURE (Instance, UDP4_INSTANCE_DATA_SIGNATURE); + + if (!Instance->Configured || (Instance->ConfigData.ReceiveTimeout == 0)) { + // + // Skip this instance if it's not configured or no receive timeout. + // + continue; + } + + NET_LIST_FOR_EACH_SAFE (WrapEntry, NextEntry, &Instance->RcvdDgramQue) { + // + // Iterate all the rxdatas belonging to this udp instance. + // + Wrap = NET_LIST_USER_STRUCT (Entry, UDP4_RXDATA_WRAP, Link); + + if (Wrap->TimeoutTick <= UDP4_TIMEOUT_INTERVAL / 1000) { + // + // Remove this RxData if it timeouts. + // + Udp4RecycleRxDataWrap (NULL, (VOID *) Wrap); + } else { + Wrap->TimeoutTick -= UDP4_TIMEOUT_INTERVAL / 1000; + } + } + } +} + + +/** + This function intializes the new created udp instance. + + @param Udp4Service Pointer to the UDP4_SERVICE_DATA. + @param Instance Pointer to the un-initialized UDP4_INSTANCE_DATA. + + @return None. + +**/ +VOID +Udp4InitInstance ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN UDP4_INSTANCE_DATA *Instance + ) +{ + // + // Set the signature. + // + Instance->Signature = UDP4_INSTANCE_DATA_SIGNATURE; + + // + // Init the lists. + // + NetListInit (&Instance->Link); + NetListInit (&Instance->RcvdDgramQue); + NetListInit (&Instance->DeliveredDgramQue); + + // + // Init the NET_MAPs. + // + NetMapInit (&Instance->TxTokens); + NetMapInit (&Instance->RxTokens); + NetMapInit (&Instance->McastIps); + + // + // Save the pointer to the UDP4_SERVICE_DATA, and initialize other members. + // + Instance->Udp4Service = Udp4Service; + Instance->Udp4Proto = mUdp4Protocol; + Instance->IcmpError = EFI_SUCCESS; + Instance->Configured = FALSE; + Instance->IsNoMapping = FALSE; + Instance->Destroyed = FALSE; +} + + +/** + This function cleans the udp instance. + + @param Instance Pointer to the UDP4_INSTANCE_DATA to clean. + + @return None. + +**/ +VOID +Udp4CleanInstance ( + IN UDP4_INSTANCE_DATA *Instance + ) +{ + NetMapClean (&Instance->McastIps); + NetMapClean (&Instance->RxTokens); + NetMapClean (&Instance->TxTokens); +} + + +/** + This function finds the udp instance by the specified pair. + + @param InstanceList Pointer to the head of the list linking the udp + instances. + @param Address Pointer to the specified IPv4 address. + @param Port The udp port number. + + @return Is the specified pair found or not. + +**/ +STATIC +BOOLEAN +Udp4FindInstanceByPort ( + IN NET_LIST_ENTRY *InstanceList, + IN EFI_IPv4_ADDRESS *Address, + IN UINT16 Port + ) +{ + NET_LIST_ENTRY *Entry; + UDP4_INSTANCE_DATA *Instance; + EFI_UDP4_CONFIG_DATA *ConfigData; + + NET_LIST_FOR_EACH (Entry, InstanceList) { + // + // Iterate all the udp instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link); + ConfigData = &Instance->ConfigData; + + if (!Instance->Configured || ConfigData->AcceptAnyPort) { + // + // If the instance is not configured or the configdata of the instance indicates + // this instance accepts any port, skip it. + // + continue; + } + + if (EFI_IP_EQUAL (ConfigData->StationAddress, *Address) && + (ConfigData->StationPort == Port)) { + // + // if both the address and the port are the same, return TRUE. + // + return TRUE; + } + } + + // + // return FALSE when matching fails. + // + return FALSE; +} + + +/** + This function tries to bind the udp instance according to the configured port + allocation stragety. + + @param InstanceList Pointer to the head of the list linking the udp + instances. + @param ConfigData Pointer to the ConfigData of the instance to be + bound. + + @retval EFI_SUCCESS The bound operation is completed successfully. + @retval EFI_ACCESS_DENIED The specified by the ConfigData is + already used by other instance. + @retval EFI_OUT_OF_RESOURCES No available port resources. + +**/ +EFI_STATUS +Udp4Bind ( + IN NET_LIST_ENTRY *InstanceList, + IN EFI_UDP4_CONFIG_DATA *ConfigData + ) +{ + EFI_IPv4_ADDRESS *StationAddress; + UINT16 StartPort; + + if (ConfigData->AcceptAnyPort) { + return EFI_SUCCESS; + } + + StationAddress = &ConfigData->StationAddress; + + if (ConfigData->StationPort != 0) { + + if (!ConfigData->AllowDuplicatePort && + Udp4FindInstanceByPort (InstanceList, StationAddress, ConfigData->StationPort)) { + // + // Do not allow duplicate port and the port is already used by other instance. + // + return EFI_ACCESS_DENIED; + } + } else { + // + // select a random port for this instance; + // + + if (ConfigData->AllowDuplicatePort) { + // + // Just pick up the random port if the instance allows duplicate port. + // + ConfigData->StationPort = mUdp4RandomPort; + } else { + + StartPort = mUdp4RandomPort; + + while (Udp4FindInstanceByPort(InstanceList, StationAddress, mUdp4RandomPort)) { + + mUdp4RandomPort++; + if (mUdp4RandomPort == 0) { + mUdp4RandomPort = UDP4_PORT_KNOWN; + } + + if (mUdp4RandomPort == StartPort) { + // + // No available port. + // + return EFI_OUT_OF_RESOURCES; + } + } + + ConfigData->StationPort = mUdp4RandomPort; + } + + mUdp4RandomPort++; + if (mUdp4RandomPort == 0) { + mUdp4RandomPort = UDP4_PORT_KNOWN; + } + } + + return EFI_SUCCESS; +} + + +/** + This function is used to check whether the NewConfigData has any un-reconfigurable + parameters changed compared to the OldConfigData. + + @param OldConfigData Pointer to the current ConfigData the udp instance + uses. + @param NewConfigData Pointer to the new ConfigData. + + @return The instance is reconfigurable or not according to the NewConfigData. + +**/ +BOOLEAN +Udp4IsReconfigurable ( + IN EFI_UDP4_CONFIG_DATA *OldConfigData, + IN EFI_UDP4_CONFIG_DATA *NewConfigData + ) +{ + if ((NewConfigData->AcceptAnyPort != OldConfigData->AcceptAnyPort) || + (NewConfigData->AcceptBroadcast != OldConfigData->AcceptBroadcast) || + (NewConfigData->AcceptPromiscuous != OldConfigData->AcceptPromiscuous) || + (NewConfigData->AllowDuplicatePort != OldConfigData->AllowDuplicatePort)) { + // + // The receiving filter parameters cannot be changed. + // + return FALSE; + } + + if ((!NewConfigData->AcceptAnyPort) && + (NewConfigData->StationPort != OldConfigData->StationPort)) { + // + // The port is not changeable. + // + return FALSE; + } + + if (!NewConfigData->AcceptPromiscuous) { + + if (NewConfigData->UseDefaultAddress != OldConfigData->UseDefaultAddress) { + // + // The NewConfigData differs to the old one on the UseDefaultAddress. + // + return FALSE; + } + + if (!NewConfigData->UseDefaultAddress && + (!EFI_IP_EQUAL (NewConfigData->StationAddress, OldConfigData->StationAddress) || + !EFI_IP_EQUAL (NewConfigData->SubnetMask, OldConfigData->SubnetMask))) { + // + // If the instance doesn't use the default address, and the new address or + // new subnet mask is different from the old values. + // + return FALSE; + } + } + + if (!EFI_IP_EQUAL (NewConfigData->RemoteAddress, OldConfigData->RemoteAddress)) { + // + // The remoteaddress is not the same. + // + return FALSE; + } + + if ((EFI_IP4 (NewConfigData->RemoteAddress) != 0) && + (NewConfigData->RemotePort != OldConfigData->RemotePort)) { + // + // The RemotePort differs if it's designated in the configdata. + // + return FALSE; + } + + // + // All checks pass, return TRUE. + // + return TRUE; +} + + +/** + This function builds the Ip4 configdata from the Udp4ConfigData. + + @param Udp4ConfigData Pointer to the EFI_UDP4_CONFIG_DATA. + @param Ip4ConfigData Pointer to the EFI_IP4_CONFIG_DATA. + + @return None. + +**/ +VOID +Udp4BuildIp4ConfigData ( + IN EFI_UDP4_CONFIG_DATA *Udp4ConfigData, + IN EFI_IP4_CONFIG_DATA *Ip4ConfigData + ) +{ + *Ip4ConfigData = mIpIoDefaultIpConfigData; + Ip4ConfigData->DefaultProtocol = EFI_IP_PROTO_UDP; + Ip4ConfigData->AcceptBroadcast = Udp4ConfigData->AcceptBroadcast; + Ip4ConfigData->AcceptPromiscuous = Udp4ConfigData->AcceptPromiscuous; + Ip4ConfigData->UseDefaultAddress = Udp4ConfigData->UseDefaultAddress; + Ip4ConfigData->StationAddress = Udp4ConfigData->StationAddress; + Ip4ConfigData->SubnetMask = Udp4ConfigData->SubnetMask; + + // + // use the -1 magic number to disable the receiving process of the ip instance. + // + Ip4ConfigData->ReceiveTimeout = (UINT32) (-1); +} + + +/** + This function validates the TxToken, it returns the error code according to the spec. + + @param Instance Pointer to the udp instance context data. + @param TxToken Pointer to the token to be checked. + + @retval EFI_SUCCESS The TxToken is valid. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: This is + NULL. Token is NULL. Token.Event is NULL. + Token.Packet.TxData is NULL. + Token.Packet.TxData.FragmentCount is zero. + Token.Packet.TxData.DataLength is not equal to the + sum of fragment lengths. One or more of the + Token.Packet.TxData.FragmentTable[]. + FragmentLength fields is zero. One or more of the + Token.Packet.TxData.FragmentTable[]. + FragmentBuffer fields is NULL. + Token.Packet.TxData. GatewayAddress is not a + unicast IPv4 address if it is not NULL. One or + more IPv4 addresses in Token.Packet.TxData. + UdpSessionData are not valid unicast IPv4 + addresses if the UdpSessionData is not NULL. + @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP + packet size. + +**/ +EFI_STATUS +Udp4ValidateTxToken ( + IN UDP4_INSTANCE_DATA *Instance, + IN EFI_UDP4_COMPLETION_TOKEN *TxToken + ) +{ + EFI_UDP4_TRANSMIT_DATA *TxData; + UINT32 Index; + UINT32 TotalLen; + EFI_UDP4_CONFIG_DATA *ConfigData; + EFI_UDP4_SESSION_DATA *UdpSessionData; + IP4_ADDR SourceAddress; + + if (TxToken->Event == NULL) { + return EFI_INVALID_PARAMETER; + } + + TxData = TxToken->Packet.TxData; + + if ((TxData == NULL) || (TxData->FragmentCount == 0)) { + return EFI_INVALID_PARAMETER; + } + + TotalLen = 0; + for (Index = 0; Index < TxData->FragmentCount; Index++) { + + if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) || + (TxData->FragmentTable[Index].FragmentLength == 0)) { + // + // if the FragmentBuffer is NULL or the FragmentLeng is zero. + // + return EFI_INVALID_PARAMETER; + } + + TotalLen += TxData->FragmentTable[Index].FragmentLength; + } + + if (TotalLen != TxData->DataLength) { + // + // The TotalLen calculated by adding all the FragmentLeng doesn't equal to the + // DataLength. + // + return EFI_INVALID_PARAMETER; + } + + if ((TxData->GatewayAddress != NULL) && + !Ip4IsUnicast(EFI_NTOHL (*(TxData->GatewayAddress)), 0)) { + // + // The specified GatewayAddress is not a unicast IPv4 address while it's not 0. + // + return EFI_INVALID_PARAMETER; + } + + ConfigData = &Instance->ConfigData; + UdpSessionData = TxData->UdpSessionData; + + if (UdpSessionData != NULL) { + + SourceAddress = EFI_NTOHL (UdpSessionData->SourceAddress); + + if ((SourceAddress != 0) && !Ip4IsUnicast (SourceAddress, 0)) { + // + // Check whether SourceAddress is a valid IPv4 address in case it's not zero. + // The configured station address is used if SourceAddress is zero. + // + return EFI_INVALID_PARAMETER; + } + + if ((UdpSessionData->DestinationPort == 0) && (ConfigData->RemotePort == 0)) { + // + // Ambiguous, no avalaible DestinationPort for this token. + // + return EFI_INVALID_PARAMETER; + } + + if (EFI_IP4 (UdpSessionData->DestinationAddress) == 0) { + // + // The DestinationAddress specified in the UdpSessionData is 0. + // + return EFI_INVALID_PARAMETER; + } + } else if (EFI_IP4 (ConfigData->RemoteAddress) == 0) { + // + // the configured RemoteAddress is all zero, and the user doens't override the + // destination address. + // + return EFI_INVALID_PARAMETER; + } + + if (TxData->DataLength > UDP4_MAX_DATA_SIZE) { + return EFI_BAD_BUFFER_SIZE; + } + + return EFI_SUCCESS; +} + + +/** + This function checks whether the specified Token duplicates with the one in the Map. + + @param Map Pointer to the NET_MAP. + @param Item Pointer to the NET_MAP_ITEM contain the pointer to + the Token. + @param Context Pointer to the Token to be checked. + + @retval EFI_SUCCESS The Token specified by Context differs from the + one in the Item. + @retval EFI_ACCESS_DENIED The Token duplicates with the one in the Item. + +**/ +EFI_STATUS +Udp4TokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_UDP4_COMPLETION_TOKEN *Token; + EFI_UDP4_COMPLETION_TOKEN *TokenInItem; + + Token = (EFI_UDP4_COMPLETION_TOKEN*) Context; + TokenInItem = (EFI_UDP4_COMPLETION_TOKEN*) Item->Key; + + if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) { + // + // The Token duplicates with the TokenInItem in case either the two pointers are the + // same or the Events of these two tokens are the same. + // + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} + + +/** + This function calculates the checksum for the Packet, utilizing the pre-calculated + pseudo HeadSum to reduce some overhead. + + @param Packet Pointer to the NET_BUF contains the udp datagram. + @param HeadSum Checksum of the pseudo header execpt the length + field. + + @return The 16-bit checksum of this udp datagram. + +**/ +UINT16 +Udp4Checksum ( + IN NET_BUF *Packet, + IN UINT16 HeadSum + ) +{ + UINT16 Checksum; + + Checksum = NetbufChecksum (Packet); + Checksum = NetAddChecksum (Checksum, HeadSum); + + Checksum = NetAddChecksum (Checksum, HTONS ((UINT16) Packet->TotalSize)); + + return ~Checksum; +} + + +/** + This function removes the specified Token from the TokenMap. + + @param TokenMap Pointer to the NET_MAP containing the tokens. + @param Token Pointer to the Token to be removed. + + @retval EFI_SUCCESS The specified Token is removed from the TokenMap. + @retval EFI_NOT_FOUND The specified Token is not found in the TokenMap. + +**/ +EFI_STATUS +Udp4RemoveToken ( + IN NET_MAP *TokenMap, + IN EFI_UDP4_COMPLETION_TOKEN *Token + ) +{ + NET_MAP_ITEM *Item; + + // + // Find the Token first. + // + Item = NetMapFindKey (TokenMap, (VOID *) Token); + + if (Item != NULL) { + // + // Remove the token if it's found in the map. + // + NetMapRemoveItem (TokenMap, Item, NULL); + + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + + +/** + This function is the packet transmitting notify function registered to the IpIo + interface. It's called to signal the udp TxToken when IpIo layer completes the + transmitting of the udp datagram. + + @param Status The completion status of the output udp datagram. + @param Context Pointer to the context data. + @param Sender Pointer to the Ip sender of the udp datagram. + @param NotifyData Pointer to the notify data. + + @return None. + +**/ +STATIC +VOID +Udp4DgramSent ( + IN EFI_STATUS Status, + IN VOID *Context, + IN VOID *Sender, + IN VOID *NotifyData + ) +{ + UDP4_INSTANCE_DATA *Instance; + EFI_UDP4_COMPLETION_TOKEN *Token; + + Instance = (UDP4_INSTANCE_DATA *) Context; + Token = (EFI_UDP4_COMPLETION_TOKEN *) NotifyData; + + if (Udp4RemoveToken (&Instance->TxTokens, Token) == EFI_SUCCESS) { + // + // The token may be cancelled. Only signal it if the remove operation succeeds. + // + Token->Status = Status; + gBS->SignalEvent (Token->Event); + } +} + + +/** + This function processes the received datagram passed up by the IpIo layer. + + @param Status The status of this udp datagram. + @param IcmpError The IcmpError code, only available when Status is + EFI_ICMP_ERROR. + @param NetSession Pointer to the EFI_NET_SESSION_DATA. + @param Packet Pointer to the NET_BUF containing the received udp + datagram. + @param Context Pointer to the context data. + + @return None. + +**/ +STATIC +VOID +Udp4DgramRcvd ( + IN EFI_STATUS Status, + IN ICMP_ERROR IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet, + IN VOID *Context + ) +{ + NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE); + + // + // IpIo only passes received packets with Status EFI_SUCCESS or EFI_ICMP_ERROR. + // + if (Status == EFI_SUCCESS) { + // + // Demultiplex the received datagram. + // + Udp4Demultiplex ((UDP4_SERVICE_DATA *) Context, NetSession, Packet); + } else { + // + // Handle the ICMP_ERROR packet. + // + Udp4IcmpHandler ((UDP4_SERVICE_DATA *) Context, IcmpError, NetSession, Packet); + } +} + + +/** + This function removes the multicast group specified by Arg from the Map. + + @param Map Pointer to the NET_MAP. + @param Item Pointer to the NET_MAP_ITEM. + @param Arg Pointer to the Arg, it's the pointer to a + multicast IPv4 Address. + + @retval EFI_SUCCESS The multicast address is removed. + @retval EFI_ABORTED The specified multicast address is removed and the + Arg is not NULL. + +**/ +EFI_STATUS +Udp4LeaveGroup ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ) +{ + EFI_IPv4_ADDRESS *McastIp; + + McastIp = Arg; + + if ((McastIp != NULL) && ((UINTN) EFI_IP4 (*McastIp) != (UINTN) (Item->Key))) { + // + // McastIp is not NULL and the multicast address contained in the Item + // is not the same as McastIp. + // + return EFI_SUCCESS; + } + + // + // Remove this Item. + // + NetMapRemoveItem (Map, Item, NULL); + + if (McastIp != NULL) { + // + // Return EFI_ABORTED in case McastIp is not NULL to terminate the iteration. + // + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + This function cancle the token specified by Arg in the Map. + + @param Map Pointer to the NET_MAP. + @param Item Pointer to the NET_MAP_ITEM. + @param Arg Pointer to the token to be cancelled, if NULL, all + the tokens in this Map will be cancelled. + + @retval EFI_SUCCESS The token is cancelled if Arg is NULL or the token + is not the same as that in the Item if Arg is not + NULL. + @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is + cancelled. + +**/ +STATIC +EFI_STATUS +Udp4CancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ) +{ + EFI_UDP4_COMPLETION_TOKEN *TokenToCancel; + NET_BUF *Packet; + IP_IO *IpIo; + + if ((Arg != NULL) && (Item->Key != Arg)) { + return EFI_SUCCESS; + } + + if (Item->Value != NULL) { + // + // If the token is a transmit token, the corresponding Packet is recorded in + // Item->Value, invoke IpIo to cancel this packet first. The IpIoCancelTxToken + // will invoke Udp4DgramSent, the token will be signaled and this Item will + // be removed from the Map there. + // + Packet = (NET_BUF *) (Item->Value); + IpIo = (IP_IO *) (*((UINTN *) &Packet->ProtoData[0])); + + IpIoCancelTxToken (IpIo, Packet); + } else { + // + // The token is a receive token. Abort it and remove it from the Map. + // + TokenToCancel = (EFI_UDP4_COMPLETION_TOKEN *) Item->Key; + + TokenToCancel->Status = EFI_ABORTED; + gBS->SignalEvent (TokenToCancel->Event); + + NetMapRemoveItem (Map, Item, NULL); + } + + if (Arg != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + This function removes all the Wrap datas in the RcvdDgramQue. + + @param RcvdDgramQue Pointer to the list containing all the Wrap datas. + + @return None. + +**/ +VOID +Udp4FlushRxData ( + IN NET_LIST_ENTRY *RcvdDgramQue + ) +{ + UDP4_RXDATA_WRAP *Wrap; + EFI_TPL OldTpl; + + OldTpl = NET_RAISE_TPL (NET_TPL_RECYCLE); + + while (!NetListIsEmpty (RcvdDgramQue)) { + // + // Iterate all the Wraps in the RcvdDgramQue. + // + Wrap = NET_LIST_HEAD (RcvdDgramQue, UDP4_RXDATA_WRAP, Link); + + // + // The Wrap will be removed from the RcvdDgramQue by this function call. + // + Udp4RecycleRxDataWrap (NULL, (VOID *) Wrap); + } + + NET_RESTORE_TPL (OldTpl); +} + + + +/** + + @param Instance Pointer to the udp instance context data. + @param Token Pointer to the token to be canceled, if NULL, all + tokens in this instance will be cancelled. + + @retval EFI_SUCCESS The Token is cancelled. + @retval EFI_NOT_FOUND The Token is not found. + +**/ +EFI_STATUS +Udp4InstanceCancelToken ( + IN UDP4_INSTANCE_DATA *Instance, + IN EFI_UDP4_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Cancle this token from the TxTokens map. + // + Status = NetMapIterate (&Instance->TxTokens, Udp4CancelTokens, Token); + + if ((Token != NULL) && (Status == EFI_ABORTED)) { + // + // If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from + // the TxTokens, just return success. + // + return EFI_SUCCESS; + } + + // + // Try to cancel this token from the RxTokens map in condition either the Token + // is NULL or the specified Token is not in TxTokens. + // + Status = NetMapIterate (&Instance->RxTokens, Udp4CancelTokens, Token); + + if ((Token != NULL) && (Status == EFI_SUCCESS)) { + // + // If Token isn't NULL and Status is EFI_SUCCESS, the token is neither in the + // TxTokens nor the RxTokens, or say, it's not found. + // + return EFI_NOT_FOUND; + } + + ASSERT ((Token != NULL) || ((0 == NetMapGetCount (&Instance->TxTokens)) + && (0 == NetMapGetCount (&Instance->RxTokens)))); + + return EFI_SUCCESS; +} + + +/** + This function matches the received udp datagram with the Instance. + + @param Instance Pointer to the udp instance context data. + @param Udp4Session Pointer to the EFI_UDP4_SESSION_DATA abstracted + from the received udp datagram. + + @return The udp datagram matches the receiving requirments of the Instance or not. + +**/ +STATIC +BOOLEAN +Udp4MatchDgram ( + IN UDP4_INSTANCE_DATA *Instance, + IN EFI_UDP4_SESSION_DATA *Udp4Session + ) +{ + EFI_UDP4_CONFIG_DATA *ConfigData; + IP4_ADDR Destination; + + ConfigData = &Instance->ConfigData; + + if (ConfigData->AcceptPromiscuous) { + // + // Always matches if this instance is in the promiscuous state. + // + return TRUE; + } + + if ((!ConfigData->AcceptAnyPort && (Udp4Session->DestinationPort != ConfigData->StationPort)) || + ((ConfigData->RemotePort != 0) && (Udp4Session->SourcePort != ConfigData->RemotePort))) { + // + // The local port or the remote port doesn't match. + // + return FALSE; + } + + if ((EFI_IP4 (ConfigData->RemoteAddress) != 0) && + !EFI_IP_EQUAL (ConfigData->RemoteAddress, Udp4Session->SourceAddress)) { + // + // This datagram doesn't come from the instance's specified sender. + // + return FALSE; + } + + if ((EFI_IP4 (ConfigData->StationAddress) == 0) || + EFI_IP_EQUAL (Udp4Session->DestinationAddress, ConfigData->StationAddress)) { + // + // The instance is configured to receive datagrams destinated to any station IP or + // the destination address of this datagram matches the configured station IP. + // + return TRUE; + } + + Destination = EFI_IP4 (Udp4Session->DestinationAddress); + + if (IP4_IS_LOCAL_BROADCAST (Destination) && ConfigData->AcceptBroadcast) { + // + // The instance is configured to receive broadcast and this is a broadcast packet. + // + return TRUE; + } + + if (IP4_IS_MULTICAST (NTOHL (Destination)) && + (NULL != NetMapFindKey (&Instance->McastIps, (VOID *) (UINTN) Destination))) { + // + // It's a multicast packet and the multicast address is accepted by this instance. + // + return TRUE; + } + + return FALSE; +} + + +/** + This function removes the Wrap specified by Context and release relevant resources. + + @param Event The Event this notify function registered to. + @param Context Pointer to the context data. + + @return None. + +**/ +STATIC +VOID +EFIAPI +Udp4RecycleRxDataWrap ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UDP4_RXDATA_WRAP *Wrap; + + Wrap = (UDP4_RXDATA_WRAP *) Context; + + // + // Remove the Wrap from the list it belongs to. + // + NetListRemoveEntry (&Wrap->Link); + + // + // Free the Packet associated with this Wrap. + // + NetbufFree (Wrap->Packet); + + // + // Close the event. + // + gBS->CloseEvent (Wrap->RxData.RecycleSignal); + + NetFreePool (Wrap); +} + + +/** + This function wraps the Packet and the RxData. + + @param Instance Pointer to the instance context data. + @param Packet Pointer to the buffer containing the received + datagram. + @param RxData Pointer to the EFI_UDP4_RECEIVE_DATA of this + datagram. + + @return Pointer to the structure wrapping the RxData and the Packet. + +**/ +STATIC +UDP4_RXDATA_WRAP * +Udp4WrapRxData ( + IN UDP4_INSTANCE_DATA *Instance, + IN NET_BUF *Packet, + IN EFI_UDP4_RECEIVE_DATA *RxData + ) +{ + EFI_STATUS Status; + UDP4_RXDATA_WRAP *Wrap; + + // + // Allocate buffer for the Wrap. + // + Wrap = NetAllocatePool (sizeof (UDP4_RXDATA_WRAP) + + (Packet->BlockOpNum - 1) * sizeof (EFI_UDP4_FRAGMENT_DATA)); + if (Wrap == NULL) { + return NULL; + } + + NetListInit (&Wrap->Link); + + Wrap->RxData = *RxData; + + // + // Create the Recycle event. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + NET_TPL_RECYCLE, + Udp4RecycleRxDataWrap, + Wrap, + &Wrap->RxData.RecycleSignal + ); + if (EFI_ERROR (Status)) { + NetFreePool (Wrap); + return NULL; + } + + Wrap->Packet = Packet; + Wrap->TimeoutTick = Instance->ConfigData.ReceiveTimeout; + + return Wrap; +} + + +/** + This function enqueues the received datagram into the instances' receiving queues. + + @param Udp4Service Pointer to the udp service context data. + @param Packet Pointer to the buffer containing the received + datagram. + @param RxData Pointer to the EFI_UDP4_RECEIVE_DATA of this + datagram. + + @return The times this datagram is enqueued. + +**/ +STATIC +UINTN +Udp4EnqueueDgram ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN NET_BUF *Packet, + IN EFI_UDP4_RECEIVE_DATA *RxData + ) +{ + NET_LIST_ENTRY *Entry; + UDP4_INSTANCE_DATA *Instance; + UDP4_RXDATA_WRAP *Wrap; + UINTN Enqueued; + + Enqueued = 0; + + NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) { + // + // Iterate the instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link); + + if (!Instance->Configured) { + continue; + } + + if (Udp4MatchDgram (Instance, &RxData->UdpSession)) { + // + // Wrap the RxData and put this Wrap into the instances RcvdDgramQue. + // + Wrap = Udp4WrapRxData (Instance, Packet, RxData); + if (Wrap == NULL) { + continue; + } + + NET_GET_REF (Packet); + + NetListInsertTail (&Instance->RcvdDgramQue, &Wrap->Link); + + Enqueued++; + } + } + + return Enqueued; +} + + +/** + This function delivers the received datagrams for the specified instance. + + @param Instance Pointer to the instance context data. + + @return None. + +**/ +VOID +Udp4InstanceDeliverDgram ( + IN UDP4_INSTANCE_DATA *Instance + ) +{ + UDP4_RXDATA_WRAP *Wrap; + EFI_UDP4_COMPLETION_TOKEN *Token; + NET_BUF *Dup; + EFI_UDP4_RECEIVE_DATA *RxData; + + if (!NetListIsEmpty (&Instance->RcvdDgramQue) && + !NetMapIsEmpty (&Instance->RxTokens)) { + + Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP4_RXDATA_WRAP, Link); + + if (NET_BUF_SHARED (Wrap->Packet)) { + // + // Duplicate the Packet if it is shared between instances. + // + Dup = NetbufDuplicate (Wrap->Packet, NULL, 0); + if (Dup == NULL) { + return; + } + + NetbufFree (Wrap->Packet); + + Wrap->Packet = Dup; + } + + NetListRemoveHead (&Instance->RcvdDgramQue); + + Token = (EFI_UDP4_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL); + + // + // Build the FragmentTable and set the FragmentCount in RxData. + // + RxData = &Wrap->RxData; + RxData->FragmentCount = Wrap->Packet->BlockOpNum; + + NetbufBuildExt ( + Wrap->Packet, + (NET_FRAGMENT *) RxData->FragmentTable, + &RxData->FragmentCount + ); + + Token->Status = EFI_SUCCESS; + Token->Packet.RxData = &Wrap->RxData; + + gBS->SignalEvent (Token->Event); + + NetListInsertTail (&Instance->DeliveredDgramQue, &Wrap->Link); + } +} + + +/** + This function delivers the datagrams enqueued in the instances. + + @param Udp4Service Pointer to the udp service context data. + + @return None. + +**/ +STATIC +VOID +Udp4DeliverDgram ( + IN UDP4_SERVICE_DATA *Udp4Service + ) +{ + NET_LIST_ENTRY *Entry; + UDP4_INSTANCE_DATA *Instance; + + NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) { + // + // Iterate the instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link); + + if (!Instance->Configured) { + continue; + } + + // + // Deliver the datagrams of this instance. + // + Udp4InstanceDeliverDgram (Instance); + } +} + + +/** + This function demultiplexes the received udp datagram to the apropriate instances. + + @param Udp4Service Pointer to the udp service context data. + @param NetSession Pointer to the EFI_NET_SESSION_DATA abstrated from + the received datagram. + @param Packet Pointer to the buffer containing the received udp + datagram. + + @return None. + +**/ +STATIC +VOID +Udp4Demultiplex ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet + ) +{ + EFI_UDP4_HEADER *Udp4Header; + UINT16 HeadSum; + EFI_UDP4_RECEIVE_DATA RxData; + EFI_UDP4_SESSION_DATA *Udp4Session; + UINTN Enqueued; + + // + // Get the datagram header from the packet buffer. + // + Udp4Header = (EFI_UDP4_HEADER *) NetbufGetByte (Packet, 0, NULL); + + if (Udp4Header->Checksum != 0) { + // + // check the checksum. + // + HeadSum = NetPseudoHeadChecksum ( + NetSession->Source, + NetSession->Dest, + EFI_IP_PROTO_UDP, + 0 + ); + + if (Udp4Checksum (Packet, HeadSum) != 0) { + // + // Wrong checksum. + // + return; + } + } + + gRT->GetTime (&RxData.TimeStamp, NULL); + + Udp4Session = &RxData.UdpSession; + EFI_IP4 (Udp4Session->SourceAddress) = NetSession->Source; + EFI_IP4 (Udp4Session->DestinationAddress) = NetSession->Dest; + Udp4Session->SourcePort = NTOHS (Udp4Header->SrcPort); + Udp4Session->DestinationPort = NTOHS (Udp4Header->DstPort); + + // + // Trim the UDP header. + // + NetbufTrim (Packet, UDP4_HEADER_SIZE, TRUE); + + RxData.DataLength = (UINT32) Packet->TotalSize; + + // + // Try to enqueue this datagram into the instances. + // + Enqueued = Udp4EnqueueDgram (Udp4Service, Packet, &RxData); + + if (Enqueued == 0) { + // + // Send the port unreachable ICMP packet before we free this NET_BUF + // + Udp4SendPortUnreach (Udp4Service->IpIo, NetSession, Udp4Header); + } + + // + // Try to free the packet before deliver it. + // + NetbufFree (Packet); + + if (Enqueued > 0) { + // + // Deliver the datagram. + // + Udp4DeliverDgram (Udp4Service); + } +} + + +/** + This function builds and sends out a icmp port unreachable message. + + @param IpIo Pointer to the IP_IO instance. + @param NetSession Pointer to the EFI_NET_SESSION_DATA of the packet + causes this icmp error message. + @param Udp4Header Pointer to the udp header of the datagram causes + this icmp error message. + + @return None. + +**/ +STATIC +VOID +Udp4SendPortUnreach ( + IN IP_IO *IpIo, + IN EFI_NET_SESSION_DATA *NetSession, + IN VOID *Udp4Header + ) +{ + NET_BUF *Packet; + UINT32 Len; + IP4_ICMP_ERROR_HEAD *IcmpErrHdr; + EFI_IP4_HEADER *IpHdr; + UINT8 *Ptr; + IP_IO_OVERRIDE Override; + IP_IO_IP_INFO *IpSender; + + IpSender = IpIoFindSender (&IpIo, NetSession->Dest); + if (IpSender == NULL) { + // + // No apropriate sender, since we cannot send out the ICMP message through + // the default zero station address IP instance, abort. + // + return; + } + + IpHdr = NetSession->IpHdr; + + // + // Calculate the requried length of the icmp error message. + // + Len = sizeof (IP4_ICMP_ERROR_HEAD) + (EFI_IP4_HEADER_LEN (IpHdr) - + sizeof (IP4_HEAD)) + ICMP_ERROR_PACKET_LENGTH; + + // + // Allocate buffer for the icmp error message. + // + Packet = NetbufAlloc (Len); + if (Packet == NULL) { + return; + } + + // + // Allocate space for the IP4_ICMP_ERROR_HEAD. + // + IcmpErrHdr = (IP4_ICMP_ERROR_HEAD *) NetbufAllocSpace (Packet, Len, FALSE); + + // + // Set the required fields for the icmp port unreachable message. + // + IcmpErrHdr->Head.Type = ICMP_TYPE_UNREACH; + IcmpErrHdr->Head.Code = ICMP_CODE_UNREACH_PORT; + IcmpErrHdr->Head.Checksum = 0; + IcmpErrHdr->Fourth = 0; + + // + // Copy the IP header of the datagram tragged the error. + // + NetCopyMem (&IcmpErrHdr->IpHead, IpHdr, EFI_IP4_HEADER_LEN (IpHdr)); + + // + // Copy the UDP header. + // + Ptr = (UINT8 *) &IcmpErrHdr->IpHead + EFI_IP4_HEADER_LEN (IpHdr); + NetCopyMem (Ptr, Udp4Header, ICMP_ERROR_PACKET_LENGTH); + + // + // Calculate the checksum. + // + IcmpErrHdr->Head.Checksum = ~(NetbufChecksum (Packet)); + + // + // Fill the override data. + // + Override.DoNotFragment = FALSE; + Override.TypeOfService = 0; + Override.TimeToLive = 255; + Override.Protocol = EFI_IP_PROTO_ICMP; + EFI_IP4 (Override.SourceAddress) = NetSession->Dest; + EFI_IP4 (Override.GatewayAddress) = 0; + + // + // Send out this icmp packet. + // + IpIoSend (IpIo, Packet, IpSender, NULL, NULL, NetSession->Source, &Override); + + NetbufFree (Packet); +} + + +/** + This function handles the received Icmp Error message and demultiplexes it to the + instance. + + @param Udp4Service Pointer to the udp service context data. + @param IcmpError The icmp error code. + @param NetSession Pointer to the EFI_NET_SESSION_DATA abstracted + from the received Icmp Error packet. + @param Packet Pointer to the Icmp Error packet. + + @return None. + +**/ +STATIC +VOID +Udp4IcmpHandler ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN ICMP_ERROR IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet + ) +{ + EFI_UDP4_HEADER *Udp4Header; + EFI_UDP4_SESSION_DATA Udp4Session; + NET_LIST_ENTRY *Entry; + UDP4_INSTANCE_DATA *Instance; + + Udp4Header = (EFI_UDP4_HEADER *) NetbufGetByte (Packet, 0, NULL); + + EFI_IP4 (Udp4Session.SourceAddress) = NetSession->Source; + EFI_IP4 (Udp4Session.DestinationAddress) = NetSession->Dest; + Udp4Session.SourcePort = NTOHS (Udp4Header->DstPort); + Udp4Session.DestinationPort = NTOHS (Udp4Header->SrcPort); + + NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) { + // + // Iterate all the instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link); + + if (!Instance->Configured || + Instance->ConfigData.AcceptPromiscuous || + Instance->ConfigData.AcceptAnyPort || + (EFI_IP4 (Instance->ConfigData.StationAddress) == 0)) { + // + // Don't try to deliver the ICMP error to this instance if it is not configured, + // or it's configured to be promiscuous or accept any port or accept all the + // datagrams. + // + continue; + } + + if (Udp4MatchDgram (Instance, &Udp4Session)) { + // + // Translate the Icmp Error code according to the udp spec. + // + Instance->IcmpError = IpIoGetIcmpErrStatus (IcmpError, NULL, NULL); + + if (IcmpError > ICMP_ERR_UNREACH_PORT) { + Instance->IcmpError = EFI_ICMP_ERROR; + } + + // + // Notify the instance with the received Icmp Error. + // + Udp4ReportIcmpError (Instance); + + break; + } + } + + NetbufFree (Packet); +} + + +/** + This function reports the received ICMP error. + + @param Instance Pointer to the udp instance context data. + + @return None. + +**/ +VOID +Udp4ReportIcmpError ( + IN UDP4_INSTANCE_DATA *Instance + ) +{ + EFI_UDP4_COMPLETION_TOKEN *Token; + + if (NetMapIsEmpty (&Instance->RxTokens)) { + // + // There are no receive tokens to deliver the ICMP error. + // + return; + } + + if (EFI_ERROR (Instance->IcmpError)) { + // + // Try to get a RxToken from the RxTokens map. + // + Token = (EFI_UDP4_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL); + + if (Token != NULL) { + // + // Report the error through the Token. + // + Token->Status = Instance->IcmpError; + gBS->SignalEvent (Token->Event); + + // + // Clear the IcmpError. + // + Instance->IcmpError = EFI_SUCCESS; + } + } +} + + +/** + This function is a dummy ext-free function for the NET_BUF created for the output + udp datagram. + + @param Context Pointer to the context data. + + @return None. + +**/ +VOID +Udp4NetVectorExtFree ( + VOID *Context + ) +{ +} + + +/** + Set the Udp4 variable data. + + @param Udp4Service Udp4 service data. + + @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the + variable. + @retval other Set variable failed. + +**/ +EFI_STATUS +Udp4SetVariableData ( + IN UDP4_SERVICE_DATA *Udp4Service + ) +{ + UINT32 NumConfiguredInstance; + NET_LIST_ENTRY *Entry; + UINTN VariableDataSize; + EFI_UDP4_VARIABLE_DATA *Udp4VariableData; + EFI_UDP4_SERVICE_POINT *Udp4ServicePoint; + UDP4_INSTANCE_DATA *Udp4Instance; + CHAR16 *NewMacString; + EFI_STATUS Status; + + NumConfiguredInstance = 0; + + // + // Go through the children list to count the configured children. + // + NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) { + Udp4Instance = NET_LIST_USER_STRUCT_S ( + Entry, + UDP4_INSTANCE_DATA, + Link, + UDP4_INSTANCE_DATA_SIGNATURE + ); + + if (Udp4Instance->Configured) { + NumConfiguredInstance++; + } + } + + // + // Calculate the size of the Udp4VariableData. As there may be no Udp4 child, + // we should add extra buffer for the service points only if the number of configured + // children is more than 1. + // + VariableDataSize = sizeof (EFI_UDP4_VARIABLE_DATA); + + if (NumConfiguredInstance > 1) { + VariableDataSize += sizeof (EFI_UDP4_SERVICE_POINT) * (NumConfiguredInstance - 1); + } + + Udp4VariableData = NetAllocatePool (VariableDataSize); + if (Udp4VariableData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Udp4VariableData->DriverHandle = Udp4Service->ImageHandle; + Udp4VariableData->ServiceCount = NumConfiguredInstance; + + Udp4ServicePoint = &Udp4VariableData->Services[0]; + + // + // Go through the children list to fill the configured children's address pairs. + // + NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) { + Udp4Instance = NET_LIST_USER_STRUCT_S ( + Entry, + UDP4_INSTANCE_DATA, + Link, + UDP4_INSTANCE_DATA_SIGNATURE + ); + + if (Udp4Instance->Configured) { + Udp4ServicePoint->InstanceHandle = Udp4Instance->ChildHandle; + Udp4ServicePoint->LocalAddress = Udp4Instance->ConfigData.StationAddress; + Udp4ServicePoint->LocalPort = Udp4Instance->ConfigData.StationPort; + Udp4ServicePoint->RemoteAddress = Udp4Instance->ConfigData.RemoteAddress; + Udp4ServicePoint->RemotePort = Udp4Instance->ConfigData.RemotePort; + + Udp4ServicePoint++; + } + } + + // + // Get the mac string. + // + Status = NetLibGetMacString ( + Udp4Service->ControllerHandle, + Udp4Service->ImageHandle, + &NewMacString + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + if (Udp4Service->MacString != NULL) { + // + // The variable is set already, we're going to update it. + // + if (StrCmp (Udp4Service->MacString, NewMacString) != 0) { + // + // The mac address is changed, delete the previous variable first. + // + gRT->SetVariable ( + Udp4Service->MacString, + &gEfiUdp4ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + NULL + ); + } + + NetFreePool (Udp4Service->MacString); + } + + Udp4Service->MacString = NewMacString; + + Status = gRT->SetVariable ( + Udp4Service->MacString, + &gEfiUdp4ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + VariableDataSize, + (VOID *) Udp4VariableData + ); + +ON_ERROR: + + NetFreePool (Udp4VariableData); + + return Status; +} + + +/** + Clear the variable and free the resource. + + @param Udp4Service Udp4 service data. + + @return None. + +**/ +VOID +Udp4ClearVariableData ( + IN UDP4_SERVICE_DATA *Udp4Service + ) +{ + ASSERT (Udp4Service->MacString != NULL); + + gRT->SetVariable ( + Udp4Service->MacString, + &gEfiUdp4ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + NULL + ); + + NetFreePool (Udp4Service->MacString); + Udp4Service->MacString = NULL; +} diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.h b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.h new file mode 100644 index 0000000000..7825624090 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.h @@ -0,0 +1,299 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Udp4Impl.h + +Abstract: + + EFI UDPv4 protocol implementation + + +**/ + +#ifndef _UDP4_IMPL_H_ +#define _UDP4_IMPL_H_ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Udp4Driver.h" + + +extern EFI_COMPONENT_NAME_PROTOCOL gUdp4ComponentName; +extern EFI_SERVICE_BINDING_PROTOCOL mUdp4ServiceBinding; +extern EFI_UDP4_PROTOCOL mUdp4Protocol; +extern UINT16 mUdp4RandomPort; + +#define ICMP_ERROR_PACKET_LENGTH 8 + +#define UDP4_TIMEOUT_INTERVAL (50 * TICKS_PER_MS) // 50 milliseconds + +#define UDP4_HEADER_SIZE sizeof (EFI_UDP4_HEADER) +#define UDP4_MAX_DATA_SIZE 65507 + +#define UDP4_PORT_KNOWN 1024 + +#define UDP4_SERVICE_DATA_SIGNATURE EFI_SIGNATURE_32('U', 'd', 'p', '4') + +#define UDP4_SERVICE_DATA_FROM_THIS(a) \ + CR ( \ + (a), \ + UDP4_SERVICE_DATA, \ + ServiceBinding, \ + UDP4_SERVICE_DATA_SIGNATURE \ + ) + +typedef struct _UDP4_SERVICE_DATA_ { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + EFI_HANDLE ImageHandle; + EFI_HANDLE ControllerHandle; + NET_LIST_ENTRY ChildrenList; + UINTN ChildrenNumber; + IP_IO *IpIo; + + EFI_EVENT TimeoutEvent; + + CHAR16 *MacString; +} UDP4_SERVICE_DATA; + +#define UDP4_INSTANCE_DATA_SIGNATURE EFI_SIGNATURE_32('U', 'd', 'p', 'I') + +#define UDP4_INSTANCE_DATA_FROM_THIS(a) \ + CR ( \ + (a), \ + UDP4_INSTANCE_DATA, \ + Udp4Proto, \ + UDP4_INSTANCE_DATA_SIGNATURE \ + ) + +typedef struct _UDP4_INSTANCE_DATA_ { + UINT32 Signature; + NET_LIST_ENTRY Link; + + UDP4_SERVICE_DATA *Udp4Service; + EFI_UDP4_PROTOCOL Udp4Proto; + EFI_UDP4_CONFIG_DATA ConfigData; + EFI_HANDLE ChildHandle; + BOOLEAN Configured; + BOOLEAN IsNoMapping; + + NET_MAP TxTokens; + NET_MAP RxTokens; + + NET_MAP McastIps; + + NET_LIST_ENTRY RcvdDgramQue; + NET_LIST_ENTRY DeliveredDgramQue; + + UINT16 HeadSum; + + EFI_STATUS IcmpError; + + IP_IO_IP_INFO *IpInfo; + + BOOLEAN Destroyed; +} UDP4_INSTANCE_DATA; + +typedef struct _UDP4_RXDATA_WRAP_ { + NET_LIST_ENTRY Link; + NET_BUF *Packet; + UINT32 TimeoutTick; + EFI_UDP4_RECEIVE_DATA RxData; +} UDP4_RXDATA_WRAP; + +EFI_STATUS +EFIAPI +Udp4GetModeData ( + IN EFI_UDP4_PROTOCOL *This, + OUT EFI_UDP4_CONFIG_DATA *Udp4ConfigData OPTIONAL, + OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +EFI_STATUS +EFIAPI +Udp4Configure ( + IN EFI_UDP4_PROTOCOL *This, + IN EFI_UDP4_CONFIG_DATA *UdpConfigData OPTIONAL + ); + +EFI_STATUS +EFIAPI +Udp4Groups ( + IN EFI_UDP4_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv4_ADDRESS *MulticastAddress OPTIONAL + ); + +EFI_STATUS +EFIAPI +Udp4Routes ( + IN EFI_UDP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ); + +EFI_STATUS +EFIAPI +Udp4Transmit ( + IN EFI_UDP4_PROTOCOL *This, + IN EFI_UDP4_COMPLETION_TOKEN *Token + ); + +EFI_STATUS +EFIAPI +Udp4Receive ( + IN EFI_UDP4_PROTOCOL *This, + IN EFI_UDP4_COMPLETION_TOKEN *Token + ); + +EFI_STATUS +EFIAPI +Udp4Cancel ( + IN EFI_UDP4_PROTOCOL *This, + IN EFI_UDP4_COMPLETION_TOKEN *Token OPTIONAL + ); + +EFI_STATUS +EFIAPI +Udp4Poll ( + IN EFI_UDP4_PROTOCOL *This + ); + +EFI_STATUS +Udp4CreateService ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN EFI_HANDLE ImageHandle, + IN EFI_HANDLE ControllerHandle + ); + +VOID +Udp4CleanService ( + IN UDP4_SERVICE_DATA *Udp4Service + ); + +VOID +Udp4InitInstance ( + IN UDP4_SERVICE_DATA *Udp4Service, + IN UDP4_INSTANCE_DATA *Instance + ); + +VOID +Udp4CleanInstance ( + IN UDP4_INSTANCE_DATA *Instance + ); + +EFI_STATUS +Udp4Bind ( + IN NET_LIST_ENTRY *InstanceList, + IN EFI_UDP4_CONFIG_DATA *ConfigData + ); + +BOOLEAN +Udp4IsReconfigurable ( + IN EFI_UDP4_CONFIG_DATA *OldConfigData, + IN EFI_UDP4_CONFIG_DATA *NewConfigData + ); + +VOID +Udp4BuildIp4ConfigData ( + IN EFI_UDP4_CONFIG_DATA *Udp4ConfigData, + IN EFI_IP4_CONFIG_DATA *Ip4ConfigData + ); + +EFI_STATUS +Udp4ValidateTxToken ( + IN UDP4_INSTANCE_DATA *Instance, + IN EFI_UDP4_COMPLETION_TOKEN *TxToken + ); + +EFI_STATUS +Udp4TokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ); + +UINT16 +Udp4Checksum ( + IN NET_BUF *Packet, + IN UINT16 HeadSum + ); + +EFI_STATUS +Udp4RemoveToken ( + IN NET_MAP *TokenMap, + IN EFI_UDP4_COMPLETION_TOKEN *Token + ); + +EFI_STATUS +Udp4LeaveGroup ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ); + +VOID +Udp4FlushRxData ( + IN NET_LIST_ENTRY *RcvdDgramQue + ); + +EFI_STATUS +Udp4InstanceCancelToken ( + IN UDP4_INSTANCE_DATA *Instance, + IN EFI_UDP4_COMPLETION_TOKEN *Token OPTIONAL + ); + +VOID +Udp4InstanceDeliverDgram ( + IN UDP4_INSTANCE_DATA *Instance + ); + +VOID +Udp4ReportIcmpError ( + IN UDP4_INSTANCE_DATA *Instance + ); + +VOID +Udp4NetVectorExtFree ( + VOID *Context + ); + +EFI_STATUS +Udp4SetVariableData ( + IN UDP4_SERVICE_DATA *Udp4Service + ); + +VOID +Udp4ClearVariableData ( + IN UDP4_SERVICE_DATA *Udp4Service + ); + +#endif + diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c new file mode 100644 index 0000000000..4f897cb54b --- /dev/null +++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c @@ -0,0 +1,869 @@ +/** @file + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Udp4Main.c + +Abstract: + + +**/ + +#include "Udp4Impl.h" + +#include + +EFI_UDP4_PROTOCOL mUdp4Protocol = { + Udp4GetModeData, + Udp4Configure, + Udp4Groups, + Udp4Routes, + Udp4Transmit, + Udp4Receive, + Udp4Cancel, + Udp4Poll +}; + + +/** + This function copies the current operational settings of this EFI UDPv4 Protocol + instance into user-supplied buffers. This function is used optionally to retrieve + the operational mode data of underlying networks or drivers. + + @param This Pointer to the EFI_UDP4_PROTOCOL instance. + @param Udp4ConfigData Pointer to the buffer to receive the current + configuration data. + @param Ip4ModeData Pointer to the EFI IPv4 Protocol mode data + structure. + @param MnpConfigData Pointer to the managed network configuration data + structure. + @param SnpModeData Pointer to the simple network mode data structure. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED When Udp4ConfigData is queried, no configuration + data is available because this instance has not + been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +Udp4GetModeData ( + IN EFI_UDP4_PROTOCOL *This, + OUT EFI_UDP4_CONFIG_DATA *Udp4ConfigData OPTIONAL, + OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ) +{ + UDP4_INSTANCE_DATA *Instance; + EFI_IP4_PROTOCOL *Ip; + EFI_TPL OldTpl; + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP4_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured && (Udp4ConfigData != NULL)) { + return EFI_NOT_STARTED; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (Udp4ConfigData != NULL) { + // + // Set the Udp4ConfigData. + // + *Udp4ConfigData = Instance->ConfigData; + } + + Ip = Instance->IpInfo->Ip; + + // + // Get the underlying Ip4ModeData, MnpConfigData and SnpModeData. + // + Status = Ip->GetModeData (Ip, Ip4ModeData, MnpConfigData, SnpModeData); + + NET_RESTORE_TPL (OldTpl); + + return Status; +} + + +/** + This function is used to do the following: + Initialize and start this instance of the EFI UDPv4 Protocol. + Change the filtering rules and operational parameters. + Reset this instance of the EFI UDPv4 Protocol. + + @param This Pointer to the EFI_UDP4_PROTOCOL instance. + @param UdpConfigData Pointer to the buffer to receive the current mode + data. + + @retval EFI_SUCCESS The configuration settings were set, changed, or + reset successfully. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, + BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE: This is + NULL. UdpConfigData.StationAddress is not a valid + unicast IPv4 address. UdpConfigData.SubnetMask is + not a valid IPv4 address mask. + UdpConfigData.RemoteAddress is not a valid unicast + IPv4 address if it is not zero. + @retval EFI_ALREADY_STARTED The EFI UDPv4 Protocol instance is already + started/configured and must be stopped/reset + before it can be reconfigured. Only TypeOfService, + TimeToLive, DoNotFragment, ReceiveTimeout, and + TransmitTimeout can be reconfigured without + stopping the current instance of the EFI UDPv4 + Protocol. + @retval EFI_ACCESS_DENIED UdpConfigData.AllowDuplicatePort is FALSE and + UdpConfigData.StationPort is already used by other + instance. + @retval EFI_OUT_OF_RESOURCES The EFI UDPv4 Protocol driver cannot allocate + memory for this EFI UDPv4 Protocol instance. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred and + this instance was not opened. + +**/ +EFI_STATUS +EFIAPI +Udp4Configure ( + IN EFI_UDP4_PROTOCOL *This, + IN EFI_UDP4_CONFIG_DATA *UdpConfigData OPTIONAL + ) +{ + EFI_STATUS Status; + UDP4_INSTANCE_DATA *Instance; + UDP4_SERVICE_DATA *Udp4Service; + EFI_TPL OldTpl; + IP4_ADDR StationAddress; + IP4_ADDR SubnetMask; + IP4_ADDR RemoteAddress; + EFI_IP4_CONFIG_DATA Ip4ConfigData; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP4_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured && (UdpConfigData == NULL)) { + return EFI_SUCCESS; + } + + Udp4Service = Instance->Udp4Service; + Status = EFI_SUCCESS; + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (UdpConfigData != NULL) { + + StationAddress = EFI_NTOHL (UdpConfigData->StationAddress); + SubnetMask = EFI_NTOHL (UdpConfigData->SubnetMask); + RemoteAddress = EFI_NTOHL (UdpConfigData->RemoteAddress); + + if (!UdpConfigData->UseDefaultAddress && + (!IP4_IS_VALID_NETMASK (SubnetMask) || + !((StationAddress == 0) || Ip4IsUnicast (StationAddress, SubnetMask)) || + !((RemoteAddress == 0) || Ip4IsUnicast (RemoteAddress, 0)))) { + // + // Don't use default address, and subnet mask is invalid or StationAddress is not + // a valid unicast IPv4 address or RemoteAddress is not a valid unicast IPv4 address + // if it is not 0. + // + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + if (Instance->Configured) { + // + // The instance is already configured, try to do the re-configuration. + // + if (!Udp4IsReconfigurable (&Instance->ConfigData, UdpConfigData)) { + // + // If the new configuration data wants to change some unreconfigurable + // settings, return EFI_ALREADY_STARTED. + // + Status = EFI_ALREADY_STARTED; + goto ON_EXIT; + } + + // + // Save the reconfigurable parameters. + // + Instance->ConfigData.TypeOfService = UdpConfigData->TypeOfService; + Instance->ConfigData.TimeToLive = UdpConfigData->TimeToLive; + Instance->ConfigData.DoNotFragment = UdpConfigData->DoNotFragment; + Instance->ConfigData.ReceiveTimeout = UdpConfigData->ReceiveTimeout; + Instance->ConfigData.TransmitTimeout = UdpConfigData->TransmitTimeout; + } else { + // + // Construct the Ip configuration data from the UdpConfigData. + // + Udp4BuildIp4ConfigData (UdpConfigData, &Ip4ConfigData); + + // + // Configure the Ip instance wrapped in the IpInfo. + // + Status = IpIoConfigIp (Instance->IpInfo, &Ip4ConfigData); + if (EFI_ERROR (Status)) { + if (Status == EFI_NO_MAPPING) { + Instance->IsNoMapping = TRUE; + } + + goto ON_EXIT; + } + + Instance->IsNoMapping = FALSE; + + // + // Save the configuration data. + // + Instance->ConfigData = *UdpConfigData; + Instance->ConfigData.StationAddress = Ip4ConfigData.StationAddress; + Instance->ConfigData.SubnetMask = Ip4ConfigData.SubnetMask; + + // + // Try to allocate the required port resource. + // + Status = Udp4Bind (&Udp4Service->ChildrenList, &Instance->ConfigData); + if (EFI_ERROR (Status)) { + // + // Reset the ip instance if bind fails. + // + IpIoConfigIp (Instance->IpInfo, NULL); + goto ON_EXIT; + } + + // + // Pre calculate the checksum for the pseudo head, ignore the UDP length first. + // + Instance->HeadSum = NetPseudoHeadChecksum ( + EFI_IP4 (Instance->ConfigData.StationAddress), + EFI_IP4 (Instance->ConfigData.RemoteAddress), + EFI_IP_PROTO_UDP, + 0 + ); + + Instance->Configured = TRUE; + } + } else { + // + // UdpConfigData is NULL, reset the instance. + // + Instance->Configured = FALSE; + Instance->IsNoMapping = FALSE; + + // + // Reset the Ip instance wrapped in the IpInfo. + // + IpIoConfigIp (Instance->IpInfo, NULL); + + // + // Cancel all the user tokens. + // + Udp4InstanceCancelToken (Instance, NULL); + + // + // Remove the buffered RxData for this instance. + // + Udp4FlushRxData (&Instance->RcvdDgramQue); + } + + Udp4SetVariableData (Instance->Udp4Service); + +ON_EXIT: + + NET_RESTORE_TPL (OldTpl); + + return Status; +} + + +/** + This function is used to enable and disable the multicast group filtering. + + @param This Pointer to the EFI_UDP4_PROTOCOL instance. + @param JoinFlag Set to TRUE to join a multicast group. Set to + FALSE to leave one or all multicast groups. + @param MulticastAddress Pointer to multicast group address to join or + leave. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI UDPv4 Protocol instance has not been + started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, + BOOTP, RARP, etc.) is not finished yet. + @retval EFI_OUT_OF_RESOURCES Could not allocate resources to join the group. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. JoinFlag is TRUE and + MulticastAddress is NULL. JoinFlag is TRUE and + *MulticastAddress is not a valid multicast + address. + @retval EFI_ALREADY_STARTED The group address is already in the group table + (when JoinFlag is TRUE). + @retval EFI_NOT_FOUND The group address is not in the group table (when + JoinFlag is FALSE). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Udp4Groups ( + IN EFI_UDP4_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv4_ADDRESS *MulticastAddress OPTIONAL + ) +{ + EFI_STATUS Status; + UDP4_INSTANCE_DATA *Instance; + EFI_IP4_PROTOCOL *Ip; + EFI_TPL OldTpl; + + if ((This == NULL) || + (JoinFlag && (MulticastAddress == NULL)) || + (JoinFlag && !IP4_IS_MULTICAST (EFI_NTOHL (*MulticastAddress)))) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP4_INSTANCE_DATA_FROM_THIS (This); + + if (Instance->IsNoMapping) { + return EFI_NO_MAPPING; + } + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + Ip = Instance->IpInfo->Ip; + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + // + // Invoke the Ip instance the Udp4 instance consumes to do the group operation. + // + Status = Ip->Groups (Ip, JoinFlag, MulticastAddress); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Keep a local copy of the configured multicast IPs because IpIo receives + // datagrams from the 0 station address IP instance and then UDP delivers to + // the matched instance. This copy of multicast IPs is used to avoid receive + // the mutlicast datagrams destinated to multicast IPs the other instances configured. + // + if (JoinFlag) { + + NetMapInsertTail ( + &Instance->McastIps, + (VOID *) (UINTN) EFI_IP4 (*MulticastAddress), + NULL + ); + } else { + + NetMapIterate (&Instance->McastIps, Udp4LeaveGroup, MulticastAddress); + } + +ON_EXIT: + + NET_RESTORE_TPL (OldTpl); + + return Status; +} + + +/** + This function adds a route to or deletes a route from the routing table. + + @param This Pointer to the EFI_UDP4_PROTOCOL instance. + @param DeleteRoute Set to TRUE to delete this route from the routing + table. Set to FALSE to add this route to the + routing table. + @param SubnetAddress The destination network address that needs to be + routed. + @param SubnetMask The subnet mask of SubnetAddress. + @param GatewayAddress The gateway IP address for this route. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI UDPv4 Protocol instance has not been + started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, + BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. SubnetAddress is NULL. SubnetMask is + NULL. GatewayAddress is NULL. SubnetAddress is not + a valid subnet address. SubnetMask is not a valid + subnet mask. GatewayAddress is not a valid unicast + IP address. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table. + @retval EFI_ACCESS_DENIED The route is already defined in the routing table. + +**/ +EFI_STATUS +EFIAPI +Udp4Routes ( + IN EFI_UDP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ) +{ + UDP4_INSTANCE_DATA *Instance; + EFI_IP4_PROTOCOL *Ip; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP4_INSTANCE_DATA_FROM_THIS (This); + + if (Instance->IsNoMapping) { + return EFI_NO_MAPPING; + } + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + Ip = Instance->IpInfo->Ip; + + // + // Invoke the Ip instance the Udp4 instance consumes to do the actual operation. + // + return Ip->Routes (Ip, DeleteRoute, SubnetAddress, SubnetMask, GatewayAddress); +} + + +/** + This function places a sending request to this instance of the EFI UDPv4 Protocol, + alongside the transmit data that was filled by the user. + + @param This Pointer to the EFI_UDP4_PROTOCOL instance. + @param Token Pointer to the completion token that will be + placed into the transmit queue. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This EFI UDPv4 Protocol instance has not been + started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, + BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: This is + NULL. Token is NULL. Token.Event is NULL. + Token.Packet.TxData is NULL. + Token.Packet.TxData.FragmentCount is zero. + Token.Packet.TxData.DataLength is not equal to the + sum of fragment lengths. One or more of the + Token.Packet.TxData.FragmentTable[]. + FragmentLength fields is zero. One or more of the + Token.Packet.TxData.FragmentTable[]. + FragmentBuffer fields is NULL. + Token.Packet.TxData. GatewayAddress is not a + unicast IPv4 address if it is not NULL. One or + more IPv4 addresses in Token.Packet.TxData. + UdpSessionData are not valid unicast IPv4 + addresses if the UdpSessionData is not NULL. + @retval EFI_ACCESS_DENIED The transmit completion token with the same + Token.Event is already in the transmit queue. + @retval EFI_NOT_READY The completion token could not be queued because + the transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data. + @retval EFI_NOT_FOUND There is no route to the destination network or + address. + @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP + packet size. Or the length of the IP header + UDP + header + data length is greater than MTU if + DoNotFragment is TRUE. + +**/ +EFI_STATUS +EFIAPI +Udp4Transmit ( + IN EFI_UDP4_PROTOCOL *This, + IN EFI_UDP4_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + UDP4_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + NET_BUF *Packet; + EFI_UDP4_HEADER *Udp4Header; + EFI_UDP4_CONFIG_DATA *ConfigData; + IP4_ADDR Destination; + EFI_UDP4_TRANSMIT_DATA *TxData; + EFI_UDP4_SESSION_DATA *UdpSessionData; + UDP4_SERVICE_DATA *Udp4Service; + IP_IO_OVERRIDE Override; + UINT16 HeadSum; + + if ((This == NULL) || (Token == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP4_INSTANCE_DATA_FROM_THIS (This); + + if (Instance->IsNoMapping) { + return EFI_NO_MAPPING; + } + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + // + // Validate the Token, if the token is invalid return the error code. + // + Status = Udp4ValidateTxToken (Instance, Token); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + if (EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp4TokenExist, Token)) || + EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp4TokenExist, Token))) { + // + // Try to find a duplicate token in the two token maps, if found, return + // EFI_ACCESS_DENIED. + // + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + TxData = Token->Packet.TxData; + + // + // Create a net buffer to hold the user buffer and the udp header. + // + Packet = NetbufFromExt ( + (NET_FRAGMENT *)TxData->FragmentTable, + TxData->FragmentCount, + UDP4_HEADER_SIZE, + 0, + Udp4NetVectorExtFree, + NULL + ); + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Store the IpIo in ProtoData. + // + Udp4Service = Instance->Udp4Service; + *((UINTN *) &Packet->ProtoData[0]) = (UINTN) (Udp4Service->IpIo); + + Udp4Header = (EFI_UDP4_HEADER *) NetbufAllocSpace (Packet, UDP4_HEADER_SIZE, TRUE); + ConfigData = &Instance->ConfigData; + + // + // Fill the udp header. + // + Udp4Header->SrcPort = HTONS (ConfigData->StationPort); + Udp4Header->DstPort = HTONS (ConfigData->RemotePort); + Udp4Header->Length = HTONS (Packet->TotalSize); + Udp4Header->Checksum = 0; + + UdpSessionData = TxData->UdpSessionData; + Override.SourceAddress = ConfigData->StationAddress; + + if (UdpSessionData != NULL) { + // + // Set the SourceAddress, SrcPort and Destination according to the specified + // UdpSessionData. + // + if (EFI_IP4 (UdpSessionData->SourceAddress) != 0) { + Override.SourceAddress = UdpSessionData->SourceAddress; + } + + if (UdpSessionData->SourcePort != 0) { + Udp4Header->SrcPort = HTONS (UdpSessionData->SourcePort); + } + + Destination = EFI_IP4 (UdpSessionData->DestinationAddress); + + if (UdpSessionData->DestinationPort != 0) { + Udp4Header->DstPort = HTONS (UdpSessionData->DestinationPort); + } + + // + // calculate the pseudo head checksum using the overridden parameters. + // + HeadSum = NetPseudoHeadChecksum ( + EFI_IP4 (Override.SourceAddress), + Destination, + EFI_IP_PROTO_UDP, + 0 + ); + } else { + // + // UdpSessionData is NULL, use the address and port information previously configured. + // + Destination = EFI_IP4 (ConfigData->RemoteAddress); + HeadSum = Instance->HeadSum; + } + + // + // calculate the checksum. + // + Udp4Header->Checksum = Udp4Checksum (Packet, HeadSum); + if (Udp4Header->Checksum == 0) { + // + // If the calculated checksum is 0, fill the Checksum field with all ones. + // + Udp4Header->Checksum = 0xffff; + } + + // + // Fill the IpIo Override data. + // + EFI_IP4 (Override.GatewayAddress) = (TxData->GatewayAddress != NULL) ? + EFI_IP4 (*(TxData->GatewayAddress)) : 0; + Override.Protocol = EFI_IP_PROTO_UDP; + Override.TypeOfService = ConfigData->TypeOfService; + Override.TimeToLive = ConfigData->TimeToLive; + Override.DoNotFragment = ConfigData->DoNotFragment; + + // + // Save the token into the TxToken map. + // + Status = NetMapInsertTail (&Instance->TxTokens, Token, Packet); + if (EFI_ERROR (Status)) { + goto FREE_PACKET; + } + + // + // Send out this datagram through IpIo. + // + Status = IpIoSend ( + Udp4Service->IpIo, + Packet, + Instance->IpInfo, + Instance, + Token, + Destination, + &Override + ); + if (EFI_ERROR (Status)) { + // + // Remove this token from the TxTokens. + // + Udp4RemoveToken (&Instance->TxTokens, Token); + } + +FREE_PACKET: + + NetbufFree (Packet); + +ON_EXIT: + + NET_RESTORE_TPL (OldTpl); + + return Status; +} + + +/** + This function places a completion token into the receive packet queue. This function + is always asynchronous. + + @param This Pointer to the EFI_UDP4_PROTOCOL instance. + @param Token Pointer to a token that is associated with the + receive data descriptor. + + @retval EFI_SUCCESS The receive completion token is cached. + @retval EFI_NOT_STARTED This EFI UDPv4 Protocol instance has not been + started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, + BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. Token is NULL. Token.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued + due to a lack of system resources (usually + memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI UDPv4 Protocol instance has been reset to + startup defaults. + @retval EFI_ACCESS_DENIED A receive completion token with the same + Token.Event is already in the receive queue. + @retval EFI_NOT_READY The receive request could not be queued because + the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +Udp4Receive ( + IN EFI_UDP4_PROTOCOL *This, + IN EFI_UDP4_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + UDP4_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + + if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP4_INSTANCE_DATA_FROM_THIS (This); + + if (Instance->IsNoMapping) { + return EFI_NO_MAPPING; + } + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + if (EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp4TokenExist, Token))|| + EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp4TokenExist, Token))) { + // + // Return EFI_ACCESS_DENIED if the specified token is already in the TxTokens or + // RxTokens map. + // + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + Token->Packet.RxData = NULL; + + // + // Save the token into the RxTokens map. + // + Status = NetMapInsertTail (&Instance->RxTokens, Token, NULL); + if (EFI_ERROR (Status)) { + return EFI_NOT_READY; + } + + // + // If there is an icmp error, report it. + // + Udp4ReportIcmpError (Instance); + + // + // Try to delivered the received datagrams. + // + Udp4InstanceDeliverDgram (Instance); + +ON_EXIT: + + NET_RESTORE_TPL (OldTpl); + + return Status; +} + + +/** + This function is used to abort a pending transmit or receive request. + + @param This Pointer to the EFI_UDP4_PROTOCOL instance. + @param Token Pointer to a token that has been issued by + EFI_UDP4_PROTOCOL.Transmit() or + EFI_UDP4_PROTOCOL.Receive(). + + @retval EFI_SUCCESS The asynchronous I/O request is aborted and + Token.Event is signaled. When Token is NULL, all + pending requests are aborted and their events are + signaled. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NO_MAPPING When using the default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O + request is not found in the transmit or receive + queue. It is either completed or not issued by + Transmit() or Receive(). + +**/ +EFI_STATUS +EFIAPI +Udp4Cancel ( + IN EFI_UDP4_PROTOCOL *This, + IN EFI_UDP4_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + EFI_STATUS Status; + UDP4_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP4_INSTANCE_DATA_FROM_THIS (This); + + if (Instance->IsNoMapping) { + return EFI_NO_MAPPING; + } + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + // + // Cancle the tokens specified by Token for this instance. + // + Status = Udp4InstanceCancelToken (Instance, Token); + + NET_RESTORE_TPL (OldTpl); + + return Status; +} + + +/** + This function can be used by network drivers and applications to increase the rate that + data packets are moved between the communications device and the transmit/receive queues. + Argumens: + This - Pointer to the EFI_UDP4_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or + receive queue. + +**/ +EFI_STATUS +EFIAPI +Udp4Poll ( + IN EFI_UDP4_PROTOCOL *This + ) +{ + UDP4_INSTANCE_DATA *Instance; + EFI_IP4_PROTOCOL *Ip; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP4_INSTANCE_DATA_FROM_THIS (This); + Ip = Instance->IpInfo->Ip; + + // + // Invode the Ip instance consumed by the udp instance to do the poll operation. + // + return Ip->Poll (Ip); +}