+/** @file\r
+ Network library.\r
+ \r
+Copyright (c) 2005 - 2009, Intel Corporation.<BR>\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+**/\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/DriverBinding.h>\r
+#include <Protocol/ServiceBinding.h>\r
+#include <Protocol/SimpleNetwork.h>\r
+#include <Protocol/HiiConfigRouting.h>\r
+#include <Protocol/ComponentName.h>\r
+#include <Protocol/ComponentName2.h>\r
+\r
+#include <Guid/NicIp4ConfigNvData.h>\r
+\r
+#include <Library/NetLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/HiiLib.h>\r
+#include <Library/PrintLib.h>\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 mNetLibHexStr[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};\r
+\r
+#define NIC_ITEM_CONFIG_SIZE sizeof (NIC_IP4_CONFIG_INFO) + sizeof (EFI_IP4_ROUTE_TABLE) * MAX_IP4_CONFIG_IN_VARIABLE\r
+\r
+//\r
+// All the supported IP4 maskes in host byte order.\r
+//\r
+IP4_ADDR gIp4AllMasks[IP4_MASK_NUM] = {\r
+ 0x00000000,\r
+ 0x80000000,\r
+ 0xC0000000,\r
+ 0xE0000000,\r
+ 0xF0000000,\r
+ 0xF8000000,\r
+ 0xFC000000,\r
+ 0xFE000000,\r
+\r
+ 0xFF000000,\r
+ 0xFF800000,\r
+ 0xFFC00000,\r
+ 0xFFE00000,\r
+ 0xFFF00000,\r
+ 0xFFF80000,\r
+ 0xFFFC0000,\r
+ 0xFFFE0000,\r
+\r
+ 0xFFFF0000,\r
+ 0xFFFF8000,\r
+ 0xFFFFC000,\r
+ 0xFFFFE000,\r
+ 0xFFFFF000,\r
+ 0xFFFFF800,\r
+ 0xFFFFFC00,\r
+ 0xFFFFFE00,\r
+\r
+ 0xFFFFFF00,\r
+ 0xFFFFFF80,\r
+ 0xFFFFFFC0,\r
+ 0xFFFFFFE0,\r
+ 0xFFFFFFF0,\r
+ 0xFFFFFFF8,\r
+ 0xFFFFFFFC,\r
+ 0xFFFFFFFE,\r
+ 0xFFFFFFFF,\r
+};\r
+\r
+EFI_IPv4_ADDRESS mZeroIp4Addr = {{0, 0, 0, 0}};\r
+\r
+//\r
+// Any error level digitally larger than mNetDebugLevelMax \r
+// will be silently discarded.\r
+//\r
+UINTN mNetDebugLevelMax = NETDEBUG_LEVEL_ERROR;\r
+UINT32 mSyslogPacketSeq = 0xDEADBEEF;\r
+\r
+// \r
+// You can change mSyslogDstMac mSyslogDstIp and mSyslogSrcIp \r
+// here to direct the syslog packets to the syslog deamon. The \r
+// default is broadcast to both the ethernet and IP. \r
+//\r
+UINT8 mSyslogDstMac[NET_ETHER_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};\r
+UINT32 mSyslogDstIp = 0xffffffff;\r
+UINT32 mSyslogSrcIp = 0;\r
+\r
+CHAR8 *\r
+mMonthName[] = {\r
+ "Jan",\r
+ "Feb",\r
+ "Mar",\r
+ "Apr",\r
+ "May",\r
+ "Jun",\r
+ "Jul",\r
+ "Aug",\r
+ "Sep",\r
+ "Oct",\r
+ "Nov",\r
+ "Dec"\r
+};\r
+\r
+/**\r
+ Locate the handles that support SNP, then open one of them \r
+ to send the syslog packets. The caller isn't required to close\r
+ the SNP after use because the SNP is opened by HandleProtocol.\r
+\r
+ @return The point to SNP if one is properly openned. Otherwise NULL\r
+\r
+**/\r
+EFI_SIMPLE_NETWORK_PROTOCOL *\r
+SyslogLocateSnp (\r
+ VOID\r
+ )\r
+{\r
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE *Handles;\r
+ UINTN HandleCount;\r
+ UINTN Index;\r
+\r
+ //\r
+ // Locate the handles which has SNP installed.\r
+ //\r
+ Handles = NULL;\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiSimpleNetworkProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &Handles\r
+ );\r
+\r
+ if (EFI_ERROR (Status) || (HandleCount == 0)) {\r
+ return NULL;\r
+ }\r
+ \r
+ //\r
+ // Try to open one of the ethernet SNP protocol to send packet\r
+ //\r
+ Snp = NULL;\r
+ \r
+ for (Index = 0; Index < HandleCount; Index++) {\r
+ Status = gBS->HandleProtocol (\r
+ Handles[Index],\r
+ &gEfiSimpleNetworkProtocolGuid,\r
+ (VOID **) &Snp\r
+ );\r
+\r
+ if ((Status == EFI_SUCCESS) && (Snp != NULL) && \r
+ (Snp->Mode->IfType == NET_IFTYPE_ETHERNET) &&\r
+ (Snp->Mode->MaxPacketSize >= NET_SYSLOG_PACKET_LEN)) {\r
+ \r
+ break;\r
+ }\r
+\r
+ Snp = NULL;\r
+ }\r
+\r
+ FreePool (Handles);\r
+ return Snp;\r
+}\r
+\r
+/**\r
+ Transmit a syslog packet synchronously through SNP. The Packet\r
+ already has the ethernet header prepended. This function should \r
+ fill in the source MAC because it will try to locate a SNP each\r
+ time it is called to avoid the problem if SNP is unloaded.\r
+ This code snip is copied from MNP. \r
+\r
+ @param[in] Packet - The Syslog packet \r
+ @param[in] Length - The length of the packet\r
+\r
+ @retval EFI_DEVICE_ERROR - Failed to locate a usable SNP protocol\r
+ @retval EFI_TIMEOUT - Timeout happened to send the packet.\r
+ @retval EFI_SUCCESS - Packet is sent.\r
+ \r
+**/\r
+EFI_STATUS\r
+SyslogSendPacket (\r
+ IN CHAR8 *Packet,\r
+ IN UINT32 Length\r
+ )\r
+{\r
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
+ ETHER_HEAD *Ether;\r
+ EFI_STATUS Status;\r
+ EFI_EVENT TimeoutEvent;\r
+ UINT8 *TxBuf;\r
+\r
+ Snp = SyslogLocateSnp ();\r
+\r
+ if (Snp == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Ether = (ETHER_HEAD *) Packet;\r
+ CopyMem (Ether->SrcMac, Snp->Mode->CurrentAddress.Addr, NET_ETHER_ADDR_LEN);\r
+\r
+ //\r
+ // Start the timeout event.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER,\r
+ TPL_NOTIFY,\r
+ NULL,\r
+ NULL,\r
+ &TimeoutEvent\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
+ for (;;) {\r
+ //\r
+ // Transmit the packet through SNP.\r
+ //\r
+ Status = Snp->Transmit (Snp, 0, Length, Packet, NULL, NULL, NULL);\r
+\r
+ if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ break;\r
+ }\r
+ \r
+ //\r
+ // If Status is EFI_SUCCESS, the packet is put in the transmit queue.\r
+ // if Status is EFI_NOT_READY, the transmit engine of the network\r
+ // interface is busy. Both need to sync SNP.\r
+ //\r
+ TxBuf = NULL;\r
+\r
+ do {\r
+ //\r
+ // Get the recycled transmit buffer status.\r
+ //\r
+ Snp->GetStatus (Snp, NULL, (VOID **) &TxBuf);\r
+\r
+ if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
+ Status = EFI_TIMEOUT;\r
+ break;\r
+ }\r
+\r
+ } while (TxBuf == NULL);\r
+\r
+ if ((Status == EFI_SUCCESS) || (Status == EFI_TIMEOUT)) {\r
+ break;\r
+ }\r
+ \r
+ //\r
+ // Status is EFI_NOT_READY. Restart the timer event and\r
+ // call Snp->Transmit again.\r
+ //\r
+ gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT);\r
+ }\r
+\r
+ gBS->SetTimer (TimeoutEvent, TimerCancel, 0);\r
+\r
+ON_EXIT:\r
+ gBS->CloseEvent (TimeoutEvent);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Build a syslog packet, including the Ethernet/Ip/Udp headers \r
+ and user's message. \r
+ \r
+ @param[in] Level - Syslog servity level\r
+ @param[in] Module - The module that generates the log\r
+ @param[in] File - The file that contains the current log\r
+ @param[in] Line - The line of code in the File that contains the current log\r
+ @param[in] Message - The log message\r
+ @param[in] BufLen - The lenght of the Buf\r
+ @param[out] Buf - The buffer to put the packet data\r
+\r
+Returns:\r
+\r
+ The length of the syslog packet built.\r
+\r
+**/\r
+UINT32\r
+SyslogBuildPacket (\r
+ IN UINT32 Level,\r
+ IN UINT8 *Module,\r
+ IN UINT8 *File,\r
+ IN UINT32 Line,\r
+ IN UINT8 *Message,\r
+ IN UINT32 BufLen,\r
+ OUT CHAR8 *Buf \r
+ )\r
+{\r
+ ETHER_HEAD *Ether;\r
+ IP4_HEAD *Ip4;\r
+ EFI_UDP_HEADER *Udp4;\r
+ EFI_TIME Time;\r
+ UINT32 Pri;\r
+ UINT32 Len;\r
+\r
+ //\r
+ // Fill in the Ethernet header. Leave alone the source MAC. \r
+ // SyslogSendPacket will fill in the address for us.\r
+ //\r
+ Ether = (ETHER_HEAD *) Buf;\r
+ CopyMem (Ether->DstMac, mSyslogDstMac, NET_ETHER_ADDR_LEN);\r
+ ZeroMem (Ether->SrcMac, NET_ETHER_ADDR_LEN);\r
+\r
+ Ether->EtherType = HTONS (0x0800); // IPv4 protocol\r
+\r
+ Buf += sizeof (ETHER_HEAD);\r
+ BufLen -= sizeof (ETHER_HEAD);\r
+\r
+ //\r
+ // Fill in the IP header\r
+ //\r
+ Ip4 = (IP4_HEAD *) Buf;\r
+ Ip4->HeadLen = 5;\r
+ Ip4->Ver = 4;\r
+ Ip4->Tos = 0;\r
+ Ip4->TotalLen = 0;\r
+ Ip4->Id = (UINT16) mSyslogPacketSeq;\r
+ Ip4->Fragment = 0;\r
+ Ip4->Ttl = 16;\r
+ Ip4->Protocol = 0x11;\r
+ Ip4->Checksum = 0;\r
+ Ip4->Src = mSyslogSrcIp;\r
+ Ip4->Dst = mSyslogDstIp;\r
+\r
+ Buf += sizeof (IP4_HEAD);\r
+ BufLen -= sizeof (IP4_HEAD);\r
+\r
+ //\r
+ // Fill in the UDP header, Udp checksum is optional. Leave it zero.\r
+ //\r
+ Udp4 = (EFI_UDP_HEADER *) Buf;\r
+ Udp4->SrcPort = HTONS (514);\r
+ Udp4->DstPort = HTONS (514);\r
+ Udp4->Length = 0;\r
+ Udp4->Checksum = 0;\r
+\r
+ Buf += sizeof (EFI_UDP_HEADER);\r
+ BufLen -= sizeof (EFI_UDP_HEADER);\r
+\r
+ //\r
+ // Build the syslog message body with <PRI> Timestamp machine module Message\r
+ //\r
+ Pri = ((NET_SYSLOG_FACILITY & 31) << 3) | (Level & 7);\r
+ gRT->GetTime (&Time, NULL);\r
+\r
+ //\r
+ // Use %a to format the ASCII strings, %s to format UNICODE strings\r
+ //\r
+ Len = 0;\r
+ Len += (UINT32) AsciiSPrint (\r
+ Buf,\r
+ BufLen,\r
+ "<%d> %a %d %d:%d:%d ",\r
+ Pri,\r
+ mMonthName [Time.Month-1], \r
+ Time.Day,\r
+ Time.Hour,\r
+ Time.Minute,\r
+ Time.Second\r
+ );\r
+ Len--;\r
+\r
+ Len += (UINT32) AsciiSPrint (\r
+ Buf + Len, \r
+ BufLen - Len, \r
+ "Tiano %a: %a (Line: %d File: %a)", \r
+ Module,\r
+ Message,\r
+ Line,\r
+ File\r
+ );\r
+ Len--;\r
+\r
+ //\r
+ // OK, patch the IP length/checksum and UDP length fields.\r
+ //\r
+ Len += sizeof (EFI_UDP_HEADER);\r
+ Udp4->Length = HTONS ((UINT16) Len);\r
+\r
+ Len += sizeof (IP4_HEAD);\r
+ Ip4->TotalLen = HTONS ((UINT16) Len);\r
+ Ip4->Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Ip4, sizeof (IP4_HEAD)));\r
+\r
+ return Len + sizeof (ETHER_HEAD);\r
+}\r
+\r
+/**\r
+ Allocate a buffer, then format the message to it. This is a \r
+ help function for the NET_DEBUG_XXX macros. The PrintArg of \r
+ these macros treats the variable length print parameters as a \r
+ single parameter, and pass it to the NetDebugASPrint. For\r
+ example, NET_DEBUG_TRACE ("Tcp", ("State transit to %a\n", Name))\r
+ if extracted to: \r
+ \r
+ NetDebugOutput (\r
+ NETDEBUG_LEVEL_TRACE, \r
+ "Tcp", \r
+ __FILE__,\r
+ __LINE__,\r
+ NetDebugASPrint ("State transit to %a\n", Name) \r
+ ) \r
+ \r
+ @param Format The ASCII format string.\r
+ @param ... The variable length parameter whose format is determined \r
+ by the Format string.\r
+\r
+ @return The buffer containing the formatted message,\r
+ or NULL if failed to allocate memory.\r
+\r
+**/\r
+CHAR8 *\r
+NetDebugASPrint (\r
+ IN CHAR8 *Format,\r
+ ...\r
+ )\r
+{\r
+ VA_LIST Marker;\r
+ CHAR8 *Buf;\r
+\r
+ Buf = (CHAR8 *) AllocatePool (NET_DEBUG_MSG_LEN);\r
+\r
+ if (Buf == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ VA_START (Marker, Format);\r
+ AsciiVSPrint (Buf, NET_DEBUG_MSG_LEN, Format, Marker);\r
+ VA_END (Marker);\r
+\r
+ return Buf;\r
+}\r
+\r
+/**\r
+ Builds an UDP4 syslog packet and send it using SNP.\r
+\r
+ This function will locate a instance of SNP then send the message through it.\r
+ Because it isn't open the SNP BY_DRIVER, apply caution when using it.\r
+\r
+ @param Level The servity level of the message.\r
+ @param Module The Moudle that generates the log.\r
+ @param File The file that contains the log.\r
+ @param Line The exact line that contains the log.\r
+ @param Message The user message to log.\r
+\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet\r
+ @retval EFI_SUCCESS The log is discard because that it is more verbose \r
+ than the mNetDebugLevelMax. Or, it has been sent out.\r
+**/ \r
+EFI_STATUS\r
+NetDebugOutput (\r
+ IN UINT32 Level, \r
+ IN UINT8 *Module,\r
+ IN UINT8 *File,\r
+ IN UINT32 Line,\r
+ IN UINT8 *Message\r
+ )\r
+{\r
+ CHAR8 *Packet;\r
+ UINT32 Len;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Check whether the message should be sent out\r
+ //\r
+ if (Message == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Level > mNetDebugLevelMax) {\r
+ Status = EFI_SUCCESS;\r
+ goto ON_EXIT;\r
+ }\r
+ \r
+ //\r
+ // Allocate a maxium of 1024 bytes, the caller should ensure\r
+ // that the message plus the ethernet/ip/udp header is shorter\r
+ // than this\r
+ //\r
+ Packet = (CHAR8 *) AllocatePool (NET_SYSLOG_PACKET_LEN);\r
+\r
+ if (Packet == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ON_EXIT;\r
+ }\r
+ \r
+ //\r
+ // Build the message: Ethernet header + IP header + Udp Header + user data\r
+ //\r
+ Len = SyslogBuildPacket (\r
+ Level,\r
+ Module,\r
+ File,\r
+ Line,\r
+ Message,\r
+ NET_SYSLOG_PACKET_LEN,\r
+ Packet\r
+ );\r
+\r
+ mSyslogPacketSeq++;\r
+ Status = SyslogSendPacket (Packet, Len);\r
+ FreePool (Packet);\r
+\r
+ON_EXIT:\r
+ FreePool (Message);\r
+ return Status;\r
+}\r
+/**\r
+ Return the length of the mask. \r
+ \r
+ Return the length of the mask, the correct value is from 0 to 32.\r
+ If the mask is invalid, return the invalid length 33, which is IP4_MASK_NUM.\r
+ NetMask is in the host byte order.\r
+\r
+ @param[in] NetMask The netmask to get the length from.\r
+\r
+ @return The length of the netmask, IP4_MASK_NUM if the mask is invalid.\r
+ \r
+**/\r
+INTN\r
+EFIAPI\r
+NetGetMaskLength (\r
+ IN IP4_ADDR NetMask\r
+ )\r
+{\r
+ INTN Index;\r
+\r
+ for (Index = 0; Index < IP4_MASK_NUM; Index++) {\r
+ if (NetMask == gIp4AllMasks[Index]) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ return Index;\r
+}\r
+\r
+\r
+\r
+/**\r
+ Return the class of the IP address, such as class A, B, C.\r
+ Addr is in host byte order.\r
+ \r
+ The address of class A starts with 0.\r
+ If the address belong to class A, return IP4_ADDR_CLASSA.\r
+ The address of class B starts with 10. \r
+ If the address belong to class B, return IP4_ADDR_CLASSB.\r
+ The address of class C starts with 110. \r
+ If the address belong to class C, return IP4_ADDR_CLASSC.\r
+ The address of class D starts with 1110. \r
+ If the address belong to class D, return IP4_ADDR_CLASSD.\r
+ The address of class E starts with 1111.\r
+ If the address belong to class E, return IP4_ADDR_CLASSE.\r
+\r
+ \r
+ @param[in] Addr The address to get the class from.\r
+\r
+ @return IP address class, such as IP4_ADDR_CLASSA.\r
+\r
+**/\r
+INTN\r
+EFIAPI\r
+NetGetIpClass (\r
+ IN IP4_ADDR Addr\r
+ )\r
+{\r
+ UINT8 ByteOne;\r
+\r
+ ByteOne = (UINT8) (Addr >> 24);\r
+\r
+ if ((ByteOne & 0x80) == 0) {\r
+ return IP4_ADDR_CLASSA;\r
+\r
+ } else if ((ByteOne & 0xC0) == 0x80) {\r
+ return IP4_ADDR_CLASSB;\r
+\r
+ } else if ((ByteOne & 0xE0) == 0xC0) {\r
+ return IP4_ADDR_CLASSC;\r
+\r
+ } else if ((ByteOne & 0xF0) == 0xE0) {\r
+ return IP4_ADDR_CLASSD;\r
+\r
+ } else {\r
+ return IP4_ADDR_CLASSE;\r
+\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Check whether the IP is a valid unicast address according to\r
+ the netmask. If NetMask is zero, use the IP address's class to get the default mask.\r
+ \r
+ If Ip is 0, IP is not a valid unicast address.\r
+ Class D address is used for multicasting and class E address is reserved for future. If Ip\r
+ belongs to class D or class E, IP is not a valid unicast address. \r
+ If all bits of the host address of IP are 0 or 1, IP is also not a valid unicast address.\r
+\r
+ @param[in] Ip The IP to check against.\r
+ @param[in] NetMask The mask of the IP.\r
+\r
+ @return TRUE if IP is a valid unicast address on the network, otherwise FALSE.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+NetIp4IsUnicast (\r
+ IN IP4_ADDR Ip,\r
+ IN IP4_ADDR NetMask\r
+ )\r
+{\r
+ INTN Class;\r
+\r
+ Class = NetGetIpClass (Ip);\r
+\r
+ if ((Ip == 0) || (Class >= IP4_ADDR_CLASSD)) {\r
+ return FALSE;\r
+ }\r
+\r
+ if (NetMask == 0) {\r
+ NetMask = gIp4AllMasks[Class << 3];\r
+ }\r
+\r
+ if (((Ip &~NetMask) == ~NetMask) || ((Ip &~NetMask) == 0)) {\r
+ return FALSE;\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Check whether the incoming IPv6 address is a valid unicast address.\r
+\r
+ If the address is a multicast address has binary 0xFF at the start, it is not\r
+ a valid unicast address. If the address is unspecified ::, it is not a valid\r
+ unicast address to be assigned to any node. If the address is loopback address\r
+ ::1, it is also not a valid unicast address to be assigned to any physical\r
+ interface. \r
+\r
+ @param[in] Ip6 The IPv6 address to check against.\r
+\r
+ @return TRUE if Ip6 is a valid unicast address on the network, otherwise FALSE.\r
+\r
+**/ \r
+BOOLEAN\r
+NetIp6IsValidUnicast (\r
+ IN EFI_IPv6_ADDRESS *Ip6\r
+ ) \r
+{\r
+ UINT8 Byte;\r
+ UINT8 Index;\r
+ \r
+ if (Ip6->Addr[0] == 0xFF) {\r
+ return FALSE;\r
+ }\r
+\r
+ for (Index = 0; Index < 15; Index++) {\r
+ if (Ip6->Addr[Index] != 0) {\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ Byte = Ip6->Addr[Index];\r
+\r
+ if (Byte == 0x0 || Byte == 0x1) {\r
+ return FALSE;\r
+ }\r
+\r
+ return TRUE; \r
+}\r
+\r
+/**\r
+ Check whether the incoming Ipv6 address is the unspecified address or not.\r
+\r
+ @param[in] Ip6 - Ip6 address, in network order.\r
+\r
+ @retval TRUE - Yes, unspecified\r
+ @retval FALSE - No\r
+ \r
+**/\r
+BOOLEAN\r
+NetIp6IsUnspecifiedAddr (\r
+ IN EFI_IPv6_ADDRESS *Ip6\r
+ )\r
+{\r
+ UINT8 Index;\r
+\r
+ for (Index = 0; Index < 16; Index++) {\r
+ if (Ip6->Addr[Index] != 0) {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Check whether the incoming Ipv6 address is a link-local address.\r
+\r
+ @param[in] Ip6 - Ip6 address, in network order.\r
+\r
+ @retval TRUE - Yes, link-local address\r
+ @retval FALSE - No\r
+ \r
+**/\r
+BOOLEAN\r
+NetIp6IsLinkLocalAddr (\r
+ IN EFI_IPv6_ADDRESS *Ip6\r
+ )\r
+{\r
+ UINT8 Index;\r
+ \r
+ ASSERT (Ip6 != NULL);\r
+\r
+ if (Ip6->Addr[0] != 0xFE) {\r
+ return FALSE;\r
+ }\r
+ \r
+ if (Ip6->Addr[1] != 0x80) {\r
+ return FALSE;\r
+ }\r
+\r
+ for (Index = 2; Index < 8; Index++) {\r
+ if (Ip6->Addr[Index] != 0) {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Check whether the Ipv6 address1 and address2 are on the connected network.\r
+\r
+ @param[in] Ip1 - Ip6 address1, in network order.\r
+ @param[in] Ip2 - Ip6 address2, in network order.\r
+ @param[in] PrefixLength - The prefix length of the checking net.\r
+\r
+ @retval TRUE - Yes, connected.\r
+ @retval FALSE - No.\r
+ \r
+**/\r
+BOOLEAN\r
+NetIp6IsNetEqual (\r
+ EFI_IPv6_ADDRESS *Ip1,\r
+ EFI_IPv6_ADDRESS *Ip2,\r
+ UINT8 PrefixLength\r
+ )\r
+{\r
+ UINT8 Byte;\r
+ UINT8 Bit;\r
+ UINT8 Mask;\r
+\r
+ ASSERT (Ip1 != NULL && Ip2 != NULL);\r
+ \r
+ if (PrefixLength == 0) {\r
+ return TRUE;\r
+ }\r
+\r
+ Byte = (UINT8) (PrefixLength / 8);\r
+ Bit = (UINT8) (PrefixLength % 8);\r
+ \r
+ if (CompareMem (Ip1, Ip2, Byte) != 0) {\r
+ return FALSE;\r
+ }\r
+\r
+ if (Bit > 0) {\r
+ Mask = (UINT8) (0xFF << (8 - Bit));\r
+\r
+ if ((Ip1->Addr[Byte] & Mask) != (Ip2->Addr[Byte] & Mask)) {\r
+ return FALSE;\r
+ } \r
+ }\r
+ \r
+ return TRUE;\r
+}\r
+\r
+\r
+/**\r
+ Switches the endianess of an IPv6 address\r
+\r
+ This function swaps the bytes in a 128-bit IPv6 address to switch the value\r
+ from little endian to big endian or vice versa. The byte swapped value is\r
+ returned.\r
+\r
+ @param Ip6 Points to an IPv6 address\r
+\r
+ @return The byte swapped IPv6 address.\r
+\r
+**/\r
+EFI_IPv6_ADDRESS *\r
+Ip6Swap128 (\r
+ EFI_IPv6_ADDRESS *Ip6\r
+ )\r
+{\r
+ UINT64 High;\r
+ UINT64 Low;\r
+\r
+ CopyMem (&High, Ip6, sizeof (UINT64));\r
+ CopyMem (&Low, &Ip6->Addr[8], sizeof (UINT64));\r
+\r
+ High = SwapBytes64 (High);\r
+ Low = SwapBytes64 (Low);\r
+\r
+ CopyMem (Ip6, &Low, sizeof (UINT64));\r
+ CopyMem (&Ip6->Addr[8], &High, sizeof (UINT64));\r
+\r
+ return Ip6;\r
+}\r
+\r
+/**\r
+ Initialize a random seed using current time.\r
+ \r
+ Get current time first. Then initialize a random seed based on some basic \r
+ mathematics operation on the hour, day, minute, second, nanosecond and year \r
+ of the current time.\r
+ \r
+ @return The random seed initialized with current time.\r
+\r
+**/\r
+UINT32\r
+EFIAPI\r
+NetRandomInitSeed (\r
+ VOID\r
+ )\r
+{\r
+ EFI_TIME Time;\r
+ UINT32 Seed;\r
+\r
+ gRT->GetTime (&Time, NULL);\r
+ Seed = (~Time.Hour << 24 | Time.Day << 16 | Time.Minute << 8 | Time.Second);\r
+ Seed ^= Time.Nanosecond;\r
+ Seed ^= Time.Year << 7;\r
+\r
+ return Seed;\r
+}\r
+\r
+\r
+/**\r
+ Extract a UINT32 from a byte stream.\r
+ \r
+ Copy a UINT32 from a byte stream, then converts it from Network \r
+ byte order to host byte order. Use this function to avoid alignment error.\r
+\r
+ @param[in] Buf The buffer to extract the UINT32.\r
+\r
+ @return The UINT32 extracted.\r
+\r
+**/\r
+UINT32\r
+EFIAPI\r
+NetGetUint32 (\r
+ IN UINT8 *Buf\r
+ )\r
+{\r
+ UINT32 Value;\r
+\r
+ CopyMem (&Value, Buf, sizeof (UINT32));\r
+ return NTOHL (Value);\r
+}\r
+\r
+\r
+/**\r
+ Put a UINT32 to the byte stream in network byte order. \r
+ \r
+ Converts a UINT32 from host byte order to network byte order. Then copy it to the \r
+ byte stream.\r
+\r
+ @param[in, out] Buf The buffer to put the UINT32.\r
+ @param[in] Data The data to put.\r
+ \r
+**/\r
+VOID\r
+EFIAPI\r
+NetPutUint32 (\r
+ IN OUT UINT8 *Buf,\r
+ IN UINT32 Data\r
+ )\r
+{\r
+ Data = HTONL (Data);\r
+ CopyMem (Buf, &Data, sizeof (UINT32));\r
+}\r
+\r
+\r
+/**\r
+ Remove the first node entry on the list, and return the removed node entry.\r
+ \r
+ Removes the first node Entry from a doubly linked list. It is up to the caller of\r
+ this function to release the memory used by the first node if that is required. On\r
+ exit, the removed node is returned. \r
+\r
+ If Head is NULL, then ASSERT().\r
+ If Head was not initialized, then ASSERT().\r
+ If PcdMaximumLinkedListLength is not zero, and the number of nodes in the\r
+ linked list including the head node is greater than or equal to PcdMaximumLinkedListLength,\r
+ then ASSERT(). \r
+\r
+ @param[in, out] Head The list header.\r
+\r
+ @return The first node entry that is removed from the list, NULL if the list is empty.\r
+\r
+**/\r
+LIST_ENTRY *\r
+EFIAPI\r
+NetListRemoveHead (\r
+ IN OUT LIST_ENTRY *Head\r
+ )\r
+{\r
+ LIST_ENTRY *First;\r
+\r
+ ASSERT (Head != NULL);\r
+\r
+ if (IsListEmpty (Head)) {\r
+ return NULL;\r
+ }\r
+\r
+ First = Head->ForwardLink;\r
+ Head->ForwardLink = First->ForwardLink;\r
+ First->ForwardLink->BackLink = Head;\r
+\r
+ DEBUG_CODE (\r
+ First->ForwardLink = (LIST_ENTRY *) NULL;\r
+ First->BackLink = (LIST_ENTRY *) NULL;\r
+ );\r
+\r
+ return First;\r
+}\r
+\r
+\r
+/**\r
+ Remove the last node entry on the list and and return the removed node entry.\r
+\r
+ Removes the last node entry from a doubly linked list. It is up to the caller of\r
+ this function to release the memory used by the first node if that is required. On\r
+ exit, the removed node is returned. \r
+\r
+ If Head is NULL, then ASSERT().\r
+ If Head was not initialized, then ASSERT().\r
+ If PcdMaximumLinkedListLength is not zero, and the number of nodes in the\r
+ linked list including the head node is greater than or equal to PcdMaximumLinkedListLength,\r
+ then ASSERT(). \r
+ \r
+ @param[in, out] Head The list head.\r
+\r
+ @return The last node entry that is removed from the list, NULL if the list is empty.\r
+\r
+**/\r
+LIST_ENTRY *\r
+EFIAPI\r
+NetListRemoveTail (\r
+ IN OUT LIST_ENTRY *Head\r
+ )\r
+{\r
+ LIST_ENTRY *Last;\r
+\r
+ ASSERT (Head != NULL);\r
+\r
+ if (IsListEmpty (Head)) {\r
+ return NULL;\r
+ }\r
+\r
+ Last = Head->BackLink;\r
+ Head->BackLink = Last->BackLink;\r
+ Last->BackLink->ForwardLink = Head;\r
+\r
+ DEBUG_CODE (\r
+ Last->ForwardLink = (LIST_ENTRY *) NULL;\r
+ Last->BackLink = (LIST_ENTRY *) NULL;\r
+ );\r
+\r
+ return Last;\r
+}\r
+\r
+\r
+/**\r
+ Insert a new node entry after a designated node entry of a doubly linked list.\r
+ \r
+ Inserts a new node entry donated by NewEntry after the node entry donated by PrevEntry\r
+ of the doubly linked list.\r
+ \r
+ @param[in, out] PrevEntry The previous entry to insert after.\r
+ @param[in, out] NewEntry The new entry to insert.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+NetListInsertAfter (\r
+ IN OUT LIST_ENTRY *PrevEntry,\r
+ IN OUT LIST_ENTRY *NewEntry\r
+ )\r
+{\r
+ NewEntry->BackLink = PrevEntry;\r
+ NewEntry->ForwardLink = PrevEntry->ForwardLink;\r
+ PrevEntry->ForwardLink->BackLink = NewEntry;\r
+ PrevEntry->ForwardLink = NewEntry;\r
+}\r
+\r
+\r
+/**\r
+ Insert a new node entry before a designated node entry of a doubly linked list.\r
+ \r
+ Inserts a new node entry donated by NewEntry after the node entry donated by PostEntry\r
+ of the doubly linked list.\r
+ \r
+ @param[in, out] PostEntry The entry to insert before.\r
+ @param[in, out] NewEntry The new entry to insert.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+NetListInsertBefore (\r
+ IN OUT LIST_ENTRY *PostEntry,\r
+ IN OUT LIST_ENTRY *NewEntry\r
+ )\r
+{\r
+ NewEntry->ForwardLink = PostEntry;\r
+ NewEntry->BackLink = PostEntry->BackLink;\r
+ PostEntry->BackLink->ForwardLink = NewEntry;\r
+ PostEntry->BackLink = NewEntry;\r
+}\r
+\r
+\r
+/**\r
+ Initialize the netmap. Netmap is a reposity to keep the <Key, Value> pairs.\r
+ \r
+ Initialize the forward and backward links of two head nodes donated by Map->Used \r
+ and Map->Recycled of two doubly linked lists.\r
+ Initializes the count of the <Key, Value> pairs in the netmap to zero.\r
+ \r
+ If Map is NULL, then ASSERT().\r
+ If the address of Map->Used is NULL, then ASSERT().\r
+ If the address of Map->Recycled is NULl, then ASSERT().\r
+ \r
+ @param[in, out] Map The netmap to initialize.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+NetMapInit (\r
+ IN OUT NET_MAP *Map\r
+ )\r
+{\r
+ ASSERT (Map != NULL);\r
+\r
+ InitializeListHead (&Map->Used);\r
+ InitializeListHead (&Map->Recycled);\r
+ Map->Count = 0;\r
+}\r
+\r
+\r
+/**\r
+ To clean up the netmap, that is, release allocated memories.\r
+ \r
+ Removes all nodes of the Used doubly linked list and free memory of all related netmap items.\r
+ Removes all nodes of the Recycled doubly linked list and free memory of all related netmap items.\r
+ The number of the <Key, Value> pairs in the netmap is set to be zero.\r
+ \r
+ If Map is NULL, then ASSERT().\r
+ \r
+ @param[in, out] Map The netmap to clean up.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+NetMapClean (\r
+ IN OUT NET_MAP *Map\r
+ )\r
+{\r
+ NET_MAP_ITEM *Item;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+\r
+ ASSERT (Map != NULL);\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Map->Used) {\r
+ Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);\r
+\r
+ RemoveEntryList (&Item->Link);\r
+ Map->Count--;\r
+\r
+ gBS->FreePool (Item);\r
+ }\r
+\r
+ ASSERT ((Map->Count == 0) && IsListEmpty (&Map->Used));\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Map->Recycled) {\r
+ Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);\r
+\r
+ RemoveEntryList (&Item->Link);\r
+ gBS->FreePool (Item);\r
+ }\r
+\r
+ ASSERT (IsListEmpty (&Map->Recycled));\r
+}\r
+\r
+\r
+/**\r
+ Test whether the netmap is empty and return true if it is.\r
+ \r
+ If the number of the <Key, Value> pairs in the netmap is zero, return TRUE.\r
+ \r
+ If Map is NULL, then ASSERT().\r
+ \r
+ \r
+ @param[in] Map The net map to test.\r
+\r
+ @return TRUE if the netmap is empty, otherwise FALSE.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+NetMapIsEmpty (\r
+ IN NET_MAP *Map\r
+ )\r
+{\r
+ ASSERT (Map != NULL);\r
+ return (BOOLEAN) (Map->Count == 0);\r
+}\r
+\r
+\r
+/**\r
+ Return the number of the <Key, Value> pairs in the netmap.\r
+\r
+ @param[in] Map The netmap to get the entry number.\r
+\r
+ @return The entry number in the netmap.\r
+\r
+**/\r
+UINTN\r
+EFIAPI\r
+NetMapGetCount (\r
+ IN NET_MAP *Map\r
+ )\r
+{\r
+ return Map->Count;\r
+}\r
+\r
+\r
+/**\r
+ Return one allocated item. \r
+ \r
+ If the Recycled doubly linked list of the netmap is empty, it will try to allocate \r
+ a batch of items if there are enough resources and add corresponding nodes to the begining\r
+ of the Recycled doubly linked list of the netmap. Otherwise, it will directly remove\r
+ the fist node entry of the Recycled doubly linked list and return the corresponding item.\r
+ \r
+ If Map is NULL, then ASSERT().\r
+ \r
+ @param[in, out] Map The netmap to allocate item for.\r
+\r
+ @return The allocated item. If NULL, the\r
+ allocation failed due to resource limit.\r
+\r
+**/\r
+NET_MAP_ITEM *\r
+NetMapAllocItem (\r
+ IN OUT NET_MAP *Map\r
+ )\r
+{\r
+ NET_MAP_ITEM *Item;\r
+ LIST_ENTRY *Head;\r
+ UINTN Index;\r
+\r
+ ASSERT (Map != NULL);\r
+\r
+ Head = &Map->Recycled;\r
+\r
+ if (IsListEmpty (Head)) {\r
+ for (Index = 0; Index < NET_MAP_INCREAMENT; Index++) {\r
+ Item = AllocatePool (sizeof (NET_MAP_ITEM));\r
+\r
+ if (Item == NULL) {\r
+ if (Index == 0) {\r
+ return NULL;\r
+ }\r
+\r
+ break;\r
+ }\r
+\r
+ InsertHeadList (Head, &Item->Link);\r
+ }\r
+ }\r
+\r
+ Item = NET_LIST_HEAD (Head, NET_MAP_ITEM, Link);\r
+ NetListRemoveHead (Head);\r
+\r
+ return Item;\r
+}\r
+\r
+\r
+/**\r
+ Allocate an item to save the <Key, Value> pair to the head of the netmap.\r
+ \r
+ Allocate an item to save the <Key, Value> pair and add corresponding node entry\r
+ to the beginning of the Used doubly linked list. The number of the <Key, Value> \r
+ pairs in the netmap increase by 1.\r
+\r
+ If Map is NULL, then ASSERT().\r
+ \r
+ @param[in, out] Map The netmap to insert into.\r
+ @param[in] Key The user's key.\r
+ @param[in] Value The user's value for the key.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the item.\r
+ @retval EFI_SUCCESS The item is inserted to the head.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NetMapInsertHead (\r
+ IN OUT NET_MAP *Map,\r
+ IN VOID *Key,\r
+ IN VOID *Value OPTIONAL\r
+ )\r
+{\r
+ NET_MAP_ITEM *Item;\r
+\r
+ ASSERT (Map != NULL);\r
+\r
+ Item = NetMapAllocItem (Map);\r
+\r
+ if (Item == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Item->Key = Key;\r
+ Item->Value = Value;\r
+ InsertHeadList (&Map->Used, &Item->Link);\r
+\r
+ Map->Count++;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Allocate an item to save the <Key, Value> pair to the tail of the netmap.\r
+\r
+ Allocate an item to save the <Key, Value> pair and add corresponding node entry\r
+ to the tail of the Used doubly linked list. The number of the <Key, Value> \r
+ pairs in the netmap increase by 1.\r
+\r
+ If Map is NULL, then ASSERT().\r
+ \r
+ @param[in, out] Map The netmap to insert into.\r
+ @param[in] Key The user's key.\r
+ @param[in] Value The user's value for the key.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the item.\r
+ @retval EFI_SUCCESS The item is inserted to the tail.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NetMapInsertTail (\r
+ IN OUT NET_MAP *Map,\r
+ IN VOID *Key,\r
+ IN VOID *Value OPTIONAL\r
+ )\r
+{\r
+ NET_MAP_ITEM *Item;\r
+\r
+ ASSERT (Map != NULL);\r
+\r
+ Item = NetMapAllocItem (Map);\r
+\r
+ if (Item == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Item->Key = Key;\r
+ Item->Value = Value;\r
+ InsertTailList (&Map->Used, &Item->Link);\r
+\r
+ Map->Count++;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Check whether the item is in the Map and return TRUE if it is.\r
+\r
+ @param[in] Map The netmap to search within.\r
+ @param[in] Item The item to search.\r
+\r
+ @return TRUE if the item is in the netmap, otherwise FALSE.\r
+\r
+**/\r
+BOOLEAN\r
+NetItemInMap (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_ITEM *Item\r
+ )\r
+{\r
+ LIST_ENTRY *ListEntry;\r
+\r
+ NET_LIST_FOR_EACH (ListEntry, &Map->Used) {\r
+ if (ListEntry == &Item->Link) {\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+\r
+/**\r
+ Find the key in the netmap and returns the point to the item contains the Key.\r
+ \r
+ Iterate the Used doubly linked list of the netmap to get every item. Compare the key of every \r
+ item with the key to search. It returns the point to the item contains the Key if found.\r
+\r
+ If Map is NULL, then ASSERT().\r
+ \r
+ @param[in] Map The netmap to search within.\r
+ @param[in] Key The key to search.\r
+\r
+ @return The point to the item contains the Key, or NULL if Key isn't in the map.\r
+\r
+**/\r
+NET_MAP_ITEM *\r
+EFIAPI\r
+NetMapFindKey (\r
+ IN NET_MAP *Map,\r
+ IN VOID *Key\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ NET_MAP_ITEM *Item;\r
+\r
+ ASSERT (Map != NULL);\r
+\r
+ NET_LIST_FOR_EACH (Entry, &Map->Used) {\r
+ Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);\r
+\r
+ if (Item->Key == Key) {\r
+ return Item;\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+\r
+/**\r
+ Remove the node entry of the item from the netmap and return the key of the removed item.\r
+ \r
+ Remove the node entry of the item from the Used doubly linked list of the netmap. \r
+ The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node \r
+ entry of the item to the Recycled doubly linked list of the netmap. If Value is not NULL,\r
+ Value will point to the value of the item. It returns the key of the removed item.\r
+ \r
+ If Map is NULL, then ASSERT().\r
+ If Item is NULL, then ASSERT().\r
+ if item in not in the netmap, then ASSERT().\r
+ \r
+ @param[in, out] Map The netmap to remove the item from.\r
+ @param[in, out] Item The item to remove.\r
+ @param[out] Value The variable to receive the value if not NULL.\r
+\r
+ @return The key of the removed item.\r
+\r
+**/\r
+VOID *\r
+EFIAPI\r
+NetMapRemoveItem (\r
+ IN OUT NET_MAP *Map,\r
+ IN OUT NET_MAP_ITEM *Item,\r
+ OUT VOID **Value OPTIONAL\r
+ )\r
+{\r
+ ASSERT ((Map != NULL) && (Item != NULL));\r
+ ASSERT (NetItemInMap (Map, Item));\r
+\r
+ RemoveEntryList (&Item->Link);\r
+ Map->Count--;\r
+ InsertHeadList (&Map->Recycled, &Item->Link);\r
+\r
+ if (Value != NULL) {\r
+ *Value = Item->Value;\r
+ }\r
+\r
+ return Item->Key;\r
+}\r
+\r
+\r
+/**\r
+ Remove the first node entry on the netmap and return the key of the removed item.\r
+\r
+ Remove the first node entry from the Used doubly linked list of the netmap. \r
+ The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node \r
+ entry to the Recycled doubly linked list of the netmap. If parameter Value is not NULL,\r
+ parameter Value will point to the value of the item. It returns the key of the removed item.\r
+ \r
+ If Map is NULL, then ASSERT().\r
+ If the Used doubly linked list is empty, then ASSERT().\r
+ \r
+ @param[in, out] Map The netmap to remove the head from.\r
+ @param[out] Value The variable to receive the value if not NULL.\r
+\r
+ @return The key of the item removed.\r
+\r
+**/\r
+VOID *\r
+EFIAPI\r
+NetMapRemoveHead (\r
+ IN OUT NET_MAP *Map,\r
+ OUT VOID **Value OPTIONAL\r
+ )\r
+{\r
+ NET_MAP_ITEM *Item;\r
+\r
+ //\r
+ // Often, it indicates a programming error to remove\r
+ // the first entry in an empty list\r
+ //\r
+ ASSERT (Map && !IsListEmpty (&Map->Used));\r
+\r
+ Item = NET_LIST_HEAD (&Map->Used, NET_MAP_ITEM, Link);\r
+ RemoveEntryList (&Item->Link);\r
+ Map->Count--;\r
+ InsertHeadList (&Map->Recycled, &Item->Link);\r
+\r
+ if (Value != NULL) {\r
+ *Value = Item->Value;\r
+ }\r
+\r
+ return Item->Key;\r
+}\r
+\r
+\r
+/**\r
+ Remove the last node entry on the netmap and return the key of the removed item.\r
+\r
+ Remove the last node entry from the Used doubly linked list of the netmap. \r
+ The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node \r
+ entry to the Recycled doubly linked list of the netmap. If parameter Value is not NULL,\r
+ parameter Value will point to the value of the item. It returns the key of the removed item.\r
+ \r
+ If Map is NULL, then ASSERT().\r
+ If the Used doubly linked list is empty, then ASSERT().\r
+ \r
+ @param[in, out] Map The netmap to remove the tail from.\r
+ @param[out] Value The variable to receive the value if not NULL.\r
+\r
+ @return The key of the item removed.\r
+\r
+**/\r
+VOID *\r
+EFIAPI\r
+NetMapRemoveTail (\r
+ IN OUT NET_MAP *Map,\r
+ OUT VOID **Value OPTIONAL\r
+ )\r
+{\r
+ NET_MAP_ITEM *Item;\r
+\r
+ //\r
+ // Often, it indicates a programming error to remove\r
+ // the last entry in an empty list\r
+ //\r
+ ASSERT (Map && !IsListEmpty (&Map->Used));\r
+\r
+ Item = NET_LIST_TAIL (&Map->Used, NET_MAP_ITEM, Link);\r
+ RemoveEntryList (&Item->Link);\r
+ Map->Count--;\r
+ InsertHeadList (&Map->Recycled, &Item->Link);\r
+\r
+ if (Value != NULL) {\r
+ *Value = Item->Value;\r
+ }\r
+\r
+ return Item->Key;\r
+}\r
+\r
+\r
+/**\r
+ Iterate through the netmap and call CallBack for each item.\r
+ \r
+ It will contiue the traverse if CallBack returns EFI_SUCCESS, otherwise, break\r
+ from the loop. It returns the CallBack's last return value. This function is \r
+ delete safe for the current item.\r
+\r
+ If Map is NULL, then ASSERT().\r
+ If CallBack is NULL, then ASSERT().\r
+ \r
+ @param[in] Map The Map to iterate through.\r
+ @param[in] CallBack The callback function to call for each item.\r
+ @param[in] Arg The opaque parameter to the callback.\r
+\r
+ @retval EFI_SUCCESS There is no item in the netmap or CallBack for each item\r
+ return EFI_SUCCESS.\r
+ @retval Others It returns the CallBack's last return value.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NetMapIterate (\r
+ IN NET_MAP *Map,\r
+ IN NET_MAP_CALLBACK CallBack,\r
+ IN VOID *Arg\r
+ )\r
+{\r
+\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Next;\r
+ LIST_ENTRY *Head;\r
+ NET_MAP_ITEM *Item;\r
+ EFI_STATUS Result;\r
+\r
+ ASSERT ((Map != NULL) && (CallBack != NULL));\r
+\r
+ Head = &Map->Used;\r
+\r
+ if (IsListEmpty (Head)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {\r
+ Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);\r
+ Result = CallBack (Map, Item, Arg);\r
+\r
+ if (EFI_ERROR (Result)) {\r
+ return Result;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This is the default unload handle for all the network drivers.\r
+\r
+ Disconnect the driver specified by ImageHandle from all the devices in the handle database.\r
+ Uninstall all the protocols installed in the driver entry point.\r
+ \r
+ @param[in] ImageHandle The drivers' driver image.\r
+\r
+ @retval EFI_SUCCESS The image is unloaded.\r
+ @retval Others Failed to unload the image.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NetLibDefaultUnload (\r
+ IN EFI_HANDLE ImageHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE *DeviceHandleBuffer;\r
+ UINTN DeviceHandleCount;\r
+ UINTN Index;\r
+ EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;\r
+ EFI_COMPONENT_NAME_PROTOCOL *ComponentName;\r
+ EFI_COMPONENT_NAME2_PROTOCOL *ComponentName2;\r
+\r
+ //\r
+ // Get the list of all the handles in the handle database.\r
+ // If there is an error getting the list, then the unload\r
+ // operation fails.\r
+ //\r
+ Status = gBS->LocateHandleBuffer (\r
+ AllHandles,\r
+ NULL,\r
+ NULL,\r
+ &DeviceHandleCount,\r
+ &DeviceHandleBuffer\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Disconnect the driver specified by ImageHandle from all\r
+ // the devices in the handle database.\r
+ //\r
+ for (Index = 0; Index < DeviceHandleCount; Index++) {\r
+ Status = gBS->DisconnectController (\r
+ DeviceHandleBuffer[Index],\r
+ ImageHandle,\r
+ NULL\r
+ );\r
+ }\r
+\r
+ //\r
+ // Uninstall all the protocols installed in the driver entry point\r
+ //\r
+ for (Index = 0; Index < DeviceHandleCount; Index++) {\r
+ Status = gBS->HandleProtocol (\r
+ DeviceHandleBuffer[Index],\r
+ &gEfiDriverBindingProtocolGuid,\r
+ (VOID **) &DriverBinding\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
+\r
+ if (DriverBinding->ImageHandle != ImageHandle) {\r
+ continue;\r
+ }\r
+\r
+ gBS->UninstallProtocolInterface (\r
+ ImageHandle,\r
+ &gEfiDriverBindingProtocolGuid,\r
+ DriverBinding\r
+ );\r
+ Status = gBS->HandleProtocol (\r
+ DeviceHandleBuffer[Index],\r
+ &gEfiComponentNameProtocolGuid,\r
+ (VOID **) &ComponentName\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ gBS->UninstallProtocolInterface (\r
+ ImageHandle,\r
+ &gEfiComponentNameProtocolGuid,\r
+ ComponentName\r
+ );\r
+ }\r
+\r
+ Status = gBS->HandleProtocol (\r
+ DeviceHandleBuffer[Index],\r
+ &gEfiComponentName2ProtocolGuid,\r
+ (VOID **) &ComponentName2\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ gBS->UninstallProtocolInterface (\r
+ ImageHandle,\r
+ &gEfiComponentName2ProtocolGuid,\r
+ ComponentName2\r
+ );\r
+ }\r
+ }\r
+\r
+ //\r
+ // Free the buffer containing the list of handles from the handle database\r
+ //\r
+ if (DeviceHandleBuffer != NULL) {\r
+ gBS->FreePool (DeviceHandleBuffer);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+\r
+/**\r
+ Create a child of the service that is identified by ServiceBindingGuid.\r
+ \r
+ Get the ServiceBinding Protocol first, then use it to create a child.\r
+\r
+ If ServiceBindingGuid is NULL, then ASSERT().\r
+ If ChildHandle is NULL, then ASSERT().\r
+ \r
+ @param[in] Controller The controller which has the service installed.\r
+ @param[in] Image The image handle used to open service.\r
+ @param[in] ServiceBindingGuid The service's Guid.\r
+ @param[in, out] ChildHandle The handle to receive the create child.\r
+\r
+ @retval EFI_SUCCESS The child is successfully created.\r
+ @retval Others Failed to create the child.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NetLibCreateServiceChild (\r
+ IN EFI_HANDLE Controller,\r
+ IN EFI_HANDLE Image,\r
+ IN EFI_GUID *ServiceBindingGuid,\r
+ IN OUT EFI_HANDLE *ChildHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_SERVICE_BINDING_PROTOCOL *Service;\r
+\r
+\r
+ ASSERT ((ServiceBindingGuid != NULL) && (ChildHandle != NULL));\r
+\r
+ //\r
+ // Get the ServiceBinding Protocol\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ Controller,\r
+ ServiceBindingGuid,\r
+ (VOID **) &Service,\r
+ Image,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Create a child\r
+ //\r
+ Status = Service->CreateChild (Service, ChildHandle);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Destory a child of the service that is identified by ServiceBindingGuid.\r
+ \r
+ Get the ServiceBinding Protocol first, then use it to destroy a child.\r
+ \r
+ If ServiceBindingGuid is NULL, then ASSERT().\r
+ \r
+ @param[in] Controller The controller which has the service installed.\r
+ @param[in] Image The image handle used to open service.\r
+ @param[in] ServiceBindingGuid The service's Guid.\r
+ @param[in] ChildHandle The child to destory.\r
+\r
+ @retval EFI_SUCCESS The child is successfully destoried.\r
+ @retval Others Failed to destory the child.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NetLibDestroyServiceChild (\r
+ IN EFI_HANDLE Controller,\r
+ IN EFI_HANDLE Image,\r
+ IN EFI_GUID *ServiceBindingGuid,\r
+ IN EFI_HANDLE ChildHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_SERVICE_BINDING_PROTOCOL *Service;\r
+\r
+ ASSERT (ServiceBindingGuid != NULL);\r
+\r
+ //\r
+ // Get the ServiceBinding Protocol\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ Controller,\r
+ ServiceBindingGuid,\r
+ (VOID **) &Service,\r
+ Image,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // destory the child\r
+ //\r
+ Status = Service->DestroyChild (Service, ChildHandle);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Convert the mac address of the simple network protocol installed on\r
+ SnpHandle to a unicode string. Callers are responsible for freeing the\r
+ string storage.\r
+\r
+ Get the mac address of the Simple Network protocol from the SnpHandle. Then convert\r
+ the mac address into a unicode string. It takes 2 unicode characters to represent \r
+ a 1 byte binary buffer. Plus one unicode character for the null-terminator.\r
+\r
+\r
+ @param[in] SnpHandle The handle where the simple network protocol is\r
+ installed on.\r
+ @param[in] ImageHandle The image handle used to act as the agent handle to\r
+ get the simple network protocol.\r
+ @param[out] MacString The pointer to store the address of the string\r
+ representation of the mac address.\r
+ \r
+ @retval EFI_SUCCESS Convert the mac address a unicode string successfully.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough memory resource.\r
+ @retval Others Failed to open the simple network protocol.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NetLibGetMacString (\r
+ IN EFI_HANDLE SnpHandle,\r
+ IN EFI_HANDLE ImageHandle,\r
+ OUT CHAR16 **MacString\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
+ EFI_SIMPLE_NETWORK_MODE *Mode;\r
+ CHAR16 *MacAddress;\r
+ UINTN Index;\r
+\r
+ *MacString = NULL;\r
+\r
+ //\r
+ // Get the Simple Network protocol from the SnpHandle.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ SnpHandle,\r
+ &gEfiSimpleNetworkProtocolGuid,\r
+ (VOID **) &Snp,\r
+ ImageHandle,\r
+ SnpHandle,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Mode = Snp->Mode;\r
+\r
+ //\r
+ // It takes 2 unicode characters to represent a 1 byte binary buffer.\r
+ // Plus one unicode character for the null-terminator.\r
+ //\r
+ MacAddress = AllocatePool ((2 * Mode->HwAddressSize + 1) * sizeof (CHAR16));\r
+ if (MacAddress == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Convert the mac address into a unicode string.\r
+ //\r
+ for (Index = 0; Index < Mode->HwAddressSize; Index++) {\r
+ MacAddress[Index * 2] = (CHAR16) mNetLibHexStr[(Mode->CurrentAddress.Addr[Index] >> 4) & 0x0F];\r
+ MacAddress[Index * 2 + 1] = (CHAR16) mNetLibHexStr[Mode->CurrentAddress.Addr[Index] & 0x0F];\r
+ }\r
+\r
+ MacAddress[Mode->HwAddressSize * 2] = L'\0';\r
+\r
+ *MacString = MacAddress;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r