summaryrefslogtreecommitdiffstats
path: root/NetworkPkg/Library/DxeNetLib
diff options
context:
space:
mode:
authorLiming Gao <liming.gao@intel.com>2019-05-15 20:02:18 +0800
committerLiming Gao <liming.gao@intel.com>2019-05-27 09:25:18 +0800
commit4542f8b8135f1f1ee5654e25139be9769e139ddd (patch)
tree6e05d7c624d89b3df27d0d3437815a2587f0640f /NetworkPkg/Library/DxeNetLib
parentc0fd7f734e2d33e22215899b40a47b843129541d (diff)
downloadedk2-4542f8b8135f1f1ee5654e25139be9769e139ddd.tar.gz
edk2-4542f8b8135f1f1ee5654e25139be9769e139ddd.tar.bz2
edk2-4542f8b8135f1f1ee5654e25139be9769e139ddd.zip
NetworkPkg: Move Network library and drivers from MdeModulePkg to NetworkPkg
Signed-off-by: Liming Gao <liming.gao@intel.com> Cc: Siyuan Fu <siyuan.fu@intel.com> Cc: Jiaxin Wu <jiaxin.wu@intel.com> Reviewed-by: Jiaxin Wu <jiaxin.wu@intel.com> Reviewed-by: Siyuan Fu <siyuan.fu@intel.com>
Diffstat (limited to 'NetworkPkg/Library/DxeNetLib')
-rw-r--r--NetworkPkg/Library/DxeNetLib/DxeNetLib.c3394
-rw-r--r--NetworkPkg/Library/DxeNetLib/DxeNetLib.inf61
-rw-r--r--NetworkPkg/Library/DxeNetLib/DxeNetLib.uni16
-rw-r--r--NetworkPkg/Library/DxeNetLib/NetBuffer.c1890
4 files changed, 5361 insertions, 0 deletions
diff --git a/NetworkPkg/Library/DxeNetLib/DxeNetLib.c b/NetworkPkg/Library/DxeNetLib/DxeNetLib.c
new file mode 100644
index 0000000000..8e2f720666
--- /dev/null
+++ b/NetworkPkg/Library/DxeNetLib/DxeNetLib.c
@@ -0,0 +1,3394 @@
+/** @file
+ Network library.
+
+Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <IndustryStandard/SmBios.h>
+
+#include <Protocol/DriverBinding.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/AdapterInformation.h>
+#include <Protocol/ManagedNetwork.h>
+#include <Protocol/Ip4Config2.h>
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+
+#include <Guid/SmBios.h>
+
+#include <Library/NetLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiLib.h>
+
+#define NIC_ITEM_CONFIG_SIZE (sizeof (NIC_IP4_CONFIG_INFO) + sizeof (EFI_IP4_ROUTE_TABLE) * MAX_IP4_CONFIG_IN_VARIABLE)
+#define DEFAULT_ZERO_START ((UINTN) ~0)
+
+//
+// All the supported IP4 maskes in host byte order.
+//
+GLOBAL_REMOVE_IF_UNREFERENCED IP4_ADDR gIp4AllMasks[IP4_MASK_NUM] = {
+ 0x00000000,
+ 0x80000000,
+ 0xC0000000,
+ 0xE0000000,
+ 0xF0000000,
+ 0xF8000000,
+ 0xFC000000,
+ 0xFE000000,
+
+ 0xFF000000,
+ 0xFF800000,
+ 0xFFC00000,
+ 0xFFE00000,
+ 0xFFF00000,
+ 0xFFF80000,
+ 0xFFFC0000,
+ 0xFFFE0000,
+
+ 0xFFFF0000,
+ 0xFFFF8000,
+ 0xFFFFC000,
+ 0xFFFFE000,
+ 0xFFFFF000,
+ 0xFFFFF800,
+ 0xFFFFFC00,
+ 0xFFFFFE00,
+
+ 0xFFFFFF00,
+ 0xFFFFFF80,
+ 0xFFFFFFC0,
+ 0xFFFFFFE0,
+ 0xFFFFFFF0,
+ 0xFFFFFFF8,
+ 0xFFFFFFFC,
+ 0xFFFFFFFE,
+ 0xFFFFFFFF,
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_IPv4_ADDRESS mZeroIp4Addr = {{0, 0, 0, 0}};
+
+//
+// Any error level digitally larger than mNetDebugLevelMax
+// will be silently discarded.
+//
+GLOBAL_REMOVE_IF_UNREFERENCED UINTN mNetDebugLevelMax = NETDEBUG_LEVEL_ERROR;
+GLOBAL_REMOVE_IF_UNREFERENCED 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.
+//
+GLOBAL_REMOVE_IF_UNREFERENCED UINT8 mSyslogDstMac[NET_ETHER_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mSyslogDstIp = 0xffffffff;
+GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mSyslogSrcIp = 0;
+
+GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 *mMonthName[] = {
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec"
+};
+
+//
+// VLAN device path node template
+//
+GLOBAL_REMOVE_IF_UNREFERENCED VLAN_DEVICE_PATH mNetVlanDevicePathTemplate = {
+ {
+ MESSAGING_DEVICE_PATH,
+ MSG_VLAN_DP,
+ {
+ (UINT8) (sizeof (VLAN_DEVICE_PATH)),
+ (UINT8) ((sizeof (VLAN_DEVICE_PATH)) >> 8)
+ }
+ },
+ 0
+};
+
+/**
+ 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.
+
+ @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;
+ }
+
+ 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.
+ If Packet is NULL, then ASSERT().
+
+ @param[in] Packet The Syslog packet
+ @param[in] 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 CHAR8 *Packet,
+ IN UINT32 Length
+ )
+{
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ ETHER_HEAD *Ether;
+ EFI_STATUS Status;
+ EFI_EVENT TimeoutEvent;
+ UINT8 *TxBuf;
+
+ ASSERT (Packet != NULL);
+
+ 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, (VOID **) &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;
+}
+
+/**
+ Build a syslog packet, including the Ethernet/Ip/Udp headers
+ and user's message.
+
+ @param[in] Level Syslog severity level
+ @param[in] Module The module that generates the log
+ @param[in] File The file that contains the current log
+ @param[in] Line The line of code in the File that contains the current log
+ @param[in] Message The log message
+ @param[in] BufLen The lenght of the Buf
+ @param[out] Buf The buffer to put the packet data
+
+ @return The length of the syslog packet built, 0 represents no packet is built.
+
+**/
+UINT32
+SyslogBuildPacket (
+ IN UINT32 Level,
+ IN UINT8 *Module,
+ IN UINT8 *File,
+ IN UINT32 Line,
+ IN UINT8 *Message,
+ IN UINT32 BufLen,
+ OUT CHAR8 *Buf
+ )
+{
+ EFI_STATUS Status;
+ ETHER_HEAD *Ether;
+ IP4_HEAD *Ip4;
+ EFI_UDP_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); // IPv4 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_UDP_HEADER *) Buf;
+ Udp4->SrcPort = HTONS (514);
+ Udp4->DstPort = HTONS (514);
+ Udp4->Length = 0;
+ Udp4->Checksum = 0;
+
+ Buf += sizeof (EFI_UDP_HEADER);
+ BufLen -= sizeof (EFI_UDP_HEADER);
+
+ //
+ // Build the syslog message body with <PRI> Timestamp machine module Message
+ //
+ Pri = ((NET_SYSLOG_FACILITY & 31) << 3) | (Level & 7);
+ Status = gRT->GetTime (&Time, NULL);
+ if (EFI_ERROR (Status)) {
+ return 0;
+ }
+
+ //
+ // 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,
+ mMonthName [Time.Month-1],
+ Time.Day,
+ Time.Hour,
+ Time.Minute,
+ Time.Second
+ );
+
+ 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_UDP_HEADER);
+ Udp4->Length = HTONS ((UINT16) Len);
+
+ Len += sizeof (IP4_HEAD);
+ Ip4->TotalLen = HTONS ((UINT16) Len);
+ Ip4->Checksum = (UINT16) (~NetblockChecksum ((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)
+ )
+
+ If Format is NULL, then ASSERT().
+
+ @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 allocate memory.
+
+**/
+CHAR8 *
+EFIAPI
+NetDebugASPrint (
+ IN CHAR8 *Format,
+ ...
+ )
+{
+ VA_LIST Marker;
+ CHAR8 *Buf;
+
+ ASSERT (Format != NULL);
+
+ Buf = (CHAR8 *) 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;
+}
+
+/**
+ Builds an UDP4 syslog packet and send it using SNP.
+
+ 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 severity 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_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet.
+ @retval EFI_DEVICE_ERROR Device error occurs.
+ @retval EFI_SUCCESS The log is discard because that it is more verbose
+ than the mNetDebugLevelMax. Or, it has been sent out.
+**/
+EFI_STATUS
+EFIAPI
+NetDebugOutput (
+ IN UINT32 Level,
+ IN UINT8 *Module,
+ IN UINT8 *File,
+ IN UINT32 Line,
+ IN UINT8 *Message
+ )
+{
+ CHAR8 *Packet;
+ UINT32 Len;
+ EFI_STATUS Status;
+
+ //
+ // Check whether the message should be sent out
+ //
+ if (Message == NULL || File == NULL || Module == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ 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 = (CHAR8 *) 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 (
+ Level,
+ Module,
+ File,
+ Line,
+ Message,
+ NET_SYSLOG_PACKET_LEN,
+ Packet
+ );
+ if (Len == 0) {
+ Status = EFI_DEVICE_ERROR;
+ } else {
+ mSyslogPacketSeq++;
+ Status = SyslogSendPacket (Packet, Len);
+ }
+
+ FreePool (Packet);
+
+ON_EXIT:
+ FreePool (Message);
+ return Status;
+}
+/**
+ Return the length of the mask.
+
+ Return the length of the mask, the correct value is from 0 to 32.
+ If the mask is invalid, return the invalid length 33, which is IP4_MASK_NUM.
+ NetMask is in the host byte order.
+
+ @param[in] NetMask The netmask to get the length from.
+
+ @return The length of the netmask, IP4_MASK_NUM if the mask is invalid.
+
+**/
+INTN
+EFIAPI
+NetGetMaskLength (
+ IN IP4_ADDR NetMask
+ )
+{
+ INTN Index;
+
+ for (Index = 0; Index <= IP4_MASK_MAX; Index++) {
+ if (NetMask == gIp4AllMasks[Index]) {
+ break;
+ }
+ }
+
+ return Index;
+}
+
+
+
+/**
+ Return the class of the IP address, such as class A, B, C.
+ Addr is in host byte order.
+
+ [ATTENTION]
+ Classful addressing (IP class A/B/C) has been deprecated according to RFC4632.
+ Caller of this function could only check the returned value against
+ IP4_ADDR_CLASSD (multicast) or IP4_ADDR_CLASSE (reserved) now.
+
+ The address of class A starts with 0.
+ If the address belong to class A, return IP4_ADDR_CLASSA.
+ The address of class B starts with 10.
+ If the address belong to class B, return IP4_ADDR_CLASSB.
+ The address of class C starts with 110.
+ If the address belong to class C, return IP4_ADDR_CLASSC.
+ The address of class D starts with 1110.
+ If the address belong to class D, return IP4_ADDR_CLASSD.
+ The address of class E starts with 1111.
+ If the address belong to class E, return IP4_ADDR_CLASSE.
+
+
+ @param[in] Addr The address to get the class from.
+
+ @return IP address class, such as IP4_ADDR_CLASSA.
+
+**/
+INTN
+EFIAPI
+NetGetIpClass (
+ IN IP4_ADDR Addr
+ )
+{
+ UINT8 ByteOne;
+
+ ByteOne = (UINT8) (Addr >> 24);
+
+ if ((ByteOne & 0x80) == 0) {
+ return IP4_ADDR_CLASSA;
+
+ } else if ((ByteOne & 0xC0) == 0x80) {
+ return IP4_ADDR_CLASSB;
+
+ } else if ((ByteOne & 0xE0) == 0xC0) {
+ return IP4_ADDR_CLASSC;
+
+ } else if ((ByteOne & 0xF0) == 0xE0) {
+ return IP4_ADDR_CLASSD;
+
+ } else {
+ return IP4_ADDR_CLASSE;
+
+ }
+}
+
+
+/**
+ Check whether the IP is a valid unicast address according to
+ the netmask.
+
+ ASSERT if NetMask is zero.
+
+ If all bits of the host address of IP are 0 or 1, IP is also not a valid unicast address,
+ except when the originator is one of the endpoints of a point-to-point link with a 31-bit
+ mask (RFC3021), or a 32bit NetMask (all 0xFF) is used for special network environment (e.g.
+ PPP link).
+
+ @param[in] Ip The IP to check against.
+ @param[in] NetMask The mask of the IP.
+
+ @return TRUE if IP is a valid unicast address on the network, otherwise FALSE.
+
+**/
+BOOLEAN
+EFIAPI
+NetIp4IsUnicast (
+ IN IP4_ADDR Ip,
+ IN IP4_ADDR NetMask
+ )
+{
+ INTN MaskLength;
+
+ ASSERT (NetMask != 0);
+
+ if (Ip == 0 || IP4_IS_LOCAL_BROADCAST (Ip)) {
+ return FALSE;
+ }
+
+ MaskLength = NetGetMaskLength (NetMask);
+ ASSERT ((MaskLength >= 0) && (MaskLength <= IP4_MASK_NUM));
+ if (MaskLength < 31) {
+ if (((Ip &~NetMask) == ~NetMask) || ((Ip &~NetMask) == 0)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ Check whether the incoming IPv6 address is a valid unicast address.
+
+ ASSERT if Ip6 is NULL.
+
+ If the address is a multicast address has binary 0xFF at the start, it is not
+ a valid unicast address. If the address is unspecified ::, it is not a valid
+ unicast address to be assigned to any node. If the address is loopback address
+ ::1, it is also not a valid unicast address to be assigned to any physical
+ interface.
+
+ @param[in] Ip6 The IPv6 address to check against.
+
+ @return TRUE if Ip6 is a valid unicast address on the network, otherwise FALSE.
+
+**/
+BOOLEAN
+EFIAPI
+NetIp6IsValidUnicast (
+ IN EFI_IPv6_ADDRESS *Ip6
+ )
+{
+ UINT8 Byte;
+ UINT8 Index;
+
+ ASSERT (Ip6 != NULL);
+
+ if (Ip6->Addr[0] == 0xFF) {
+ return FALSE;
+ }
+
+ for (Index = 0; Index < 15; Index++) {
+ if (Ip6->Addr[Index] != 0) {
+ return TRUE;
+ }
+ }
+
+ Byte = Ip6->Addr[Index];
+
+ if (Byte == 0x0 || Byte == 0x1) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Check whether the incoming Ipv6 address is the unspecified address or not.
+
+ ASSERT if Ip6 is NULL.
+
+ @param[in] Ip6 - Ip6 address, in network order.
+
+ @retval TRUE - Yes, unspecified
+ @retval FALSE - No
+
+**/
+BOOLEAN
+EFIAPI
+NetIp6IsUnspecifiedAddr (
+ IN EFI_IPv6_ADDRESS *Ip6
+ )
+{
+ UINT8 Index;
+
+ ASSERT (Ip6 != NULL);
+
+ for (Index = 0; Index < 16; Index++) {
+ if (Ip6->Addr[Index] != 0) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ Check whether the incoming Ipv6 address is a link-local address.
+
+ ASSERT if Ip6 is NULL.
+
+ @param[in] Ip6 - Ip6 address, in network order.
+
+ @retval TRUE - Yes, link-local address
+ @retval FALSE - No
+
+**/
+BOOLEAN
+EFIAPI
+NetIp6IsLinkLocalAddr (
+ IN EFI_IPv6_ADDRESS *Ip6
+ )
+{
+ UINT8 Index;
+
+ ASSERT (Ip6 != NULL);
+
+ if (Ip6->Addr[0] != 0xFE) {
+ return FALSE;
+ }
+
+ if (Ip6->Addr[1] != 0x80) {
+ return FALSE;
+ }
+
+ for (Index = 2; Index < 8; Index++) {
+ if (Ip6->Addr[Index] != 0) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ Check whether the Ipv6 address1 and address2 are on the connected network.
+
+ ASSERT if Ip1 or Ip2 is NULL.
+ ASSERT if PrefixLength exceeds or equals to IP6_PREFIX_MAX.
+
+ @param[in] Ip1 - Ip6 address1, in network order.
+ @param[in] Ip2 - Ip6 address2, in network order.
+ @param[in] PrefixLength - The prefix length of the checking net.
+
+ @retval TRUE - Yes, connected.
+ @retval FALSE - No.
+
+**/
+BOOLEAN
+EFIAPI
+NetIp6IsNetEqual (
+ EFI_IPv6_ADDRESS *Ip1,
+ EFI_IPv6_ADDRESS *Ip2,
+ UINT8 PrefixLength
+ )
+{
+ UINT8 Byte;
+ UINT8 Bit;
+ UINT8 Mask;
+
+ ASSERT ((Ip1 != NULL) && (Ip2 != NULL) && (PrefixLength < IP6_PREFIX_MAX));
+
+ if (PrefixLength == 0) {
+ return TRUE;
+ }
+
+ Byte = (UINT8) (PrefixLength / 8);
+ Bit = (UINT8) (PrefixLength % 8);
+
+ if (CompareMem (Ip1, Ip2, Byte) != 0) {
+ return FALSE;
+ }
+
+ if (Bit > 0) {
+ Mask = (UINT8) (0xFF << (8 - Bit));
+
+ ASSERT (Byte < 16);
+ if (Byte >= 16) {
+ return FALSE;
+ }
+ if ((Ip1->Addr[Byte] & Mask) != (Ip2->Addr[Byte] & Mask)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Switches the endianess of an IPv6 address
+
+ ASSERT if Ip6 is NULL.
+
+ This function swaps the bytes in a 128-bit IPv6 address to switch the value
+ from little endian to big endian or vice versa. The byte swapped value is
+ returned.
+
+ @param Ip6 Points to an IPv6 address
+
+ @return The byte swapped IPv6 address.
+
+**/
+EFI_IPv6_ADDRESS *
+EFIAPI
+Ip6Swap128 (
+ EFI_IPv6_ADDRESS *Ip6
+ )
+{
+ UINT64 High;
+ UINT64 Low;
+
+ ASSERT (Ip6 != NULL);
+
+ CopyMem (&High, Ip6, sizeof (UINT64));
+ CopyMem (&Low, &Ip6->Addr[8], sizeof (UINT64));
+
+ High = SwapBytes64 (High);
+ Low = SwapBytes64 (Low);
+
+ CopyMem (Ip6, &Low, sizeof (UINT64));
+ CopyMem (&Ip6->Addr[8], &High, sizeof (UINT64));
+
+ return Ip6;
+}
+
+/**
+ Initialize a random seed using current time and monotonic count.
+
+ Get current time and monotonic count first. Then initialize a random seed
+ based on some basic mathematics operation on the hour, day, minute, second,
+ nanosecond and year of the current time and the monotonic count value.
+
+ @return The random seed initialized with current time.
+
+**/
+UINT32
+EFIAPI
+NetRandomInitSeed (
+ VOID
+ )
+{
+ EFI_TIME Time;
+ UINT32 Seed;
+ UINT64 MonotonicCount;
+
+ gRT->GetTime (&Time, NULL);
+ Seed = (Time.Hour << 24 | Time.Day << 16 | Time.Minute << 8 | Time.Second);
+ Seed ^= Time.Nanosecond;
+ Seed ^= Time.Year << 7;
+
+ gBS->GetNextMonotonicCount (&MonotonicCount);
+ Seed += (UINT32) MonotonicCount;
+
+ return Seed;
+}
+
+
+/**
+ Extract a UINT32 from a byte stream.
+
+ ASSERT if Buf is NULL.
+
+ Copy a UINT32 from a byte stream, then converts it from Network
+ byte order to host byte order. Use this function to avoid alignment error.
+
+ @param[in] Buf The buffer to extract the UINT32.
+
+ @return The UINT32 extracted.
+
+**/
+UINT32
+EFIAPI
+NetGetUint32 (
+ IN UINT8 *Buf
+ )
+{
+ UINT32 Value;
+
+ ASSERT (Buf != NULL);
+
+ CopyMem (&Value, Buf, sizeof (UINT32));
+ return NTOHL (Value);
+}
+
+
+/**
+ Put a UINT32 to the byte stream in network byte order.
+
+ ASSERT if Buf is NULL.
+
+ Converts a UINT32 from host byte order to network byte order. Then copy it to the
+ byte stream.
+
+ @param[in, out] Buf The buffer to put the UINT32.
+ @param[in] Data The data to be converted and put into the byte stream.
+
+**/
+VOID
+EFIAPI
+NetPutUint32 (
+ IN OUT UINT8 *Buf,
+ IN UINT32 Data
+ )
+{
+ ASSERT (Buf != NULL);
+
+ Data = HTONL (Data);
+ CopyMem (Buf, &Data, sizeof (UINT32));
+}
+
+
+/**
+ Remove the first node entry on the list, and return the removed node entry.
+
+ Removes the first node Entry from a doubly linked list. It is up to the caller of
+ this function to release the memory used by the first node if that is required. On
+ exit, the removed node is returned.
+
+ If Head is NULL, then ASSERT().
+ If Head was not initialized, then ASSERT().
+ If PcdMaximumLinkedListLength is not zero, and the number of nodes in the
+ linked list including the head node is greater than or equal to PcdMaximumLinkedListLength,
+ then ASSERT().
+
+ @param[in, out] Head The list header.
+
+ @return The first node entry that is removed from the list, NULL if the list is empty.
+
+**/
+LIST_ENTRY *
+EFIAPI
+NetListRemoveHead (
+ IN OUT LIST_ENTRY *Head
+ )
+{
+ LIST_ENTRY *First;
+
+ ASSERT (Head != NULL);
+
+ if (IsListEmpty (Head)) {
+ return NULL;
+ }
+
+ First = Head->ForwardLink;
+ Head->ForwardLink = First->ForwardLink;
+ First->ForwardLink->BackLink = Head;
+
+ DEBUG_CODE (
+ First->ForwardLink = (LIST_ENTRY *) NULL;
+ First->BackLink = (LIST_ENTRY *) NULL;
+ );
+
+ return First;
+}
+
+
+/**
+ Remove the last node entry on the list and and return the removed node entry.
+
+ Removes the last node entry from a doubly linked list. It is up to the caller of
+ this function to release the memory used by the first node if that is required. On
+ exit, the removed node is returned.
+
+ If Head is NULL, then ASSERT().
+ If Head was not initialized, then ASSERT().
+ If PcdMaximumLinkedListLength is not zero, and the number of nodes in the
+ linked list including the head node is greater than or equal to PcdMaximumLinkedListLength,
+ then ASSERT().
+
+ @param[in, out] Head The list head.
+
+ @return The last node entry that is removed from the list, NULL if the list is empty.
+
+**/
+LIST_ENTRY *
+EFIAPI
+NetListRemoveTail (
+ IN OUT LIST_ENTRY *Head
+ )
+{
+ LIST_ENTRY *Last;
+
+ ASSERT (Head != NULL);
+
+ if (IsListEmpty (Head)) {
+ return NULL;
+ }
+
+ Last = Head->BackLink;
+ Head->BackLink = Last->BackLink;
+ Last->BackLink->ForwardLink = Head;
+
+ DEBUG_CODE (
+ Last->ForwardLink = (LIST_ENTRY *) NULL;
+ Last->BackLink = (LIST_ENTRY *) NULL;
+ );
+
+ return Last;
+}
+
+
+/**
+ Insert a new node entry after a designated node entry of a doubly linked list.
+
+ ASSERT if PrevEntry or NewEntry is NULL.
+
+ Inserts a new node entry donated by NewEntry after the node entry donated by PrevEntry
+ of the doubly linked list.
+
+ @param[in, out] PrevEntry The previous entry to insert after.
+ @param[in, out] NewEntry The new entry to insert.
+
+**/
+VOID
+EFIAPI
+NetListInsertAfter (
+ IN OUT LIST_ENTRY *PrevEntry,
+ IN OUT LIST_ENTRY *NewEntry
+ )
+{
+ ASSERT (PrevEntry != NULL && NewEntry != NULL);
+
+ NewEntry->BackLink = PrevEntry;
+ NewEntry->ForwardLink = PrevEntry->ForwardLink;
+ PrevEntry->ForwardLink->BackLink = NewEntry;
+ PrevEntry->ForwardLink = NewEntry;
+}
+
+
+/**
+ Insert a new node entry before a designated node entry of a doubly linked list.
+
+ ASSERT if PostEntry or NewEntry is NULL.
+
+ Inserts a new node entry donated by NewEntry after the node entry donated by PostEntry
+ of the doubly linked list.
+
+ @param[in, out] PostEntry The entry to insert before.
+ @param[in, out] NewEntry The new entry to insert.
+
+**/
+VOID
+EFIAPI
+NetListInsertBefore (
+ IN OUT LIST_ENTRY *PostEntry,
+ IN OUT LIST_ENTRY *NewEntry
+ )
+{
+ ASSERT (PostEntry != NULL && NewEntry != NULL);
+
+ NewEntry->ForwardLink = PostEntry;
+ NewEntry->BackLink = PostEntry->BackLink;
+ PostEntry->BackLink->ForwardLink = NewEntry;
+ PostEntry->BackLink = NewEntry;
+}
+
+/**
+ Safe destroy nodes in a linked list, and return the length of the list after all possible operations finished.
+
+ Destroy network child instance list by list traversals is not safe due to graph dependencies between nodes.
+ This function performs a safe traversal to destroy these nodes by checking to see if the node being destroyed
+ has been removed from the list or not.
+ If it has been removed, then restart the traversal from the head.
+ If it hasn't been removed, then continue with the next node directly.
+ This function will end the iterate and return the CallBack's last return value if error happens,
+ or retrun EFI_SUCCESS if 2 complete passes are made with no changes in the number of children in the list.
+
+ @param[in] List The head of the list.
+ @param[in] CallBack Pointer to the callback function to destroy one node in the list.
+ @param[in] Context Pointer to the callback function's context: corresponds to the
+ parameter Context in NET_DESTROY_LINK_LIST_CALLBACK.
+ @param[out] ListLength The length of the link list if the function returns successfully.
+
+ @retval EFI_SUCCESS Two complete passes are made with no changes in the number of children.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+ @retval Others Return the CallBack's last return value.
+
+**/
+EFI_STATUS
+EFIAPI
+NetDestroyLinkList (
+ IN LIST_ENTRY *List,
+ IN NET_DESTROY_LINK_LIST_CALLBACK CallBack,
+ IN VOID *Context, OPTIONAL
+ OUT UINTN *ListLength OPTIONAL
+ )
+{
+ UINTN PreviousLength;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Ptr;
+ UINTN Length;
+ EFI_STATUS Status;
+
+ if (List == NULL || CallBack == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Length = 0;
+ do {
+ PreviousLength = Length;
+ Entry = GetFirstNode (List);
+ while (!IsNull (List, Entry)) {
+ Status = CallBack (Entry, Context);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Walk through the list to see whether the Entry has been removed or not.
+ // If the Entry still exists, just try to destroy the next one.
+ // If not, go back to the start point to iterate the list again.
+ //
+ for (Ptr = List->ForwardLink; Ptr != List; Ptr = Ptr->ForwardLink) {
+ if (Ptr == Entry) {
+ break;
+ }
+ }
+ if (Ptr == Entry) {
+ Entry = GetNextNode (List, Entry);
+ } else {
+ Entry = GetFirstNode (List);
+ }
+ }
+ for (Length = 0, Ptr = List->ForwardLink; Ptr != List; Length++, Ptr = Ptr->ForwardLink);
+ } while (Length != PreviousLength);
+
+ if (ListLength != NULL) {
+ *ListLength = Length;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ This function checks the input Handle to see if it's one of these handles in ChildHandleBuffer.
+
+ @param[in] Handle Handle to be checked.
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval TRUE Found the input Handle in ChildHandleBuffer.
+ @retval FALSE Can't find the input Handle in ChildHandleBuffer.
+
+**/
+BOOLEAN
+EFIAPI
+NetIsInHandleBuffer (
+ IN EFI_HANDLE Handle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ UINTN Index;
+
+ if (NumberOfChildren == 0 || ChildHandleBuffer == NULL) {
+ return FALSE;
+ }
+
+ for (Index = 0; Index < NumberOfChildren; Index++) {
+ if (Handle == ChildHandleBuffer[Index]) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Initialize the netmap. Netmap is a reposity to keep the <Key, Value> pairs.
+
+ Initialize the forward and backward links of two head nodes donated by Map->Used
+ and Map->Recycled of two doubly linked lists.
+ Initializes the count of the <Key, Value> pairs in the netmap to zero.
+
+ If Map is NULL, then ASSERT().
+ If the address of Map->Used is NULL, then ASSERT().
+ If the address of Map->Recycled is NULl, then ASSERT().
+
+ @param[in, out] Map The netmap to initialize.
+
+**/
+VOID
+EFIAPI
+NetMapInit (
+ IN OUT NET_MAP *Map
+ )
+{
+ ASSERT (Map != NULL);
+
+ InitializeListHead (&Map->Used);
+ InitializeListHead (&Map->Recycled);
+ Map->Count = 0;
+}
+
+
+/**
+ To clean up the netmap, that is, release allocated memories.
+
+ Removes all nodes of the Used doubly linked list and free memory of all related netmap items.
+ Removes all nodes of the Recycled doubly linked list and free memory of all related netmap items.
+ The number of the <Key, Value> pairs in the netmap is set to be zero.
+
+ If Map is NULL, then ASSERT().
+
+ @param[in, out] Map The netmap to clean up.
+
+**/
+VOID
+EFIAPI
+NetMapClean (
+ IN OUT NET_MAP *Map
+ )
+{
+ NET_MAP_ITEM *Item;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+
+ ASSERT (Map != NULL);
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Map->Used) {
+ Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);
+
+ RemoveEntryList (&Item->Link);
+ Map->Count--;
+
+ gBS->FreePool (Item);
+ }
+
+ ASSERT ((Map->Count == 0) && IsListEmpty (&Map->Used));
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Map->Recycled) {
+ Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);
+
+ RemoveEntryList (&Item->Link);
+ gBS->FreePool (Item);
+ }
+
+ ASSERT (IsListEmpty (&Map->Recycled));
+}
+
+
+/**
+ Test whether the netmap is empty and return true if it is.
+
+ If the number of the <Key, Value> pairs in the netmap is zero, return TRUE.
+
+ If Map is NULL, then ASSERT().
+
+ @param[in] Map The net map to test.
+
+ @return TRUE if the netmap is empty, otherwise FALSE.
+
+**/
+BOOLEAN
+EFIAPI
+NetMapIsEmpty (
+ IN NET_MAP *Map
+ )
+{
+ ASSERT (Map != NULL);
+ return (BOOLEAN) (Map->Count == 0);
+}
+
+
+/**
+ Return the number of the <Key, Value> pairs in the netmap.
+
+ If Map is NULL, then ASSERT().
+
+ @param[in] Map The netmap to get the entry number.
+
+ @return The entry number in the netmap.
+
+**/
+UINTN
+EFIAPI
+NetMapGetCount (
+ IN NET_MAP *Map
+ )
+{
+ ASSERT (Map != NULL);
+ return Map->Count;
+}
+
+
+/**
+ Return one allocated item.
+
+ If the Recycled doubly linked list of the netmap is empty, it will try to allocate
+ a batch of items if there are enough resources and add corresponding nodes to the begining
+ of the Recycled doubly linked list of the netmap. Otherwise, it will directly remove
+ the fist node entry of the Recycled doubly linked list and return the corresponding item.
+
+ If Map is NULL, then ASSERT().
+
+ @param[in, out] Map The netmap to allocate item for.
+
+ @return The allocated item. If NULL, the
+ allocation failed due to resource limit.
+
+**/
+NET_MAP_ITEM *
+NetMapAllocItem (
+ IN OUT NET_MAP *Map
+ )
+{
+ NET_MAP_ITEM *Item;
+ LIST_ENTRY *Head;
+ UINTN Index;
+
+ ASSERT (Map != NULL);
+
+ Head = &Map->Recycled;
+
+ if (IsListEmpty (Head)) {
+ for (Index = 0; Index < NET_MAP_INCREAMENT; Index++) {
+ Item = AllocatePool (sizeof (NET_MAP_ITEM));
+
+ if (Item == NULL) {
+ if (Index == 0) {
+ return NULL;
+ }
+
+ break;
+ }
+
+ InsertHeadList (Head, &Item->Link);
+ }
+ }
+
+ Item = NET_LIST_HEAD (Head, NET_MAP_ITEM, Link);
+ NetListRemoveHead (Head);
+
+ return Item;
+}
+
+
+/**
+ Allocate an item to save the <Key, Value> pair to the head of the netmap.
+
+ Allocate an item to save the <Key, Value> pair and add corresponding node entry
+ to the beginning of the Used doubly linked list. The number of the <Key, Value>
+ pairs in the netmap increase by 1.
+
+ If Map is NULL, then ASSERT().
+ If Key is NULL, then ASSERT().
+
+ @param[in, out] Map The netmap to insert into.
+ @param[in] Key The user's key.
+ @param[in] Value The user's value for the key.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the item.
+ @retval EFI_SUCCESS The item is inserted to the head.
+
+**/
+EFI_STATUS
+EFIAPI
+NetMapInsertHead (
+ IN OUT NET_MAP *Map,
+ IN VOID *Key,
+ IN VOID *Value OPTIONAL
+ )
+{
+ NET_MAP_ITEM *Item;
+
+ ASSERT (Map != NULL && Key != NULL);
+
+ Item = NetMapAllocItem (Map);
+
+ if (Item == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Item->Key = Key;
+ Item->Value = Value;
+ InsertHeadList (&Map->Used, &Item->Link);
+
+ Map->Count++;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Allocate an item to save the <Key, Value> pair to the tail of the netmap.
+
+ Allocate an item to save the <Key, Value> pair and add corresponding node entry
+ to the tail of the Used doubly linked list. The number of the <Key, Value>
+ pairs in the netmap increase by 1.
+
+ If Map is NULL, then ASSERT().
+ If Key is NULL, then ASSERT().
+
+ @param[in, out] Map The netmap to insert into.
+ @param[in] Key The user's key.
+ @param[in] Value The user's value for the key.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the item.
+ @retval EFI_SUCCESS The item is inserted to the tail.
+
+**/
+EFI_STATUS
+EFIAPI
+NetMapInsertTail (
+ IN OUT NET_MAP *Map,
+ IN VOID *Key,
+ IN VOID *Value OPTIONAL
+ )
+{
+ NET_MAP_ITEM *Item;
+
+ ASSERT (Map != NULL && Key != NULL);
+
+ Item = NetMapAllocItem (Map);
+
+ if (Item == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Item->Key = Key;
+ Item->Value = Value;
+ InsertTailList (&Map->Used, &Item->Link);
+
+ Map->Count++;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Check whether the item is in the Map and return TRUE if it is.
+
+ If Map is NULL, then ASSERT().
+ If Item is NULL, then ASSERT().
+
+ @param[in] Map The netmap to search within.
+ @param[in] Item The item to search.
+
+ @return TRUE if the item is in the netmap, otherwise FALSE.
+
+**/
+BOOLEAN
+NetItemInMap (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item
+ )
+{
+ LIST_ENTRY *ListEntry;
+
+ ASSERT (Map != NULL && Item != NULL);
+
+ NET_LIST_FOR_EACH (ListEntry, &Map->Used) {
+ if (ListEntry == &Item->Link) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Find the key in the netmap and returns the point to the item contains the Key.
+
+ Iterate the Used doubly linked list of the netmap to get every item. Compare the key of every
+ item with the key to search. It returns the point to the item contains the Key if found.
+
+ If Map is NULL, then ASSERT().
+ If Key is NULL, then ASSERT().
+
+ @param[in] Map The netmap to search within.
+ @param[in] Key The key to search.
+
+ @return The point to the item contains the Key, or NULL if Key isn't in the map.
+
+**/
+NET_MAP_ITEM *
+EFIAPI
+NetMapFindKey (
+ IN NET_MAP *Map,
+ IN VOID *Key
+ )
+{
+ LIST_ENTRY *Entry;
+ NET_MAP_ITEM *Item;
+
+ ASSERT (Map != NULL && Key != NULL);
+
+ NET_LIST_FOR_EACH (Entry, &Map->Used) {
+ Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);
+
+ if (Item->Key == Key) {
+ return Item;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Remove the node entry of the item from the netmap and return the key of the removed item.
+
+ Remove the node entry of the item from the Used doubly linked list of the netmap.
+ The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node
+ entry of the item to the Recycled doubly linked list of the netmap. If Value is not NULL,
+ Value will point to the value of the item. It returns the key of the removed item.
+
+ If Map is NULL, then ASSERT().
+ If Item is NULL, then ASSERT().
+ if item in not in the netmap, then ASSERT().
+
+ @param[in, out] Map The netmap to remove the item from.
+ @param[in, out] Item The item to remove.
+ @param[out] Value The variable to receive the value if not NULL.
+
+ @return The key of the removed item.
+
+**/
+VOID *
+EFIAPI
+NetMapRemoveItem (
+ IN OUT NET_MAP *Map,
+ IN OUT NET_MAP_ITEM *Item,
+ OUT VOID **Value OPTIONAL
+ )
+{
+ ASSERT ((Map != NULL) && (Item != NULL));
+ ASSERT (NetItemInMap (Map, Item));
+
+ RemoveEntryList (&Item->Link);
+ Map->Count--;
+ InsertHeadList (&Map->Recycled, &Item->Link);
+
+ if (Value != NULL) {
+ *Value = Item->Value;
+ }
+
+ return Item->Key;
+}
+
+
+/**
+ Remove the first node entry on the netmap and return the key of the removed item.
+
+ Remove the first node entry from the Used doubly linked list of the netmap.
+ The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node
+ entry to the Recycled doubly linked list of the netmap. If parameter Value is not NULL,
+ parameter Value will point to the value of the item. It returns the key of the removed item.
+
+ If Map is NULL, then ASSERT().
+ If the Used doubly linked list is empty, then ASSERT().
+
+ @param[in, out] Map The netmap to remove the head from.
+ @param[out] Value The variable to receive the value if not NULL.
+
+ @return The key of the item removed.
+
+**/
+VOID *
+EFIAPI
+NetMapRemoveHead (
+ IN OUT NET_MAP *Map,
+ OUT VOID **Value OPTIONAL
+ )
+{
+ NET_MAP_ITEM *Item;
+
+ //
+ // Often, it indicates a programming error to remove
+ // the first entry in an empty list
+ //
+ ASSERT (Map && !IsListEmpty (&Map->Used));
+
+ Item = NET_LIST_HEAD (&Map->Used, NET_MAP_ITEM, Link);
+ RemoveEntryList (&Item->Link);
+ Map->Count--;
+ InsertHeadList (&Map->Recycled, &Item->Link);
+
+ if (Value != NULL) {
+ *Value = Item->Value;
+ }
+
+ return Item->Key;
+}
+
+
+/**
+ Remove the last node entry on the netmap and return the key of the removed item.
+
+ Remove the last node entry from the Used doubly linked list of the netmap.
+ The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node
+ entry to the Recycled doubly linked list of the netmap. If parameter Value is not NULL,
+ parameter Value will point to the value of the item. It returns the key of the removed item.
+
+ If Map is NULL, then ASSERT().
+ If the Used doubly linked list is empty, then ASSERT().
+
+ @param[in, out] Map The netmap to remove the tail from.
+ @param[out] Value The variable to receive the value if not NULL.
+
+ @return The key of the item removed.
+
+**/
+VOID *
+EFIAPI
+NetMapRemoveTail (
+ IN OUT NET_MAP *Map,
+ OUT VOID **Value OPTIONAL
+ )
+{
+ NET_MAP_ITEM *Item;
+
+ //
+ // Often, it indicates a programming error to remove
+ // the last entry in an empty list
+ //
+ ASSERT (Map && !IsListEmpty (&Map->Used));
+
+ Item = NET_LIST_TAIL (&Map->Used, NET_MAP_ITEM, Link);
+ RemoveEntryList (&Item->Link);
+ Map->Count--;
+ InsertHeadList (&Map->Recycled, &Item->Link);
+
+ if (Value != NULL) {
+ *Value = Item->Value;
+ }
+
+ return Item->Key;
+}
+
+
+/**
+ Iterate through the netmap and call CallBack for each item.
+
+ It will continue the traverse if CallBack returns EFI_SUCCESS, otherwise, break
+ from the loop. It returns the CallBack's last return value. This function is
+ delete safe for the current item.
+
+ If Map is NULL, then ASSERT().
+ If CallBack is NULL, then ASSERT().
+
+ @param[in] Map The Map to iterate through.
+ @param[in] CallBack The callback function to call for each item.
+ @param[in] Arg The opaque parameter to the callback.
+
+ @retval EFI_SUCCESS There is no item in the netmap or CallBack for each item
+ return EFI_SUCCESS.
+ @retval Others It returns the CallBack's last return value.
+
+**/
+EFI_STATUS
+EFIAPI
+NetMapIterate (
+ IN NET_MAP *Map,
+ IN NET_MAP_CALLBACK CallBack,
+ IN VOID *Arg OPTIONAL
+ )
+{
+
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ LIST_ENTRY *Head;
+ NET_MAP_ITEM *Item;
+ EFI_STATUS Result;
+
+ ASSERT ((Map != NULL) && (CallBack != NULL));
+
+ Head = &Map->Used;
+
+ if (IsListEmpty (Head)) {
+ return EFI_SUCCESS;
+ }
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {
+ Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);
+ Result = CallBack (Map, Item, Arg);
+
+ if (EFI_ERROR (Result)) {
+ return Result;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This is the default unload handle for all the network drivers.
+
+ Disconnect the driver specified by ImageHandle from all the devices in the handle database.
+ Uninstall all the protocols installed in the driver entry point.
+
+ @param[in] ImageHandle The drivers' driver image.
+
+ @retval EFI_SUCCESS The image is unloaded.
+ @retval Others Failed to unload the image.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibDefaultUnload (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *DeviceHandleBuffer;
+ UINTN DeviceHandleCount;
+ UINTN Index;
+ UINTN Index2;
+ EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;
+ EFI_COMPONENT_NAME_PROTOCOL *ComponentName;
+ EFI_COMPONENT_NAME2_PROTOCOL *ComponentName2;
+
+ //
+ // Get the list of all the handles in the handle database.
+ // If there is an error getting the list, then the unload
+ // operation fails.
+ //
+ Status = gBS->LocateHandleBuffer (
+ AllHandles,
+ NULL,
+ NULL,
+ &DeviceHandleCount,
+ &DeviceHandleBuffer
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ for (Index = 0; Index < DeviceHandleCount; Index++) {
+ Status = gBS->HandleProtocol (
+ DeviceHandleBuffer[Index],
+ &gEfiDriverBindingProtocolGuid,
+ (VOID **) &DriverBinding
+ );
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ if (DriverBinding->ImageHandle != ImageHandle) {
+ continue;
+ }
+
+ //
+ // Disconnect the driver specified by ImageHandle from all
+ // the devices in the handle database.
+ //
+ for (Index2 = 0; Index2 < DeviceHandleCount; Index2++) {
+ Status = gBS->DisconnectController (
+ DeviceHandleBuffer[Index2],
+ DriverBinding->DriverBindingHandle,
+ NULL
+ );
+ }
+
+ //
+ // Uninstall all the protocols installed in the driver entry point
+ //
+ gBS->UninstallProtocolInterface (
+ DriverBinding->DriverBindingHandle,
+ &gEfiDriverBindingProtocolGuid,
+ DriverBinding
+ );
+
+ Status = gBS->HandleProtocol (
+ DeviceHandleBuffer[Index],
+ &gEfiComponentNameProtocolGuid,
+ (VOID **) &ComponentName
+ );
+ if (!EFI_ERROR (Status)) {
+ gBS->UninstallProtocolInterface (
+ DriverBinding->DriverBindingHandle,
+ &gEfiComponentNameProtocolGuid,
+ ComponentName
+ );
+ }
+
+ Status = gBS->HandleProtocol (
+ DeviceHandleBuffer[Index],
+ &gEfiComponentName2ProtocolGuid,
+ (VOID **) &ComponentName2
+ );
+ if (!EFI_ERROR (Status)) {
+ gBS->UninstallProtocolInterface (
+ DriverBinding->DriverBindingHandle,
+ &gEfiComponentName2ProtocolGuid,
+ ComponentName2
+ );
+ }
+ }
+
+ //
+ // Free the buffer containing the list of handles from the handle database
+ //
+ if (DeviceHandleBuffer != NULL) {
+ gBS->FreePool (DeviceHandleBuffer);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Create a child of the service that is identified by ServiceBindingGuid.
+
+ Get the ServiceBinding Protocol first, then use it to create a child.
+
+ If ServiceBindingGuid is NULL, then ASSERT().
+ If ChildHandle is NULL, then ASSERT().
+
+ @param[in] Controller The controller which has the service installed.
+ @param[in] Image The image handle used to open service.
+ @param[in] ServiceBindingGuid The service's Guid.
+ @param[in, out] ChildHandle The handle to receive the create child.
+
+ @retval EFI_SUCCESS The child is successfully created.
+ @retval Others Failed to create the child.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibCreateServiceChild (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE Image,
+ IN EFI_GUID *ServiceBindingGuid,
+ IN OUT EFI_HANDLE *ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_SERVICE_BINDING_PROTOCOL *Service;
+
+
+ ASSERT ((ServiceBindingGuid != NULL) && (ChildHandle != NULL));
+
+ //
+ // Get the ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ ServiceBindingGuid,
+ (VOID **) &Service,
+ Image,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Create a child
+ //
+ Status = Service->CreateChild (Service, ChildHandle);
+ return Status;
+}
+
+
+/**
+ Destroy a child of the service that is identified by ServiceBindingGuid.
+
+ Get the ServiceBinding Protocol first, then use it to destroy a child.
+
+ If ServiceBindingGuid is NULL, then ASSERT().
+
+ @param[in] Controller The controller which has the service installed.
+ @param[in] Image The image handle used to open service.
+ @param[in] ServiceBindingGuid The service's Guid.
+ @param[in] ChildHandle The child to destroy.
+
+ @retval EFI_SUCCESS The child is successfully destroyed.
+ @retval Others Failed to destroy the child.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibDestroyServiceChild (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE Image,
+ IN EFI_GUID *ServiceBindingGuid,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_SERVICE_BINDING_PROTOCOL *Service;
+
+ ASSERT (ServiceBindingGuid != NULL);
+
+ //
+ // Get the ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ ServiceBindingGuid,
+ (VOID **) &Service,
+ Image,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // destroy the child
+ //
+ Status = Service->DestroyChild (Service, ChildHandle);
+ return Status;
+}
+
+/**
+ Get handle with Simple Network Protocol installed on it.
+
+ There should be MNP Service Binding Protocol installed on the input ServiceHandle.
+ If Simple Network Protocol is already installed on the ServiceHandle, the
+ ServiceHandle will be returned. If SNP is not installed on the ServiceHandle,
+ try to find its parent handle with SNP installed.
+
+ @param[in] ServiceHandle The handle where network service binding protocols are
+ installed on.
+ @param[out] Snp The pointer to store the address of the SNP instance.
+ This is an optional parameter that may be NULL.
+
+ @return The SNP handle, or NULL if not found.
+
+**/
+EFI_HANDLE
+EFIAPI
+NetLibGetSnpHandle (
+ IN EFI_HANDLE ServiceHandle,
+ OUT EFI_SIMPLE_NETWORK_PROTOCOL **Snp OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *SnpInstance;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_HANDLE SnpHandle;
+
+ //
+ // Try to open SNP from ServiceHandle
+ //
+ SnpInstance = NULL;
+ Status = gBS->HandleProtocol (ServiceHandle, &gEfiSimpleNetworkProtocolGuid, (VOID **) &SnpInstance);
+ if (!EFI_ERROR (Status)) {
+ if (Snp != NULL) {
+ *Snp = SnpInstance;
+ }
+ return ServiceHandle;
+ }
+
+ //
+ // Failed to open SNP, try to get SNP handle by LocateDevicePath()
+ //
+ DevicePath = DevicePathFromHandle (ServiceHandle);
+ if (DevicePath == NULL) {
+ return NULL;
+ }
+
+ SnpHandle = NULL;
+ Status = gBS->LocateDevicePath (&gEfiSimpleNetworkProtocolGuid, &DevicePath, &SnpHandle);
+ if (EFI_ERROR (Status)) {
+ //
+ // Failed to find SNP handle
+ //
+ return NULL;
+ }
+
+ Status = gBS->HandleProtocol (SnpHandle, &gEfiSimpleNetworkProtocolGuid, (VOID **) &SnpInstance);
+ if (!EFI_ERROR (Status)) {
+ if (Snp != NULL) {
+ *Snp = SnpInstance;
+ }
+ return SnpHandle;
+ }
+
+ return NULL;
+}
+
+/**
+ Retrieve VLAN ID of a VLAN device handle.
+
+ Search VLAN device path node in Device Path of specified ServiceHandle and
+ return its VLAN ID. If no VLAN device path node found, then this ServiceHandle
+ is not a VLAN device handle, and 0 will be returned.
+
+ @param[in] ServiceHandle The handle where network service binding protocols are
+ installed on.
+
+ @return VLAN ID of the device handle, or 0 if not a VLAN device.
+
+**/
+UINT16
+EFIAPI
+NetLibGetVlanId (
+ IN EFI_HANDLE ServiceHandle
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *Node;
+
+ DevicePath = DevicePathFromHandle (ServiceHandle);
+ if (DevicePath == NULL) {
+ return 0;
+ }
+
+ Node = DevicePath;
+ while (!IsDevicePathEnd (Node)) {
+ if (Node->Type == MESSAGING_DEVICE_PATH && Node->SubType == MSG_VLAN_DP) {
+ return ((VLAN_DEVICE_PATH *) Node)->VlanId;
+ }
+ Node = NextDevicePathNode (Node);
+ }
+
+ return 0;
+}
+
+/**
+ Find VLAN device handle with specified VLAN ID.
+
+ The VLAN child device handle is created by VLAN Config Protocol on ControllerHandle.
+ This function will append VLAN device path node to the parent device path,
+ and then use LocateDevicePath() to find the correct VLAN device handle.
+
+ @param[in] ControllerHandle The handle where network service binding protocols are
+ installed on.
+ @param[in] VlanId The configured VLAN ID for the VLAN device.
+
+ @return The VLAN device handle, or NULL if not found.
+
+**/
+EFI_HANDLE
+EFIAPI
+NetLibGetVlanHandle (
+ IN EFI_HANDLE ControllerHandle,
+ IN UINT16 VlanId
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *VlanDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ VLAN_DEVICE_PATH VlanNode;
+ EFI_HANDLE Handle;
+
+ ParentDevicePath = DevicePathFromHandle (ControllerHandle);
+ if (ParentDevicePath == NULL) {
+ return NULL;
+ }
+
+ //
+ // Construct VLAN device path
+ //
+ CopyMem (&VlanNode, &mNetVlanDevicePathTemplate, sizeof (VLAN_DEVICE_PATH));
+ VlanNode.VlanId = VlanId;
+ VlanDevicePath = AppendDevicePathNode (
+ ParentDevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *) &VlanNode
+ );
+ if (VlanDevicePath == NULL) {
+ return NULL;
+ }
+
+ //
+ // Find VLAN device handle
+ //
+ Handle = NULL;
+ DevicePath = VlanDevicePath;
+ gBS->LocateDevicePath (
+ &gEfiDevicePathProtocolGuid,
+ &DevicePath,
+ &Handle
+ );
+ if (!IsDevicePathEnd (DevicePath)) {
+ //
+ // Device path is not exactly match
+ //
+ Handle = NULL;
+ }
+
+ FreePool (VlanDevicePath);
+ return Handle;
+}
+
+/**
+ Get MAC address associated with the network service handle.
+
+ If MacAddress is NULL, then ASSERT().
+ If AddressSize is NULL, then ASSERT().
+
+ There should be MNP Service Binding Protocol installed on the input ServiceHandle.
+ If SNP is installed on the ServiceHandle or its parent handle, MAC address will
+ be retrieved from SNP. If no SNP found, try to get SNP mode data use MNP.
+
+ @param[in] ServiceHandle The handle where network service binding protocols are
+ installed on.
+ @param[out] MacAddress The pointer to store the returned MAC address.
+ @param[out] AddressSize The length of returned MAC address.
+
+ @retval EFI_SUCCESS MAC address is returned successfully.
+ @retval Others Failed to get SNP mode data.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibGetMacAddress (
+ IN EFI_HANDLE ServiceHandle,
+ OUT EFI_MAC_ADDRESS *MacAddress,
+ OUT UINTN *AddressSize
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ EFI_SIMPLE_NETWORK_MODE SnpModeData;
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+ EFI_SERVICE_BINDING_PROTOCOL *MnpSb;
+ EFI_HANDLE *SnpHandle;
+ EFI_HANDLE MnpChildHandle;
+
+ ASSERT (MacAddress != NULL);
+ ASSERT (AddressSize != NULL);
+
+ //
+ // Try to get SNP handle
+ //
+ Snp = NULL;
+ SnpHandle = NetLibGetSnpHandle (ServiceHandle, &Snp);
+ if (SnpHandle != NULL) {
+ //
+ // SNP found, use it directly
+ //
+ SnpMode = Snp->Mode;
+ } else {
+ //
+ // Failed to get SNP handle, try to get MAC address from MNP
+ //
+ MnpChildHandle = NULL;
+ Status = gBS->HandleProtocol (
+ ServiceHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ (VOID **) &MnpSb
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Create a MNP child
+ //
+ Status = MnpSb->CreateChild (MnpSb, &MnpChildHandle);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Open MNP protocol
+ //
+ Status = gBS->HandleProtocol (
+ MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ (VOID **) &Mnp
+ );
+ if (EFI_ERROR (Status)) {
+ MnpSb->DestroyChild (MnpSb, MnpChildHandle);
+ return Status;
+ }
+
+ //
+ // Try to get SNP mode from MNP
+ //
+ Status = Mnp->GetModeData (Mnp, NULL, &SnpModeData);
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_STARTED)) {
+ MnpSb->DestroyChild (MnpSb, MnpChildHandle);
+ return Status;
+ }
+ SnpMode = &SnpModeData;
+
+ //
+ // Destroy the MNP child
+ //
+ MnpSb->DestroyChild (MnpSb, MnpChildHandle);
+ }
+
+ *AddressSize = SnpMode->HwAddressSize;
+ CopyMem (MacAddress->Addr, SnpMode->CurrentAddress.Addr, SnpMode->HwAddressSize);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Convert MAC address of the NIC associated with specified Service Binding Handle
+ to a unicode string. Callers are responsible for freeing the string storage.
+
+ If MacString is NULL, then ASSERT().
+
+ Locate simple network protocol associated with the Service Binding Handle and
+ get the mac address from SNP. Then convert the mac address into a unicode
+ string. It takes 2 unicode characters to represent a 1 byte binary buffer.
+ Plus one unicode character for the null-terminator.
+
+ @param[in] ServiceHandle The handle where network service binding protocol is
+ installed on.
+ @param[in] ImageHandle The image handle used to act as the agent handle to
+ get the simple network protocol. This parameter is
+ optional and may be NULL.
+ @param[out] MacString The pointer to store the address of the string
+ representation of the mac address.
+
+ @retval EFI_SUCCESS Convert the mac address a unicode string successfully.
+ @retval EFI_OUT_OF_RESOURCES There are not enough memory resource.
+ @retval Others Failed to open the simple network protocol.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibGetMacString (
+ IN EFI_HANDLE ServiceHandle,
+ IN EFI_HANDLE ImageHandle, OPTIONAL
+ OUT CHAR16 **MacString
+ )
+{
+ EFI_STATUS Status;
+ EFI_MAC_ADDRESS MacAddress;
+ UINT8 *HwAddress;
+ UINTN HwAddressSize;
+ UINT16 VlanId;
+ CHAR16 *String;
+ UINTN Index;
+ UINTN BufferSize;
+
+ ASSERT (MacString != NULL);
+
+ //
+ // Get MAC address of the network device
+ //
+ Status = NetLibGetMacAddress (ServiceHandle, &MacAddress, &HwAddressSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // It takes 2 unicode characters to represent a 1 byte binary buffer.
+ // If VLAN is configured, it will need extra 5 characters like "\0005".
+ // Plus one unicode character for the null-terminator.
+ //
+ BufferSize = (2 * HwAddressSize + 5 + 1) * sizeof (CHAR16);
+ String = AllocateZeroPool (BufferSize);
+ if (String == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ *MacString = String;
+
+ //
+ // Convert the MAC address into a unicode string.
+ //
+ HwAddress = &MacAddress.Addr[0];
+ for (Index = 0; Index < HwAddressSize; Index++) {
+ UnicodeValueToStringS (
+ String,
+ BufferSize - ((UINTN)String - (UINTN)*MacString),
+ PREFIX_ZERO | RADIX_HEX,
+ *(HwAddress++),
+ 2
+ );
+ String += StrnLenS (String, (BufferSize - ((UINTN)String - (UINTN)*MacString)) / sizeof (CHAR16));
+ }
+
+ //
+ // Append VLAN ID if any
+ //
+ VlanId = NetLibGetVlanId (ServiceHandle);
+ if (VlanId != 0) {
+ *String++ = L'\\';
+ UnicodeValueToStringS (
+ String,
+ BufferSize - ((UINTN)String - (UINTN)*MacString),
+ PREFIX_ZERO | RADIX_HEX,
+ VlanId,
+ 4
+ );
+ String += StrnLenS (String, (BufferSize - ((UINTN)String - (UINTN)*MacString)) / sizeof (CHAR16));
+ }
+
+ //
+ // Null terminate the Unicode string
+ //
+ *String = L'\0';
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Detect media status for specified network device.
+
+ If MediaPresent is NULL, then ASSERT().
+
+ The underlying UNDI driver may or may not support reporting media status from
+ GET_STATUS command (PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED). This routine
+ will try to invoke Snp->GetStatus() to get the media status: if media already
+ present, it return directly; if media not present, it will stop SNP and then
+ restart SNP to get the latest media status, this give chance to get the correct
+ media status for old UNDI driver which doesn't support reporting media status
+ from GET_STATUS command.
+ Note: there will be two limitations for current algorithm:
+ 1) for UNDI with this capability, in case of cable is not attached, there will
+ be an redundant Stop/Start() process;
+ 2) for UNDI without this capability, in case that network cable is attached when
+ Snp->Initialize() is invoked while network cable is unattached later,
+ NetLibDetectMedia() will report MediaPresent as TRUE, causing upper layer
+ apps to wait for timeout time.
+
+ @param[in] ServiceHandle The handle where network service binding protocols are
+ installed on.
+ @param[out] MediaPresent The pointer to store the media status.
+
+ @retval EFI_SUCCESS Media detection success.
+ @retval EFI_INVALID_PARAMETER ServiceHandle is not valid network device handle.
+ @retval EFI_UNSUPPORTED Network device does not support media detection.
+ @retval EFI_DEVICE_ERROR SNP is in unknown state.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibDetectMedia (
+ IN EFI_HANDLE ServiceHandle,
+ OUT BOOLEAN *MediaPresent
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE SnpHandle;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ UINT32 InterruptStatus;
+ UINT32 OldState;
+ EFI_MAC_ADDRESS *MCastFilter;
+ UINT32 MCastFilterCount;
+ UINT32 EnableFilterBits;
+ UINT32 DisableFilterBits;
+ BOOLEAN ResetMCastFilters;
+
+ ASSERT (MediaPresent != NULL);
+
+ //
+ // Get SNP handle
+ //
+ Snp = NULL;
+ SnpHandle = NetLibGetSnpHandle (ServiceHandle, &Snp);
+ if (SnpHandle == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check whether SNP support media detection
+ //
+ if (!Snp->Mode->MediaPresentSupported) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data
+ //
+ Status = Snp->GetStatus (Snp, &InterruptStatus, NULL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Snp->Mode->MediaPresent) {
+ //
+ // Media is present, return directly
+ //
+ *MediaPresent = TRUE;
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Till now, GetStatus() report no media; while, in case UNDI not support
+ // reporting media status from GetStatus(), this media status may be incorrect.
+ // So, we will stop SNP and then restart it to get the correct media status.
+ //
+ OldState = Snp->Mode->State;
+ if (OldState >= EfiSimpleNetworkMaxState) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ MCastFilter = NULL;
+
+ if (OldState == EfiSimpleNetworkInitialized) {
+ //
+ // SNP is already in use, need Shutdown/Stop and then Start/Initialize
+ //
+
+ //
+ // Backup current SNP receive filter settings
+ //
+ EnableFilterBits = Snp->Mode->ReceiveFilterSetting;
+ DisableFilterBits = Snp->Mode->ReceiveFilterMask ^ EnableFilterBits;
+
+ ResetMCastFilters = TRUE;
+ MCastFilterCount = Snp->Mode->MCastFilterCount;
+ if (MCastFilterCount != 0) {
+ MCastFilter = AllocateCopyPool (
+ MCastFilterCount * sizeof (EFI_MAC_ADDRESS),
+ Snp->Mode->MCastFilter
+ );
+ ASSERT (MCastFilter != NULL);
+ if (MCastFilter == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ ResetMCastFilters = FALSE;
+ }
+
+ //
+ // Shutdown/Stop the simple network
+ //
+ Status = Snp->Shutdown (Snp);
+ if (!EFI_ERROR (Status)) {
+ Status = Snp->Stop (Snp);
+ }
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Start/Initialize the simple network
+ //
+ Status = Snp->Start (Snp);
+ if (!EFI_ERROR (Status)) {
+ Status = Snp->Initialize (Snp, 0, 0);
+ }
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Here we get the correct media status
+ //
+ *MediaPresent = Snp->Mode->MediaPresent;
+
+ //
+ // Restore SNP receive filter settings
+ //
+ Status = Snp->ReceiveFilters (
+ Snp,
+ EnableFilterBits,
+ DisableFilterBits,
+ ResetMCastFilters,
+ MCastFilterCount,
+ MCastFilter
+ );
+
+ if (MCastFilter != NULL) {
+ FreePool (MCastFilter);
+ }
+
+ return Status;
+ }
+
+ //
+ // SNP is not in use, it's in state of EfiSimpleNetworkStopped or EfiSimpleNetworkStarted
+ //
+ if (OldState == EfiSimpleNetworkStopped) {
+ //
+ // SNP not start yet, start it
+ //
+ Status = Snp->Start (Snp);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+ }
+
+ //
+ // Initialize the simple network
+ //
+ Status = Snp->Initialize (Snp, 0, 0);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ //
+ // Here we get the correct media status
+ //
+ *MediaPresent = Snp->Mode->MediaPresent;
+
+ //
+ // Shut down the simple network
+ //
+ Snp->Shutdown (Snp);
+
+Exit:
+ if (OldState == EfiSimpleNetworkStopped) {
+ //
+ // Original SNP sate is Stopped, restore to original state
+ //
+ Snp->Stop (Snp);
+ }
+
+ if (MCastFilter != NULL) {
+ FreePool (MCastFilter);
+ }
+
+ return Status;
+}
+
+/**
+
+ Detect media state for a network device. This routine will wait for a period of time at
+ a specified checking interval when a certain network is under connecting until connection
+ process finishs or timeout. If Aip protocol is supported by low layer drivers, three kinds
+ of media states can be detected: EFI_SUCCESS, EFI_NOT_READY and EFI_NO_MEDIA, represents
+ connected state, connecting state and no media state respectively. When function detects
+ the current state is EFI_NOT_READY, it will loop to wait for next time's check until state
+ turns to be EFI_SUCCESS or EFI_NO_MEDIA. If Aip protocol is not supported, function will
+ call NetLibDetectMedia() and return state directly.
+
+ @param[in] ServiceHandle The handle where network service binding protocols are
+ installed on.
+ @param[in] Timeout The maximum number of 100ns units to wait when network
+ is connecting. Zero value means detect once and return
+ immediately.
+ @param[out] MediaState The pointer to the detected media state.
+
+ @retval EFI_SUCCESS Media detection success.
+ @retval EFI_INVALID_PARAMETER ServiceHandle is not a valid network device handle or
+ MediaState pointer is NULL.
+ @retval EFI_DEVICE_ERROR A device error occurred.
+ @retval EFI_TIMEOUT Network is connecting but timeout.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibDetectMediaWaitTimeout (
+ IN EFI_HANDLE ServiceHandle,
+ IN UINT64 Timeout,
+ OUT EFI_STATUS *MediaState
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE SnpHandle;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_ADAPTER_INFORMATION_PROTOCOL *Aip;
+ EFI_ADAPTER_INFO_MEDIA_STATE *MediaInfo;
+ BOOLEAN MediaPresent;
+ UINTN DataSize;
+ EFI_STATUS TimerStatus;
+ EFI_EVENT Timer;
+ UINT64 TimeRemained;
+
+ if (MediaState == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ *MediaState = EFI_SUCCESS;
+ MediaInfo = NULL;
+
+ //
+ // Get SNP handle
+ //
+ Snp = NULL;
+ SnpHandle = NetLibGetSnpHandle (ServiceHandle, &Snp);
+ if (SnpHandle == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = gBS->HandleProtocol (
+ SnpHandle,
+ &gEfiAdapterInformationProtocolGuid,
+ (VOID *) &Aip
+ );
+ if (EFI_ERROR (Status)) {
+
+ MediaPresent = TRUE;
+ Status = NetLibDetectMedia (ServiceHandle, &MediaPresent);
+ if (!EFI_ERROR (Status)) {
+ if (MediaPresent) {
+ *MediaState = EFI_SUCCESS;
+ } else {
+ *MediaState = EFI_NO_MEDIA;
+ }
+ }
+
+ //
+ // NetLibDetectMedia doesn't support EFI_NOT_READY status, return now!
+ //
+ return Status;
+ }
+
+ Status = Aip->GetInformation (
+ Aip,
+ &gEfiAdapterInfoMediaStateGuid,
+ (VOID **) &MediaInfo,
+ &DataSize
+ );
+ if (!EFI_ERROR (Status)) {
+
+ *MediaState = MediaInfo->MediaState;
+ FreePool (MediaInfo);
+ if (*MediaState != EFI_NOT_READY || Timeout < MEDIA_STATE_DETECT_TIME_INTERVAL) {
+
+ return EFI_SUCCESS;
+ }
+ } else {
+
+ if (MediaInfo != NULL) {
+ FreePool (MediaInfo);
+ }
+
+ if (Status == EFI_UNSUPPORTED) {
+
+ //
+ // If gEfiAdapterInfoMediaStateGuid is not supported, call NetLibDetectMedia to get media state!
+ //
+ MediaPresent = TRUE;
+ Status = NetLibDetectMedia (ServiceHandle, &MediaPresent);
+ if (!EFI_ERROR (Status)) {
+ if (MediaPresent) {
+ *MediaState = EFI_SUCCESS;
+ } else {
+ *MediaState = EFI_NO_MEDIA;
+ }
+ }
+ return Status;
+ }
+
+ return Status;
+ }
+
+ //
+ // Loop to check media state
+ //
+
+ Timer = NULL;
+ TimeRemained = Timeout;
+ Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ do {
+ Status = gBS->SetTimer (
+ Timer,
+ TimerRelative,
+ MEDIA_STATE_DETECT_TIME_INTERVAL
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent(Timer);
+ return EFI_DEVICE_ERROR;
+ }
+
+ do {
+ TimerStatus = gBS->CheckEvent (Timer);
+ if (!EFI_ERROR (TimerStatus)) {
+
+ TimeRemained -= MEDIA_STATE_DETECT_TIME_INTERVAL;
+ Status = Aip->GetInformation (
+ Aip,
+ &gEfiAdapterInfoMediaStateGuid,
+ (VOID **) &MediaInfo,
+ &DataSize
+ );
+ if (!EFI_ERROR (Status)) {
+
+ *MediaState = MediaInfo->MediaState;
+ FreePool (MediaInfo);
+ } else {
+
+ if (MediaInfo != NULL) {
+ FreePool (MediaInfo);
+ }
+ gBS->CloseEvent(Timer);
+ return Status;
+ }
+ }
+ } while (TimerStatus == EFI_NOT_READY);
+ } while (*MediaState == EFI_NOT_READY && TimeRemained >= MEDIA_STATE_DETECT_TIME_INTERVAL);
+
+ gBS->CloseEvent(Timer);
+ if (*MediaState == EFI_NOT_READY && TimeRemained < MEDIA_STATE_DETECT_TIME_INTERVAL) {
+ return EFI_TIMEOUT;
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+/**
+ Check the default address used by the IPv4 driver is static or dynamic (acquired
+ from DHCP).
+
+ If the controller handle does not have the EFI_IP4_CONFIG2_PROTOCOL installed, the
+ default address is static. If failed to get the policy from Ip4 Config2 Protocol,
+ the default address is static. Otherwise, get the result from Ip4 Config2 Protocol.
+
+ @param[in] Controller The controller handle which has the EFI_IP4_CONFIG2_PROTOCOL
+ relative with the default address to judge.
+
+ @retval TRUE If the default address is static.
+ @retval FALSE If the default address is acquired from DHCP.
+
+**/
+BOOLEAN
+NetLibDefaultAddressIsStatic (
+ IN EFI_HANDLE Controller
+ )
+{
+ EFI_STATUS Status;
+ EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2;
+ UINTN DataSize;
+ EFI_IP4_CONFIG2_POLICY Policy;
+ BOOLEAN IsStatic;
+
+ Ip4Config2 = NULL;
+
+ DataSize = sizeof (EFI_IP4_CONFIG2_POLICY);
+
+ IsStatic = TRUE;
+
+ //
+ // Get Ip4Config2 policy.
+ //
+ Status = gBS->HandleProtocol (Controller, &gEfiIp4Config2ProtocolGuid, (VOID **) &Ip4Config2);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypePolicy, &DataSize, &Policy);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ IsStatic = (BOOLEAN) (Policy == Ip4Config2PolicyStatic);
+
+ON_EXIT:
+
+ return IsStatic;
+}
+
+/**
+ Create an IPv4 device path node.
+
+ If Node is NULL, then ASSERT().
+
+ The header type of IPv4 device path node is MESSAGING_DEVICE_PATH.
+ The header subtype of IPv4 device path node is MSG_IPv4_DP.
+ Get other info from parameters to make up the whole IPv4 device path node.
+
+ @param[in, out] Node Pointer to the IPv4 device path node.
+ @param[in] Controller The controller handle.
+ @param[in] LocalIp The local IPv4 address.
+ @param[in] LocalPort The local port.
+ @param[in] RemoteIp The remote IPv4 address.
+ @param[in] RemotePort The remote port.
+ @param[in] Protocol The protocol type in the IP header.
+ @param[in] UseDefaultAddress Whether this instance is using default address or not.
+
+**/
+VOID
+EFIAPI
+NetLibCreateIPv4DPathNode (
+ IN OUT IPv4_DEVICE_PATH *Node,
+ IN EFI_HANDLE Controller,
+ IN IP4_ADDR LocalIp,
+ IN UINT16 LocalPort,
+ IN IP4_ADDR RemoteIp,
+ IN UINT16 RemotePort,
+ IN UINT16 Protocol,
+ IN BOOLEAN UseDefaultAddress
+ )
+{
+ ASSERT (Node != NULL);
+
+ Node->Header.Type = MESSAGING_DEVICE_PATH;
+ Node->Header.SubType = MSG_IPv4_DP;
+ SetDevicePathNodeLength (&Node->Header, sizeof (IPv4_DEVICE_PATH));
+
+ CopyMem (&Node->LocalIpAddress, &LocalIp, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Node->RemoteIpAddress, &RemoteIp, sizeof (EFI_IPv4_ADDRESS));
+
+ Node->LocalPort = LocalPort;
+ Node->RemotePort = RemotePort;
+
+ Node->Protocol = Protocol;
+
+ if (!UseDefaultAddress) {
+ Node->StaticIpAddress = TRUE;
+ } else {
+ Node->StaticIpAddress = NetLibDefaultAddressIsStatic (Controller);
+ }
+
+ //
+ // Set the Gateway IP address to default value 0:0:0:0.
+ // Set the Subnet mask to default value 255:255:255:0.
+ //
+ ZeroMem (&Node->GatewayIpAddress, sizeof (EFI_IPv4_ADDRESS));
+ SetMem (&Node->SubnetMask, sizeof (EFI_IPv4_ADDRESS), 0xff);
+ Node->SubnetMask.Addr[3] = 0;
+}
+
+/**
+ Create an IPv6 device path node.
+
+ If Node is NULL, then ASSERT().
+ If LocalIp is NULL, then ASSERT().
+ If RemoteIp is NULL, then ASSERT().
+
+ The header type of IPv6 device path node is MESSAGING_DEVICE_PATH.
+ The header subtype of IPv6 device path node is MSG_IPv6_DP.
+ Get other info from parameters to make up the whole IPv6 device path node.
+
+ @param[in, out] Node Pointer to the IPv6 device path node.
+ @param[in] Controller The controller handle.
+ @param[in] LocalIp The local IPv6 address.
+ @param[in] LocalPort The local port.
+ @param[in] RemoteIp The remote IPv6 address.
+ @param[in] RemotePort The remote port.
+ @param[in] Protocol The protocol type in the IP header.
+
+**/
+VOID
+EFIAPI
+NetLibCreateIPv6DPathNode (
+ IN OUT IPv6_DEVICE_PATH *Node,
+ IN EFI_HANDLE Controller,
+ IN EFI_IPv6_ADDRESS *LocalIp,
+ IN UINT16 LocalPort,
+ IN EFI_IPv6_ADDRESS *RemoteIp,
+ IN UINT16 RemotePort,
+ IN UINT16 Protocol
+ )
+{
+ ASSERT (Node != NULL && LocalIp != NULL && RemoteIp != NULL);
+
+ Node->Header.Type = MESSAGING_DEVICE_PATH;
+ Node->Header.SubType = MSG_IPv6_DP;
+ SetDevicePathNodeLength (&Node->Header, sizeof (IPv6_DEVICE_PATH));
+
+ CopyMem (&Node->LocalIpAddress, LocalIp, sizeof (EFI_IPv6_ADDRESS));
+ CopyMem (&Node->RemoteIpAddress, RemoteIp, sizeof (EFI_IPv6_ADDRESS));
+
+ Node->LocalPort = LocalPort;
+ Node->RemotePort = RemotePort;
+
+ Node->Protocol = Protocol;
+
+ //
+ // Set default value to IPAddressOrigin, PrefixLength.
+ // Set the Gateway IP address to unspecified address.
+ //
+ Node->IpAddressOrigin = 0;
+ Node->PrefixLength = IP6_PREFIX_LENGTH;
+ ZeroMem (&Node->GatewayIpAddress, sizeof (EFI_IPv6_ADDRESS));
+}
+
+/**
+ Find the UNDI/SNP handle from controller and protocol GUID.
+
+ If ProtocolGuid is NULL, then ASSERT().
+
+ For example, IP will open a MNP child to transmit/receive
+ packets, when MNP is stopped, IP should also be stopped. IP
+ needs to find its own private data which is related the IP's
+ service binding instance that is install on UNDI/SNP handle.
+ Now, the controller is either a MNP or ARP child handle. But
+ IP opens these handle BY_DRIVER, use that info, we can get the
+ UNDI/SNP handle.
+
+ @param[in] Controller Then protocol handle to check.
+ @param[in] ProtocolGuid The protocol that is related with the handle.
+
+ @return The UNDI/SNP handle or NULL for errors.
+
+**/
+EFI_HANDLE
+EFIAPI
+NetLibGetNicHandle (
+ IN EFI_HANDLE Controller,
+ IN EFI_GUID *ProtocolGuid
+ )
+{
+ EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenBuffer;
+ EFI_HANDLE Handle;
+ EFI_STATUS Status;
+ UINTN OpenCount;
+ UINTN Index;
+
+ ASSERT (ProtocolGuid != NULL);
+
+ Status = gBS->OpenProtocolInformation (
+ Controller,
+ ProtocolGuid,
+ &OpenBuffer,
+ &OpenCount
+ );
+
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ Handle = NULL;
+
+ for (Index = 0; Index < OpenCount; Index++) {
+ if ((OpenBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) != 0) {
+ Handle = OpenBuffer[Index].ControllerHandle;
+ break;
+ }
+ }
+
+ gBS->FreePool (OpenBuffer);
+ return Handle;
+}
+
+/**
+ Convert one Null-terminated ASCII string (decimal dotted) to EFI_IPv4_ADDRESS.
+
+ @param[in] String The pointer to the Ascii string.
+ @param[out] Ip4Address The pointer to the converted IPv4 address.
+
+ @retval EFI_SUCCESS Convert to IPv4 address successfully.
+ @retval EFI_INVALID_PARAMETER The string is mal-formated or Ip4Address is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibAsciiStrToIp4 (
+ IN CONST CHAR8 *String,
+ OUT EFI_IPv4_ADDRESS *Ip4Address
+ )
+{
+ RETURN_STATUS Status;
+ CHAR8 *EndPointer;
+
+ Status = AsciiStrToIpv4Address (String, &EndPointer, Ip4Address, NULL);
+ if (RETURN_ERROR (Status) || (*EndPointer != '\0')) {
+ return EFI_INVALID_PARAMETER;
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+
+/**
+ Convert one Null-terminated ASCII string to EFI_IPv6_ADDRESS. The format of the
+ string is defined in RFC 4291 - Text Representation of Addresses.
+
+ @param[in] String The pointer to the Ascii string.
+ @param[out] Ip6Address The pointer to the converted IPv6 address.
+
+ @retval EFI_SUCCESS Convert to IPv6 address successfully.
+ @retval EFI_INVALID_PARAMETER The string is mal-formated or Ip6Address is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibAsciiStrToIp6 (
+ IN CONST CHAR8 *String,
+ OUT EFI_IPv6_ADDRESS *Ip6Address
+ )
+{
+ RETURN_STATUS Status;
+ CHAR8 *EndPointer;
+
+ Status = AsciiStrToIpv6Address (String, &EndPointer, Ip6Address, NULL);
+ if (RETURN_ERROR (Status) || (*EndPointer != '\0')) {
+ return EFI_INVALID_PARAMETER;
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+
+/**
+ Convert one Null-terminated Unicode string (decimal dotted) to EFI_IPv4_ADDRESS.
+
+ @param[in] String The pointer to the Ascii string.
+ @param[out] Ip4Address The pointer to the converted IPv4 address.
+
+ @retval EFI_SUCCESS Convert to IPv4 address successfully.
+ @retval EFI_INVALID_PARAMETER The string is mal-formated or Ip4Address is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibStrToIp4 (
+ IN CONST CHAR16 *String,
+ OUT EFI_IPv4_ADDRESS *Ip4Address
+ )
+{
+ RETURN_STATUS Status;
+ CHAR16 *EndPointer;
+
+ Status = StrToIpv4Address (String, &EndPointer, Ip4Address, NULL);
+ if (RETURN_ERROR (Status) || (*EndPointer != L'\0')) {
+ return EFI_INVALID_PARAMETER;
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+
+/**
+ Convert one Null-terminated Unicode string to EFI_IPv6_ADDRESS. The format of
+ the string is defined in RFC 4291 - Text Representation of Addresses.
+
+ @param[in] String The pointer to the Ascii string.
+ @param[out] Ip6Address The pointer to the converted IPv6 address.
+
+ @retval EFI_SUCCESS Convert to IPv6 address successfully.
+ @retval EFI_INVALID_PARAMETER The string is mal-formated or Ip6Address is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibStrToIp6 (
+ IN CONST CHAR16 *String,
+ OUT EFI_IPv6_ADDRESS *Ip6Address
+ )
+{
+ RETURN_STATUS Status;
+ CHAR16 *EndPointer;
+
+ Status = StrToIpv6Address (String, &EndPointer, Ip6Address, NULL);
+ if (RETURN_ERROR (Status) || (*EndPointer != L'\0')) {
+ return EFI_INVALID_PARAMETER;
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+/**
+ Convert one Null-terminated Unicode string to EFI_IPv6_ADDRESS and prefix length.
+ The format of the string is defined in RFC 4291 - Text Representation of Addresses
+ Prefixes: ipv6-address/prefix-length.
+
+ @param[in] String The pointer to the Ascii string.
+ @param[out] Ip6Address The pointer to the converted IPv6 address.
+ @param[out] PrefixLength The pointer to the converted prefix length.
+
+ @retval EFI_SUCCESS Convert to IPv6 address successfully.
+ @retval EFI_INVALID_PARAMETER The string is mal-formated or Ip6Address is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibStrToIp6andPrefix (
+ IN CONST CHAR16 *String,
+ OUT EFI_IPv6_ADDRESS *Ip6Address,
+ OUT UINT8 *PrefixLength
+ )
+{
+ RETURN_STATUS Status;
+ CHAR16 *EndPointer;
+
+ Status = StrToIpv6Address (String, &EndPointer, Ip6Address, PrefixLength);
+ if (RETURN_ERROR (Status) || (*EndPointer != L'\0')) {
+ return EFI_INVALID_PARAMETER;
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+/**
+
+ Convert one EFI_IPv6_ADDRESS to Null-terminated Unicode string.
+ The text representation of address is defined in RFC 4291.
+
+ @param[in] Ip6Address The pointer to the IPv6 address.
+ @param[out] String The buffer to return the converted string.
+ @param[in] StringSize The length in bytes of the input String.
+
+ @retval EFI_SUCCESS Convert to string successfully.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+ @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small for the result. BufferSize has been
+ updated with the size needed to complete the request.
+**/
+EFI_STATUS
+EFIAPI
+NetLibIp6ToStr (
+ IN EFI_IPv6_ADDRESS *Ip6Address,
+ OUT CHAR16 *String,
+ IN UINTN StringSize
+ )
+{
+ UINT16 Ip6Addr[8];
+ UINTN Index;
+ UINTN LongestZerosStart;
+ UINTN LongestZerosLength;
+ UINTN CurrentZerosStart;
+ UINTN CurrentZerosLength;
+ CHAR16 Buffer[sizeof"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"];
+ CHAR16 *Ptr;
+
+ if (Ip6Address == NULL || String == NULL || StringSize == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Convert the UINT8 array to an UINT16 array for easy handling.
+ //
+ ZeroMem (Ip6Addr, sizeof (Ip6Addr));
+ for (Index = 0; Index < 16; Index++) {
+ Ip6Addr[Index / 2] |= (Ip6Address->Addr[Index] << ((1 - (Index % 2)) << 3));
+ }
+
+ //
+ // Find the longest zeros and mark it.
+ //
+ CurrentZerosStart = DEFAULT_ZERO_START;
+ CurrentZerosLength = 0;
+ LongestZerosStart = DEFAULT_ZERO_START;
+ LongestZerosLength = 0;
+ for (Index = 0; Index < 8; Index++) {
+ if (Ip6Addr[Index] == 0) {
+ if (CurrentZerosStart == DEFAULT_ZERO_START) {
+ CurrentZerosStart = Index;
+ CurrentZerosLength = 1;
+ } else {
+ CurrentZerosLength++;
+ }
+ } else {
+ if (CurrentZerosStart != DEFAULT_ZERO_START) {
+ if (CurrentZerosLength > 2 && (LongestZerosStart == (DEFAULT_ZERO_START) || CurrentZerosLength > LongestZerosLength)) {
+ LongestZerosStart = CurrentZerosStart;
+ LongestZerosLength = CurrentZerosLength;
+ }
+ CurrentZerosStart = DEFAULT_ZERO_START;
+ CurrentZerosLength = 0;
+ }
+ }
+ }
+
+ if (CurrentZerosStart != DEFAULT_ZERO_START && CurrentZerosLength > 2) {
+ if (LongestZerosStart == DEFAULT_ZERO_START || LongestZerosLength < CurrentZerosLength) {
+ LongestZerosStart = CurrentZerosStart;
+ LongestZerosLength = CurrentZerosLength;
+ }
+ }
+
+ Ptr = Buffer;
+ for (Index = 0; Index < 8; Index++) {
+ if (LongestZerosStart != DEFAULT_ZERO_START && Index >= LongestZerosStart && Index < LongestZerosStart + LongestZerosLength) {
+ if (Index == LongestZerosStart) {
+ *Ptr++ = L':';
+ }
+ continue;
+ }
+ if (Index != 0) {
+ *Ptr++ = L':';
+ }
+ Ptr += UnicodeSPrint(Ptr, 10, L"%x", Ip6Addr[Index]);
+ }
+
+ if (LongestZerosStart != DEFAULT_ZERO_START && LongestZerosStart + LongestZerosLength == 8) {
+ *Ptr++ = L':';
+ }
+ *Ptr = L'\0';
+
+ if ((UINTN)Ptr - (UINTN)Buffer > StringSize) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ StrCpyS (String, StringSize / sizeof (CHAR16), Buffer);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function obtains the system guid from the smbios table.
+
+ If SystemGuid is NULL, then ASSERT().
+
+ @param[out] SystemGuid The pointer of the returned system guid.
+
+ @retval EFI_SUCCESS Successfully obtained the system guid.
+ @retval EFI_NOT_FOUND Did not find the SMBIOS table.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibGetSystemGuid (
+ OUT EFI_GUID *SystemGuid
+ )
+{
+ EFI_STATUS Status;
+ SMBIOS_TABLE_ENTRY_POINT *SmbiosTable;
+ SMBIOS_TABLE_3_0_ENTRY_POINT *Smbios30Table;
+ SMBIOS_STRUCTURE_POINTER Smbios;
+ SMBIOS_STRUCTURE_POINTER SmbiosEnd;
+ CHAR8 *String;
+
+ ASSERT (SystemGuid != NULL);
+
+ SmbiosTable = NULL;
+ Status = EfiGetSystemConfigurationTable (&gEfiSmbios3TableGuid, (VOID **) &Smbios30Table);
+ if (!(EFI_ERROR (Status) || Smbios30Table == NULL)) {
+ Smbios.Hdr = (SMBIOS_STRUCTURE *) (UINTN) Smbios30Table->TableAddress;
+ SmbiosEnd.Raw = (UINT8 *) (UINTN) (Smbios30Table->TableAddress + Smbios30Table->TableMaximumSize);
+ } else {
+ Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SmbiosTable);
+ if (EFI_ERROR (Status) || SmbiosTable == NULL) {
+ return EFI_NOT_FOUND;
+ }
+ Smbios.Hdr = (SMBIOS_STRUCTURE *) (UINTN) SmbiosTable->TableAddress;
+ SmbiosEnd.Raw = (UINT8 *) ((UINTN) SmbiosTable->TableAddress + SmbiosTable->TableLength);
+ }
+
+ do {
+ if (Smbios.Hdr->Type == 1) {
+ if (Smbios.Hdr->Length < 0x19) {
+ //
+ // Older version did not support UUID.
+ //
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // SMBIOS tables are byte packed so we need to do a byte copy to
+ // prevend alignment faults on Itanium-based platform.
+ //
+ CopyMem (SystemGuid, &Smbios.Type1->Uuid, sizeof (EFI_GUID));
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Go to the next SMBIOS structure. Each SMBIOS structure may include 2 parts:
+ // 1. Formatted section; 2. Unformatted string section. So, 2 steps are needed
+ // to skip one SMBIOS structure.
+ //
+
+ //
+ // Step 1: Skip over formatted section.
+ //
+ String = (CHAR8 *) (Smbios.Raw + Smbios.Hdr->Length);
+
+ //
+ // Step 2: Skip over unformated string section.
+ //
+ do {
+ //
+ // Each string is terminated with a NULL(00h) BYTE and the sets of strings
+ // is terminated with an additional NULL(00h) BYTE.
+ //
+ for ( ; *String != 0; String++) {
+ }
+
+ if (*(UINT8*)++String == 0) {
+ //
+ // Pointer to the next SMBIOS structure.
+ //
+ Smbios.Raw = (UINT8 *)++String;
+ break;
+ }
+ } while (TRUE);
+ } while (Smbios.Raw < SmbiosEnd.Raw);
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Create Dns QName according the queried domain name.
+
+ If DomainName is NULL, then ASSERT().
+
+ QName is a domain name represented as a sequence of labels,
+ where each label consists of a length octet followed by that
+ number of octets. The QName terminates with the zero
+ length octet for the null label of the root. Caller should
+ take responsibility to free the buffer in returned pointer.
+
+ @param DomainName The pointer to the queried domain name string.
+
+ @retval NULL Failed to fill QName.
+ @return QName filled successfully.
+
+**/
+CHAR8 *
+EFIAPI
+NetLibCreateDnsQName (
+ IN CHAR16 *DomainName
+ )
+{
+ CHAR8 *QueryName;
+ UINTN QueryNameSize;
+ CHAR8 *Header;
+ CHAR8 *Tail;
+ UINTN Len;
+ UINTN Index;
+
+ ASSERT (DomainName != NULL);
+
+ QueryName = NULL;
+ QueryNameSize = 0;
+ Header = NULL;
+ Tail = NULL;
+
+ //
+ // One byte for first label length, one byte for terminated length zero.
+ //
+ QueryNameSize = StrLen (DomainName) + 2;
+
+ if (QueryNameSize > DNS_MAX_NAME_SIZE) {
+ return NULL;
+ }
+
+ QueryName = AllocateZeroPool (QueryNameSize);
+ if (QueryName == NULL) {
+ return NULL;
+ }
+
+ Header = QueryName;
+ Tail = Header + 1;
+ Len = 0;
+ for (Index = 0; DomainName[Index] != 0; Index++) {
+ *Tail = (CHAR8) DomainName[Index];
+ if (*Tail == '.') {
+ *Header = (CHAR8) Len;
+ Header = Tail;
+ Tail ++;
+ Len = 0;
+ } else {
+ Tail++;
+ Len++;
+ }
+ }
+ *Header = (CHAR8) Len;
+ *Tail = 0;
+
+ return QueryName;
+}
diff --git a/NetworkPkg/Library/DxeNetLib/DxeNetLib.inf b/NetworkPkg/Library/DxeNetLib/DxeNetLib.inf
new file mode 100644
index 0000000000..35a8247517
--- /dev/null
+++ b/NetworkPkg/Library/DxeNetLib/DxeNetLib.inf
@@ -0,0 +1,61 @@
+## @file
+# This library instance provides the basic network services.
+#
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+# (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxeNetLib
+ MODULE_UNI_FILE = DxeNetLib.uni
+ FILE_GUID = db6dcef3-9f4e-4340-9351-fc35aa8a5888
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = NetLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ DxeNetLib.c
+ NetBuffer.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ BaseMemoryLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ UefiLib
+ MemoryAllocationLib
+ DevicePathLib
+ PrintLib
+
+
+[Guids]
+ gEfiSmbiosTableGuid ## SOMETIMES_CONSUMES ## SystemTable
+ gEfiSmbios3TableGuid ## SOMETIMES_CONSUMES ## SystemTable
+ gEfiAdapterInfoMediaStateGuid ## SOMETIMES_CONSUMES
+
+
+[Protocols]
+ gEfiSimpleNetworkProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiManagedNetworkProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiManagedNetworkServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiIp4Config2ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiComponentNameProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiComponentName2ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiAdapterInformationProtocolGuid ## SOMETIMES_CONSUMES
diff --git a/NetworkPkg/Library/DxeNetLib/DxeNetLib.uni b/NetworkPkg/Library/DxeNetLib/DxeNetLib.uni
new file mode 100644
index 0000000000..0c9f1fddde
--- /dev/null
+++ b/NetworkPkg/Library/DxeNetLib/DxeNetLib.uni
@@ -0,0 +1,16 @@
+// /** @file
+// This library instance provides the basic network services.
+//
+// This library instance provides the basic network services.
+//
+// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Provides the basic network services"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This library instance provides the basic network services."
+
diff --git a/NetworkPkg/Library/DxeNetLib/NetBuffer.c b/NetworkPkg/Library/DxeNetLib/NetBuffer.c
new file mode 100644
index 0000000000..2408e9a104
--- /dev/null
+++ b/NetworkPkg/Library/DxeNetLib/NetBuffer.c
@@ -0,0 +1,1890 @@
+/** @file
+ Network library functions providing net buffer operation support.
+
+Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <Library/NetLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+
+/**
+ Allocate and build up the sketch for a NET_BUF.
+
+ The net buffer allocated has the BlockOpNum's NET_BLOCK_OP, and its associated
+ NET_VECTOR has the BlockNum's NET_BLOCK. But all the NET_BLOCK_OP and
+ NET_BLOCK remain un-initialized.
+
+ @param[in] BlockNum The number of NET_BLOCK in the vector of net buffer
+ @param[in] BlockOpNum The number of NET_BLOCK_OP in the net buffer
+
+ @return Pointer to the allocated NET_BUF, or NULL if the
+ allocation failed due to resource limit.
+
+**/
+NET_BUF *
+NetbufAllocStruct (
+ IN UINT32 BlockNum,
+ IN UINT32 BlockOpNum
+ )
+{
+ NET_BUF *Nbuf;
+ NET_VECTOR *Vector;
+
+ ASSERT (BlockOpNum >= 1);
+
+ //
+ // Allocate three memory blocks.
+ //
+ Nbuf = AllocateZeroPool (NET_BUF_SIZE (BlockOpNum));
+
+ if (Nbuf == NULL) {
+ return NULL;
+ }
+
+ Nbuf->Signature = NET_BUF_SIGNATURE;
+ Nbuf->RefCnt = 1;
+ Nbuf->BlockOpNum = BlockOpNum;
+ InitializeListHead (&Nbuf->List);
+
+ if (BlockNum != 0) {
+ Vector = AllocateZeroPool (NET_VECTOR_SIZE (BlockNum));
+
+ if (Vector == NULL) {
+ goto FreeNbuf;
+ }
+
+ Vector->Signature = NET_VECTOR_SIGNATURE;
+ Vector->RefCnt = 1;
+ Vector->BlockNum = BlockNum;
+ Nbuf->Vector = Vector;
+ }
+
+ return Nbuf;
+
+FreeNbuf:
+
+ FreePool (Nbuf);
+ return NULL;
+}
+
+
+/**
+ Allocate a single block NET_BUF. Upon allocation, all the
+ free space is in the tail room.
+
+ @param[in] Len The length of the block.
+
+ @return Pointer to the allocated NET_BUF, or NULL if the
+ allocation failed due to resource limit.
+
+**/
+NET_BUF *
+EFIAPI
+NetbufAlloc (
+ IN UINT32 Len
+ )
+{
+ NET_BUF *Nbuf;
+ NET_VECTOR *Vector;
+ UINT8 *Bulk;
+
+ ASSERT (Len > 0);
+
+ Nbuf = NetbufAllocStruct (1, 1);
+
+ if (Nbuf == NULL) {
+ return NULL;
+ }
+
+ Bulk = AllocatePool (Len);
+
+ if (Bulk == NULL) {
+ goto FreeNBuf;
+ }
+
+ Vector = Nbuf->Vector;
+ Vector->Len = Len;
+
+ Vector->Block[0].Bulk = Bulk;
+ Vector->Block[0].Len = Len;
+
+ Nbuf->BlockOp[0].BlockHead = Bulk;
+ Nbuf->BlockOp[0].BlockTail = Bulk + Len;
+
+ Nbuf->BlockOp[0].Head = Bulk;
+ Nbuf->BlockOp[0].Tail = Bulk;
+ Nbuf->BlockOp[0].Size = 0;
+
+ return Nbuf;
+
+FreeNBuf:
+ FreePool (Nbuf);
+ return NULL;
+}
+
+/**
+ Free the net vector.
+
+ Decrease the reference count of the net vector by one. The real resource free
+ operation isn't performed until the reference count of the net vector is
+ decreased to 0.
+
+ @param[in] Vector Pointer to the NET_VECTOR to be freed.
+
+**/
+VOID
+NetbufFreeVector (
+ IN NET_VECTOR *Vector
+ )
+{
+ UINT32 Index;
+
+ ASSERT (Vector != NULL);
+ NET_CHECK_SIGNATURE (Vector, NET_VECTOR_SIGNATURE);
+ ASSERT (Vector->RefCnt > 0);
+
+ Vector->RefCnt--;
+
+ if (Vector->RefCnt > 0) {
+ return;
+ }
+
+ if (Vector->Free != NULL) {
+ //
+ // Call external free function to free the vector if it
+ // isn't NULL. If NET_VECTOR_OWN_FIRST is set, release the
+ // first block since it is allocated by us
+ //
+ if ((Vector->Flag & NET_VECTOR_OWN_FIRST) != 0) {
+ gBS->FreePool (Vector->Block[0].Bulk);
+ }
+
+ Vector->Free (Vector->Arg);
+
+ } else {
+ //
+ // Free each memory block associated with the Vector
+ //
+ for (Index = 0; Index < Vector->BlockNum; Index++) {
+ gBS->FreePool (Vector->Block[Index].Bulk);
+ }
+ }
+
+ FreePool (Vector);
+}
+
+
+/**
+ Free the net buffer and its associated NET_VECTOR.
+
+ Decrease the reference count of the net buffer by one. Free the associated net
+ vector and itself if the reference count of the net buffer is decreased to 0.
+ The net vector free operation just decrease the reference count of the net
+ vector by one and do the real resource free operation when the reference count
+ of the net vector is 0.
+
+ @param[in] Nbuf Pointer to the NET_BUF to be freed.
+
+**/
+VOID
+EFIAPI
+NetbufFree (
+ IN NET_BUF *Nbuf
+ )
+{
+ ASSERT (Nbuf != NULL);
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ ASSERT (Nbuf->RefCnt > 0);
+
+ Nbuf->RefCnt--;
+
+ if (Nbuf->RefCnt == 0) {
+ //
+ // Update Vector only when NBuf is to be released. That is,
+ // all the sharing of Nbuf increse Vector's RefCnt by one
+ //
+ NetbufFreeVector (Nbuf->Vector);
+ FreePool (Nbuf);
+ }
+}
+
+
+/**
+ Create a copy of the net buffer that shares the associated net vector.
+
+ The reference count of the newly created net buffer is set to 1. The reference
+ count of the associated net vector is increased by one.
+
+ @param[in] Nbuf Pointer to the net buffer to be cloned.
+
+ @return Pointer to the cloned net buffer, or NULL if the
+ allocation failed due to resource limit.
+
+**/
+NET_BUF *
+EFIAPI
+NetbufClone (
+ IN NET_BUF *Nbuf
+ )
+{
+ NET_BUF *Clone;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ Clone = AllocatePool (NET_BUF_SIZE (Nbuf->BlockOpNum));
+
+ if (Clone == NULL) {
+ return NULL;
+ }
+
+ Clone->Signature = NET_BUF_SIGNATURE;
+ Clone->RefCnt = 1;
+ InitializeListHead (&Clone->List);
+
+ Clone->Ip = Nbuf->Ip;
+ Clone->Tcp = Nbuf->Tcp;
+
+ CopyMem (Clone->ProtoData, Nbuf->ProtoData, NET_PROTO_DATA);
+
+ NET_GET_REF (Nbuf->Vector);
+
+ Clone->Vector = Nbuf->Vector;
+ Clone->BlockOpNum = Nbuf->BlockOpNum;
+ Clone->TotalSize = Nbuf->TotalSize;
+ CopyMem (Clone->BlockOp, Nbuf->BlockOp, sizeof (NET_BLOCK_OP) * Nbuf->BlockOpNum);
+
+ return Clone;
+}
+
+
+/**
+ Create a duplicated copy of the net buffer with data copied and HeadSpace
+ bytes of head space reserved.
+
+ The duplicated net buffer will allocate its own memory to hold the data of the
+ source net buffer.
+
+ @param[in] Nbuf Pointer to the net buffer to be duplicated from.
+ @param[in, out] Duplicate Pointer to the net buffer to duplicate to, if
+ NULL a new net buffer is allocated.
+ @param[in] HeadSpace Length of the head space to reserve.
+
+ @return Pointer to the duplicated net buffer, or NULL if
+ the allocation failed due to resource limit.
+
+**/
+NET_BUF *
+EFIAPI
+NetbufDuplicate (
+ IN NET_BUF *Nbuf,
+ IN OUT NET_BUF *Duplicate OPTIONAL,
+ IN UINT32 HeadSpace
+ )
+{
+ UINT8 *Dst;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ if (Duplicate == NULL) {
+ Duplicate = NetbufAlloc (Nbuf->TotalSize + HeadSpace);
+ }
+
+ if (Duplicate == NULL) {
+ return NULL;
+ }
+
+ //
+ // Don't set the IP and TCP head point, since it is most
+ // like that they are pointing to the memory of Nbuf.
+ //
+ CopyMem (Duplicate->ProtoData, Nbuf->ProtoData, NET_PROTO_DATA);
+ NetbufReserve (Duplicate, HeadSpace);
+
+ Dst = NetbufAllocSpace (Duplicate, Nbuf->TotalSize, NET_BUF_TAIL);
+ NetbufCopy (Nbuf, 0, Nbuf->TotalSize, Dst);
+
+ return Duplicate;
+}
+
+
+/**
+ Free a list of net buffers.
+
+ @param[in, out] Head Pointer to the head of linked net buffers.
+
+**/
+VOID
+EFIAPI
+NetbufFreeList (
+ IN OUT LIST_ENTRY *Head
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ NET_BUF *Nbuf;
+
+ Entry = Head->ForwardLink;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ RemoveEntryList (Entry);
+ NetbufFree (Nbuf);
+ }
+
+ ASSERT (IsListEmpty (Head));
+}
+
+
+/**
+ Get the index of NET_BLOCK_OP that contains the byte at Offset in the net
+ buffer.
+
+ This can be used to, for example, retrieve the IP header in the packet. It
+ also can be used to get the fragment that contains the byte which is used
+ mainly by the library implementation itself.
+
+ @param[in] Nbuf Pointer to the net buffer.
+ @param[in] Offset The offset of the byte.
+ @param[out] Index Index of the NET_BLOCK_OP that contains the byte at
+ Offset.
+
+ @return Pointer to the Offset'th byte of data in the net buffer, or NULL
+ if there is no such data in the net buffer.
+
+**/
+UINT8 *
+EFIAPI
+NetbufGetByte (
+ IN NET_BUF *Nbuf,
+ IN UINT32 Offset,
+ OUT UINT32 *Index OPTIONAL
+ )
+{
+ NET_BLOCK_OP *BlockOp;
+ UINT32 Loop;
+ UINT32 Len;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ if (Offset >= Nbuf->TotalSize) {
+ return NULL;
+ }
+
+ BlockOp = Nbuf->BlockOp;
+ Len = 0;
+
+ for (Loop = 0; Loop < Nbuf->BlockOpNum; Loop++) {
+
+ if (Len + BlockOp[Loop].Size <= Offset) {
+ Len += BlockOp[Loop].Size;
+ continue;
+ }
+
+ if (Index != NULL) {
+ *Index = Loop;
+ }
+
+ return BlockOp[Loop].Head + (Offset - Len);
+ }
+
+ return NULL;
+}
+
+
+
+/**
+ Set the NET_BLOCK and corresponding NET_BLOCK_OP in the net buffer and
+ corresponding net vector according to the bulk pointer and bulk length.
+
+ All the pointers in the Index'th NET_BLOCK and NET_BLOCK_OP are set to the
+ bulk's head and tail respectively. So, this function alone can't be used by
+ NetbufAlloc.
+
+ @param[in, out] Nbuf Pointer to the net buffer.
+ @param[in] Bulk Pointer to the data.
+ @param[in] Len Length of the bulk data.
+ @param[in] Index The data block index in the net buffer the bulk
+ data should belong to.
+
+**/
+VOID
+NetbufSetBlock (
+ IN OUT NET_BUF *Nbuf,
+ IN UINT8 *Bulk,
+ IN UINT32 Len,
+ IN UINT32 Index
+ )
+{
+ NET_BLOCK_OP *BlockOp;
+ NET_BLOCK *Block;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ NET_CHECK_SIGNATURE (Nbuf->Vector, NET_VECTOR_SIGNATURE);
+ ASSERT (Index < Nbuf->BlockOpNum);
+
+ Block = &(Nbuf->Vector->Block[Index]);
+ BlockOp = &(Nbuf->BlockOp[Index]);
+ Block->Len = Len;
+ Block->Bulk = Bulk;
+ BlockOp->BlockHead = Bulk;
+ BlockOp->BlockTail = Bulk + Len;
+ BlockOp->Head = Bulk;
+ BlockOp->Tail = Bulk + Len;
+ BlockOp->Size = Len;
+}
+
+
+
+/**
+ Set the NET_BLOCK_OP in the net buffer. The corresponding NET_BLOCK
+ structure is left untouched.
+
+ Some times, there is no 1:1 relationship between NET_BLOCK and NET_BLOCK_OP.
+ For example, that in NetbufGetFragment.
+
+ @param[in, out] Nbuf Pointer to the net buffer.
+ @param[in] Bulk Pointer to the data.
+ @param[in] Len Length of the bulk data.
+ @param[in] Index The data block index in the net buffer the bulk
+ data should belong to.
+
+**/
+VOID
+NetbufSetBlockOp (
+ IN OUT NET_BUF *Nbuf,
+ IN UINT8 *Bulk,
+ IN UINT32 Len,
+ IN UINT32 Index
+ )
+{
+ NET_BLOCK_OP *BlockOp;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ ASSERT (Index < Nbuf->BlockOpNum);
+
+ BlockOp = &(Nbuf->BlockOp[Index]);
+ BlockOp->BlockHead = Bulk;
+ BlockOp->BlockTail = Bulk + Len;
+ BlockOp->Head = Bulk;
+ BlockOp->Tail = Bulk + Len;
+ BlockOp->Size = Len;
+}
+
+
+/**
+ Helper function for NetbufGetFragment. NetbufGetFragment may allocate the
+ first block to reserve HeadSpace bytes header space. So it needs to create a
+ new net vector for the first block and can avoid copy for the remaining data
+ by sharing the old net vector.
+
+ @param[in] Arg Point to the old NET_VECTOR.
+
+**/
+VOID
+EFIAPI
+NetbufGetFragmentFree (
+ IN VOID *Arg
+ )
+{
+ NET_VECTOR *Vector;
+
+ Vector = (NET_VECTOR *)Arg;
+ NetbufFreeVector (Vector);
+}
+
+
+/**
+ Create a NET_BUF structure which contains Len byte data of Nbuf starting from
+ Offset.
+
+ A new NET_BUF structure will be created but the associated data in NET_VECTOR
+ is shared. This function exists to do IP packet fragmentation.
+
+ @param[in] Nbuf Pointer to the net buffer to be extracted.
+ @param[in] Offset Starting point of the data to be included in the new
+ net buffer.
+ @param[in] Len Bytes of data to be included in the new net buffer.
+ @param[in] HeadSpace Bytes of head space to reserve for protocol header.
+
+ @return Pointer to the cloned net buffer, or NULL if the
+ allocation failed due to resource limit.
+
+**/
+NET_BUF *
+EFIAPI
+NetbufGetFragment (
+ IN NET_BUF *Nbuf,
+ IN UINT32 Offset,
+ IN UINT32 Len,
+ IN UINT32 HeadSpace
+ )
+{
+ NET_BUF *Child;
+ NET_VECTOR *Vector;
+ NET_BLOCK_OP *BlockOp;
+ UINT32 CurBlockOp;
+ UINT32 BlockOpNum;
+ UINT8 *FirstBulk;
+ UINT32 Index;
+ UINT32 First;
+ UINT32 Last;
+ UINT32 FirstSkip;
+ UINT32 FirstLen;
+ UINT32 LastLen;
+ UINT32 Cur;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ if ((Len == 0) || (Offset + Len > Nbuf->TotalSize)) {
+ return NULL;
+ }
+
+ //
+ // First find the first and last BlockOp that contains
+ // the valid data, and compute the offset of the first
+ // BlockOp and length of the last BlockOp
+ //
+ BlockOp = Nbuf->BlockOp;
+ Cur = 0;
+
+ for (Index = 0; Index < Nbuf->BlockOpNum; Index++) {
+ if (Offset < Cur + BlockOp[Index].Size) {
+ break;
+ }
+
+ Cur += BlockOp[Index].Size;
+ }
+
+ //
+ // First is the index of the first BlockOp, FirstSkip is
+ // the offset of the first byte in the first BlockOp.
+ //
+ First = Index;
+ FirstSkip = Offset - Cur;
+ FirstLen = BlockOp[Index].Size - FirstSkip;
+
+ Last = 0;
+ LastLen = 0;
+
+ if (Len > FirstLen) {
+ Cur += BlockOp[Index].Size;
+ Index++;
+
+ for (; Index < Nbuf->BlockOpNum; Index++) {
+ if (Offset + Len <= Cur + BlockOp[Index].Size) {
+ Last = Index;
+ LastLen = Offset + Len - Cur;
+ break;
+ }
+
+ Cur += BlockOp[Index].Size;
+ }
+
+ } else {
+ Last = First;
+ LastLen = Len;
+ FirstLen = Len;
+ }
+
+ ASSERT (Last >= First);
+ BlockOpNum = Last - First + 1;
+ CurBlockOp = 0;
+
+ if (HeadSpace != 0) {
+ //
+ // Allocate an extra block to accomdate the head space.
+ //
+ BlockOpNum++;
+
+ Child = NetbufAllocStruct (1, BlockOpNum);
+
+ if (Child == NULL) {
+ return NULL;
+ }
+
+ FirstBulk = AllocatePool (HeadSpace);
+
+ if (FirstBulk == NULL) {
+ goto FreeChild;
+ }
+
+ Vector = Child->Vector;
+ Vector->Free = NetbufGetFragmentFree;
+ Vector->Arg = Nbuf->Vector;
+ Vector->Flag = NET_VECTOR_OWN_FIRST;
+ Vector->Len = HeadSpace;
+
+ //
+ // Reserve the head space in the first block
+ //
+ NetbufSetBlock (Child, FirstBulk, HeadSpace, 0);
+ Child->BlockOp[0].Head += HeadSpace;
+ Child->BlockOp[0].Size = 0;
+ CurBlockOp++;
+
+ } else {
+ Child = NetbufAllocStruct (0, BlockOpNum);
+
+ if (Child == NULL) {
+ return NULL;
+ }
+
+ Child->Vector = Nbuf->Vector;
+ }
+
+ NET_GET_REF (Nbuf->Vector);
+ Child->TotalSize = Len;
+
+ //
+ // Set all the BlockOp up, the first and last one are special
+ // and need special process.
+ //
+ NetbufSetBlockOp (
+ Child,
+ Nbuf->BlockOp[First].Head + FirstSkip,
+ FirstLen,
+ CurBlockOp++
+ );
+
+ for (Index = First + 1; Index < Last; Index++) {
+ NetbufSetBlockOp (
+ Child,
+ BlockOp[Index].Head,
+ BlockOp[Index].Size,
+ CurBlockOp++
+ );
+ }
+
+ if (First != Last) {
+ NetbufSetBlockOp (
+ Child,
+ BlockOp[Last].Head,
+ LastLen,
+ CurBlockOp
+ );
+ }
+
+ CopyMem (Child->ProtoData, Nbuf->ProtoData, NET_PROTO_DATA);
+ return Child;
+
+FreeChild:
+
+ FreePool (Child);
+ return NULL;
+}
+
+
+
+/**
+ Build a NET_BUF from external blocks.
+
+ A new NET_BUF structure will be created from external blocks. Additional block
+ of memory will be allocated to hold reserved HeadSpace bytes of header room
+ and existing HeadLen bytes of header but the external blocks are shared by the
+ net buffer to avoid data copying.
+
+ @param[in] ExtFragment Pointer to the data block.
+ @param[in] ExtNum The number of the data blocks.
+ @param[in] HeadSpace The head space to be reserved.
+ @param[in] HeadLen The length of the protocol header, This function
+ will pull that number of data into a linear block.
+ @param[in] ExtFree Pointer to the caller provided free function.
+ @param[in] Arg The argument passed to ExtFree when ExtFree is
+ called.
+
+ @return Pointer to the net buffer built from the data blocks,
+ or NULL if the allocation failed due to resource
+ limit.
+
+**/
+NET_BUF *
+EFIAPI
+NetbufFromExt (
+ IN NET_FRAGMENT *ExtFragment,
+ IN UINT32 ExtNum,
+ IN UINT32 HeadSpace,
+ IN UINT32 HeadLen,
+ IN NET_VECTOR_EXT_FREE ExtFree,
+ IN VOID *Arg OPTIONAL
+ )
+{
+ NET_BUF *Nbuf;
+ NET_VECTOR *Vector;
+ NET_FRAGMENT SavedFragment;
+ UINT32 SavedIndex;
+ UINT32 TotalLen;
+ UINT32 BlockNum;
+ UINT8 *FirstBlock;
+ UINT32 FirstBlockLen;
+ UINT8 *Header;
+ UINT32 CurBlock;
+ UINT32 Index;
+ UINT32 Len;
+ UINT32 Copied;
+
+ ASSERT ((ExtFragment != NULL) && (ExtNum > 0) && (ExtFree != NULL));
+
+ SavedFragment.Bulk = NULL;
+ SavedFragment.Len = 0;
+
+ FirstBlockLen = 0;
+ FirstBlock = NULL;
+ BlockNum = ExtNum;
+ Index = 0;
+ TotalLen = 0;
+ SavedIndex = 0;
+ Len = 0;
+ Copied = 0;
+
+ //
+ // No need to consolidate the header if the first block is
+ // longer than the header length or there is only one block.
+ //
+ if ((ExtFragment[0].Len >= HeadLen) || (ExtNum == 1)) {
+ HeadLen = 0;
+ }
+
+ //
+ // Allocate an extra block if we need to:
+ // 1. Allocate some header space
+ // 2. aggreate the packet header
+ //
+ if ((HeadSpace != 0) || (HeadLen != 0)) {
+ FirstBlockLen = HeadLen + HeadSpace;
+ FirstBlock = AllocatePool (FirstBlockLen);
+
+ if (FirstBlock == NULL) {
+ return NULL;
+ }
+
+ BlockNum++;
+ }
+
+ //
+ // Copy the header to the first block, reduce the NET_BLOCK
+ // to allocate by one for each block that is completely covered
+ // by the first bulk.
+ //
+ if (HeadLen != 0) {
+ Len = HeadLen;
+ Header = FirstBlock + HeadSpace;
+
+ for (Index = 0; Index < ExtNum; Index++) {
+ if (Len >= ExtFragment[Index].Len) {
+ CopyMem (Header, ExtFragment[Index].Bulk, ExtFragment[Index].Len);
+
+ Copied += ExtFragment[Index].Len;
+ Len -= ExtFragment[Index].Len;
+ Header += ExtFragment[Index].Len;
+ TotalLen += ExtFragment[Index].Len;
+ BlockNum--;
+
+ if (Len == 0) {
+ //
+ // Increament the index number to point to the next
+ // non-empty fragment.
+ //
+ Index++;
+ break;
+ }
+
+ } else {
+ CopyMem (Header, ExtFragment[Index].Bulk, Len);
+
+ Copied += Len;
+ TotalLen += Len;
+
+ //
+ // Adjust the block structure to exclude the data copied,
+ // So, the left-over block can be processed as other blocks.
+ // But it must be recovered later. (SavedIndex > 0) always
+ // holds since we don't aggreate the header if the first block
+ // is bigger enough that the header is continuous
+ //
+ SavedIndex = Index;
+ SavedFragment = ExtFragment[Index];
+ ExtFragment[Index].Bulk += Len;
+ ExtFragment[Index].Len -= Len;
+ break;
+ }
+ }
+ }
+
+ Nbuf = NetbufAllocStruct (BlockNum, BlockNum);
+
+ if (Nbuf == NULL) {
+ goto FreeFirstBlock;
+ }
+
+ Vector = Nbuf->Vector;
+ Vector->Free = ExtFree;
+ Vector->Arg = Arg;
+ Vector->Flag = ((FirstBlockLen != 0) ? NET_VECTOR_OWN_FIRST : 0);
+
+ //
+ // Set the first block up which may contain
+ // some head space and aggregated header
+ //
+ CurBlock = 0;
+
+ if (FirstBlockLen != 0) {
+ NetbufSetBlock (Nbuf, FirstBlock, HeadSpace + Copied, 0);
+ Nbuf->BlockOp[0].Head += HeadSpace;
+ Nbuf->BlockOp[0].Size = Copied;
+
+ CurBlock++;
+ }
+
+ for (; Index < ExtNum; Index++) {
+ NetbufSetBlock (Nbuf, ExtFragment[Index].Bulk, ExtFragment[Index].Len, CurBlock);
+ TotalLen += ExtFragment[Index].Len;
+ CurBlock++;
+ }
+
+ Vector->Len = TotalLen + HeadSpace;
+ Nbuf->TotalSize = TotalLen;
+
+ if (SavedIndex != 0) {
+ ExtFragment[SavedIndex] = SavedFragment;
+ }
+
+ return Nbuf;
+
+FreeFirstBlock:
+ if (FirstBlock != NULL) {
+ FreePool (FirstBlock);
+ }
+ return NULL;
+}
+
+
+/**
+ Build a fragment table to contain the fragments in the net buffer. This is the
+ opposite operation of the NetbufFromExt.
+
+ @param[in] Nbuf Point to the net buffer.
+ @param[in, out] ExtFragment Pointer to the data block.
+ @param[in, out] ExtNum The number of the data blocks.
+
+ @retval EFI_BUFFER_TOO_SMALL The number of non-empty block is bigger than
+ ExtNum.
+ @retval EFI_SUCCESS Fragment table is built successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+NetbufBuildExt (
+ IN NET_BUF *Nbuf,
+ IN OUT NET_FRAGMENT *ExtFragment,
+ IN OUT UINT32 *ExtNum
+ )
+{
+ UINT32 Index;
+ UINT32 Current;
+
+ Current = 0;
+
+ for (Index = 0; (Index < Nbuf->BlockOpNum); Index++) {
+ if (Nbuf->BlockOp[Index].Size == 0) {
+ continue;
+ }
+
+ if (Current < *ExtNum) {
+ ExtFragment[Current].Bulk = Nbuf->BlockOp[Index].Head;
+ ExtFragment[Current].Len = Nbuf->BlockOp[Index].Size;
+ Current++;
+ } else {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ }
+
+ *ExtNum = Current;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Build a net buffer from a list of net buffers.
+
+ All the fragments will be collected from the list of NEW_BUF and then a new
+ net buffer will be created through NetbufFromExt.
+
+ @param[in] BufList A List of the net buffer.
+ @param[in] HeadSpace The head space to be reserved.
+ @param[in] HeaderLen The length of the protocol header, This function
+ will pull that number of data into a linear block.
+ @param[in] ExtFree Pointer to the caller provided free function.
+ @param[in] Arg The argument passed to ExtFree when ExtFree is called.
+
+ @return Pointer to the net buffer built from the list of net
+ buffers.
+
+**/
+NET_BUF *
+EFIAPI
+NetbufFromBufList (
+ IN LIST_ENTRY *BufList,
+ IN UINT32 HeadSpace,
+ IN UINT32 HeaderLen,
+ IN NET_VECTOR_EXT_FREE ExtFree,
+ IN VOID *Arg OPTIONAL
+ )
+{
+ NET_FRAGMENT *Fragment;
+ UINT32 FragmentNum;
+ LIST_ENTRY *Entry;
+ NET_BUF *Nbuf;
+ UINT32 Index;
+ UINT32 Current;
+
+ //
+ //Compute how many blocks are there
+ //
+ FragmentNum = 0;
+
+ NET_LIST_FOR_EACH (Entry, BufList) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ FragmentNum += Nbuf->BlockOpNum;
+ }
+
+ //
+ //Allocate and copy block points
+ //
+ Fragment = AllocatePool (sizeof (NET_FRAGMENT) * FragmentNum);
+
+ if (Fragment == NULL) {
+ return NULL;
+ }
+
+ Current = 0;
+
+ NET_LIST_FOR_EACH (Entry, BufList) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ for (Index = 0; Index < Nbuf->BlockOpNum; Index++) {
+ if (Nbuf->BlockOp[Index].Size != 0) {
+ Fragment[Current].Bulk = Nbuf->BlockOp[Index].Head;
+ Fragment[Current].Len = Nbuf->BlockOp[Index].Size;
+ Current++;
+ }
+ }
+ }
+
+ Nbuf = NetbufFromExt (Fragment, Current, HeadSpace, HeaderLen, ExtFree, Arg);
+ FreePool (Fragment);
+
+ return Nbuf;
+}
+
+
+/**
+ Reserve some space in the header room of the net buffer.
+
+ Upon allocation, all the space are in the tail room of the buffer. Call this
+ function to move some space to the header room. This function is quite limited
+ in that it can only reserve space from the first block of an empty NET_BUF not
+ built from the external. But it should be enough for the network stack.
+
+ @param[in, out] Nbuf Pointer to the net buffer.
+ @param[in] Len The length of buffer to be reserved from the header.
+
+**/
+VOID
+EFIAPI
+NetbufReserve (
+ IN OUT NET_BUF *Nbuf,
+ IN UINT32 Len
+ )
+{
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ NET_CHECK_SIGNATURE (Nbuf->Vector, NET_VECTOR_SIGNATURE);
+
+ ASSERT ((Nbuf->BlockOpNum == 1) && (Nbuf->TotalSize == 0));
+ ASSERT ((Nbuf->Vector->Free == NULL) && (Nbuf->Vector->Len >= Len));
+
+ Nbuf->BlockOp[0].Head += Len;
+ Nbuf->BlockOp[0].Tail += Len;
+
+ ASSERT (Nbuf->BlockOp[0].Tail <= Nbuf->BlockOp[0].BlockTail);
+}
+
+
+/**
+ Allocate Len bytes of space from the header or tail of the buffer.
+
+ @param[in, out] Nbuf Pointer to the net buffer.
+ @param[in] Len The length of the buffer to be allocated.
+ @param[in] FromHead The flag to indicate whether reserve the data
+ from head (TRUE) or tail (FALSE).
+
+ @return Pointer to the first byte of the allocated buffer,
+ or NULL if there is no sufficient space.
+
+**/
+UINT8*
+EFIAPI
+NetbufAllocSpace (
+ IN OUT NET_BUF *Nbuf,
+ IN UINT32 Len,
+ IN BOOLEAN FromHead
+ )
+{
+ NET_BLOCK_OP *BlockOp;
+ UINT32 Index;
+ UINT8 *SavedTail;
+
+ Index = 0;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ NET_CHECK_SIGNATURE (Nbuf->Vector, NET_VECTOR_SIGNATURE);
+
+ ASSERT (Len > 0);
+
+ if (FromHead) {
+ //
+ // Allocate some space from head. If the buffer is empty,
+ // allocate from the first block. If it isn't, allocate
+ // from the first non-empty block, or the block before that.
+ //
+ if (Nbuf->TotalSize == 0) {
+ Index = 0;
+ } else {
+ NetbufGetByte (Nbuf, 0, &Index);
+
+ if ((NET_HEADSPACE(&(Nbuf->BlockOp[Index])) < Len) && (Index > 0)) {
+ Index--;
+ }
+ }
+
+ BlockOp = &(Nbuf->BlockOp[Index]);
+
+ if (NET_HEADSPACE (BlockOp) < Len) {
+ return NULL;
+ }
+
+ BlockOp->Head -= Len;
+ BlockOp->Size += Len;
+ Nbuf->TotalSize += Len;
+
+ return BlockOp->Head;
+
+ } else {
+ //
+ // Allocate some space from the tail. If the buffer is empty,
+ // allocate from the first block. If it isn't, allocate
+ // from the last non-empty block, or the block after that.
+ //
+ if (Nbuf->TotalSize == 0) {
+ Index = 0;
+ } else {
+ NetbufGetByte (Nbuf, Nbuf->TotalSize - 1, &Index);
+
+ if ((NET_TAILSPACE(&(Nbuf->BlockOp[Index])) < Len) &&
+ (Index < Nbuf->BlockOpNum - 1)) {
+
+ Index++;
+ }
+ }
+
+ BlockOp = &(Nbuf->BlockOp[Index]);
+
+ if (NET_TAILSPACE (BlockOp) < Len) {
+ return NULL;
+ }
+
+ SavedTail = BlockOp->Tail;
+
+ BlockOp->Tail += Len;
+ BlockOp->Size += Len;
+ Nbuf->TotalSize += Len;
+
+ return SavedTail;
+ }
+}
+
+
+/**
+ Trim a single NET_BLOCK by Len bytes from the header or tail.
+
+ @param[in, out] BlockOp Pointer to the NET_BLOCK.
+ @param[in] Len The length of the data to be trimmed.
+ @param[in] FromHead The flag to indicate whether trim data from head
+ (TRUE) or tail (FALSE).
+
+**/
+VOID
+NetblockTrim (
+ IN OUT NET_BLOCK_OP *BlockOp,
+ IN UINT32 Len,
+ IN BOOLEAN FromHead
+ )
+{
+ ASSERT ((BlockOp != NULL) && (BlockOp->Size >= Len));
+
+ BlockOp->Size -= Len;
+
+ if (FromHead) {
+ BlockOp->Head += Len;
+ } else {
+ BlockOp->Tail -= Len;
+ }
+}
+
+
+/**
+ Trim Len bytes from the header or tail of the net buffer.
+
+ @param[in, out] Nbuf Pointer to the net buffer.
+ @param[in] Len The length of the data to be trimmed.
+ @param[in] FromHead The flag to indicate whether trim data from head
+ (TRUE) or tail (FALSE).
+
+ @return Length of the actually trimmed data, which is possible to be less
+ than Len because the TotalSize of Nbuf is less than Len.
+
+**/
+UINT32
+EFIAPI
+NetbufTrim (
+ IN OUT NET_BUF *Nbuf,
+ IN UINT32 Len,
+ IN BOOLEAN FromHead
+ )
+{
+ NET_BLOCK_OP *BlockOp;
+ UINT32 Index;
+ UINT32 Trimmed;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ if (Len == 0 || Nbuf->TotalSize == 0) {
+ return 0;
+ }
+
+ if (Len > Nbuf->TotalSize) {
+ Len = Nbuf->TotalSize;
+ }
+
+ //
+ // If FromTail is true, iterate backward. That
+ // is, init Index to NBuf->BlockNum - 1, and
+ // decrease it by 1 during each loop. Otherwise,
+ // iterate forward. That is, init Index to 0, and
+ // increase it by 1 during each loop.
+ //
+ Trimmed = 0;
+ Nbuf->TotalSize -= Len;
+
+ Index = (FromHead ? 0 : Nbuf->BlockOpNum - 1);
+ BlockOp = Nbuf->BlockOp;
+
+ for (;;) {
+ if (BlockOp[Index].Size == 0) {
+ Index += (FromHead ? 1 : -1);
+ continue;
+ }
+
+ if (Len > BlockOp[Index].Size) {
+ Len -= BlockOp[Index].Size;
+ Trimmed += BlockOp[Index].Size;
+ NetblockTrim (&BlockOp[Index], BlockOp[Index].Size, FromHead);
+ } else {
+ Trimmed += Len;
+ NetblockTrim (&BlockOp[Index], Len, FromHead);
+ break;
+ }
+
+ Index += (FromHead ? 1 : -1);
+ }
+
+ return Trimmed;
+}
+
+
+/**
+ Copy Len bytes of data from the specific offset of the net buffer to the
+ destination memory.
+
+ The Len bytes of data may cross the several fragments of the net buffer.
+
+ @param[in] Nbuf Pointer to the net buffer.
+ @param[in] Offset The sequence number of the first byte to copy.
+ @param[in] Len Length of the data to copy.
+ @param[in] Dest The destination of the data to copy to.
+
+ @return The length of the actual copied data, or 0 if the offset
+ specified exceeds the total size of net buffer.
+
+**/
+UINT32
+EFIAPI
+NetbufCopy (
+ IN NET_BUF *Nbuf,
+ IN UINT32 Offset,
+ IN UINT32 Len,
+ IN UINT8 *Dest
+ )
+{
+ NET_BLOCK_OP *BlockOp;
+ UINT32 Skip;
+ UINT32 Left;
+ UINT32 Copied;
+ UINT32 Index;
+ UINT32 Cur;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ ASSERT (Dest);
+
+ if ((Len == 0) || (Nbuf->TotalSize <= Offset)) {
+ return 0;
+ }
+
+ if (Nbuf->TotalSize - Offset < Len) {
+ Len = Nbuf->TotalSize - Offset;
+ }
+
+ BlockOp = Nbuf->BlockOp;
+
+ //
+ // Skip to the offset. Don't make "Offset-By-One" error here.
+ // Cur + BLOCK.SIZE is the first sequence number of next block.
+ // So, (Offset < Cur + BLOCK.SIZE) means that the first byte
+ // is in the current block. if (Offset == Cur + BLOCK.SIZE), the
+ // first byte is the next block's first byte.
+ //
+ Cur = 0;
+
+ for (Index = 0; Index < Nbuf->BlockOpNum; Index++) {
+ if (BlockOp[Index].Size == 0) {
+ continue;
+ }
+
+ if (Offset < Cur + BlockOp[Index].Size) {
+ break;
+ }
+
+ Cur += BlockOp[Index].Size;
+ }
+
+ //
+ // Cur is the sequence number of the first byte in the block
+ // Offset - Cur is the number of bytes before first byte to
+ // to copy in the current block.
+ //
+ Skip = Offset - Cur;
+ Left = BlockOp[Index].Size - Skip;
+
+ if (Len <= Left) {
+ CopyMem (Dest, BlockOp[Index].Head + Skip, Len);
+ return Len;
+ }
+
+ CopyMem (Dest, BlockOp[Index].Head + Skip, Left);
+
+ Dest += Left;
+ Len -= Left;
+ Copied = Left;
+
+ Index++;
+
+ for (; Index < Nbuf->BlockOpNum; Index++) {
+ if (Len > BlockOp[Index].Size) {
+ Len -= BlockOp[Index].Size;
+ Copied += BlockOp[Index].Size;
+
+ CopyMem (Dest, BlockOp[Index].Head, BlockOp[Index].Size);
+ Dest += BlockOp[Index].Size;
+ } else {
+ Copied += Len;
+ CopyMem (Dest, BlockOp[Index].Head, Len);
+ break;
+ }
+ }
+
+ return Copied;
+}
+
+
+/**
+ Initiate the net buffer queue.
+
+ @param[in, out] NbufQue Pointer to the net buffer queue to be initialized.
+
+**/
+VOID
+EFIAPI
+NetbufQueInit (
+ IN OUT NET_BUF_QUEUE *NbufQue
+ )
+{
+ NbufQue->Signature = NET_QUE_SIGNATURE;
+ NbufQue->RefCnt = 1;
+ InitializeListHead (&NbufQue->List);
+
+ InitializeListHead (&NbufQue->BufList);
+ NbufQue->BufSize = 0;
+ NbufQue->BufNum = 0;
+}
+
+
+/**
+ Allocate and initialize a net buffer queue.
+
+ @return Pointer to the allocated net buffer queue, or NULL if the
+ allocation failed due to resource limit.
+
+**/
+NET_BUF_QUEUE *
+EFIAPI
+NetbufQueAlloc (
+ VOID
+ )
+{
+ NET_BUF_QUEUE *NbufQue;
+
+ NbufQue = AllocatePool (sizeof (NET_BUF_QUEUE));
+ if (NbufQue == NULL) {
+ return NULL;
+ }
+
+ NetbufQueInit (NbufQue);
+
+ return NbufQue;
+}
+
+
+/**
+ Free a net buffer queue.
+
+ Decrease the reference count of the net buffer queue by one. The real resource
+ free operation isn't performed until the reference count of the net buffer
+ queue is decreased to 0.
+
+ @param[in] NbufQue Pointer to the net buffer queue to be freed.
+
+**/
+VOID
+EFIAPI
+NetbufQueFree (
+ IN NET_BUF_QUEUE *NbufQue
+ )
+{
+ ASSERT (NbufQue != NULL);
+ NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
+
+ NbufQue->RefCnt--;
+
+ if (NbufQue->RefCnt == 0) {
+ NetbufQueFlush (NbufQue);
+ FreePool (NbufQue);
+ }
+}
+
+
+/**
+ Append a net buffer to the net buffer queue.
+
+ @param[in, out] NbufQue Pointer to the net buffer queue.
+ @param[in, out] Nbuf Pointer to the net buffer to be appended.
+
+**/
+VOID
+EFIAPI
+NetbufQueAppend (
+ IN OUT NET_BUF_QUEUE *NbufQue,
+ IN OUT NET_BUF *Nbuf
+ )
+{
+ NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ InsertTailList (&NbufQue->BufList, &Nbuf->List);
+
+ NbufQue->BufSize += Nbuf->TotalSize;
+ NbufQue->BufNum++;
+}
+
+
+/**
+ Remove a net buffer from the head in the specific queue and return it.
+
+ @param[in, out] NbufQue Pointer to the net buffer queue.
+
+ @return Pointer to the net buffer removed from the specific queue,
+ or NULL if there is no net buffer in the specific queue.
+
+**/
+NET_BUF *
+EFIAPI
+NetbufQueRemove (
+ IN OUT NET_BUF_QUEUE *NbufQue
+ )
+{
+ NET_BUF *First;
+
+ NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
+
+ if (NbufQue->BufNum == 0) {
+ return NULL;
+ }
+
+ First = NET_LIST_USER_STRUCT (NbufQue->BufList.ForwardLink, NET_BUF, List);
+
+ NetListRemoveHead (&NbufQue->BufList);
+
+ NbufQue->BufSize -= First->TotalSize;
+ NbufQue->BufNum--;
+ return First;
+}
+
+
+/**
+ Copy Len bytes of data from the net buffer queue at the specific offset to the
+ destination memory.
+
+ The copying operation is the same as NetbufCopy but applies to the net buffer
+ queue instead of the net buffer.
+
+ @param[in] NbufQue Pointer to the net buffer queue.
+ @param[in] Offset The sequence number of the first byte to copy.
+ @param[in] Len Length of the data to copy.
+ @param[out] Dest The destination of the data to copy to.
+
+ @return The length of the actual copied data, or 0 if the offset
+ specified exceeds the total size of net buffer queue.
+
+**/
+UINT32
+EFIAPI
+NetbufQueCopy (
+ IN NET_BUF_QUEUE *NbufQue,
+ IN UINT32 Offset,
+ IN UINT32 Len,
+ OUT UINT8 *Dest
+ )
+{
+ LIST_ENTRY *Entry;
+ NET_BUF *Nbuf;
+ UINT32 Skip;
+ UINT32 Left;
+ UINT32 Cur;
+ UINT32 Copied;
+
+ NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
+ ASSERT (Dest != NULL);
+
+ if ((Len == 0) || (NbufQue->BufSize <= Offset)) {
+ return 0;
+ }
+
+ if (NbufQue->BufSize - Offset < Len) {
+ Len = NbufQue->BufSize - Offset;
+ }
+
+ //
+ // skip to the Offset
+ //
+ Cur = 0;
+ Nbuf = NULL;
+
+ NET_LIST_FOR_EACH (Entry, &NbufQue->BufList) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ if (Offset < Cur + Nbuf->TotalSize) {
+ break;
+ }
+
+ Cur += Nbuf->TotalSize;
+ }
+
+ ASSERT (Nbuf != NULL);
+
+ //
+ // Copy the data in the first buffer.
+ //
+ Skip = Offset - Cur;
+ Left = Nbuf->TotalSize - Skip;
+
+ if (Len < Left) {
+ return NetbufCopy (Nbuf, Skip, Len, Dest);
+ }
+
+ NetbufCopy (Nbuf, Skip, Left, Dest);
+ Dest += Left;
+ Len -= Left;
+ Copied = Left;
+
+ //
+ // Iterate over the others
+ //
+ Entry = Entry->ForwardLink;
+
+ while ((Len > 0) && (Entry != &NbufQue->BufList)) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ if (Len > Nbuf->TotalSize) {
+ Len -= Nbuf->TotalSize;
+ Copied += Nbuf->TotalSize;
+
+ NetbufCopy (Nbuf, 0, Nbuf->TotalSize, Dest);
+ Dest += Nbuf->TotalSize;
+
+ } else {
+ NetbufCopy (Nbuf, 0, Len, Dest);
+ Copied += Len;
+ break;
+ }
+
+ Entry = Entry->ForwardLink;
+ }
+
+ return Copied;
+}
+
+
+/**
+ Trim Len bytes of data from the buffer queue and free any net buffer
+ that is completely trimmed.
+
+ The trimming operation is the same as NetbufTrim but applies to the net buffer
+ queue instead of the net buffer.
+
+ @param[in, out] NbufQue Pointer to the net buffer queue.
+ @param[in] Len Length of the data to trim.
+
+ @return The actual length of the data trimmed.
+
+**/
+UINT32
+EFIAPI
+NetbufQueTrim (
+ IN OUT NET_BUF_QUEUE *NbufQue,
+ IN UINT32 Len
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ NET_BUF *Nbuf;
+ UINT32 Trimmed;
+
+ NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
+
+ if (Len == 0) {
+ return 0;
+ }
+
+ if (Len > NbufQue->BufSize) {
+ Len = NbufQue->BufSize;
+ }
+
+ NbufQue->BufSize -= Len;
+ Trimmed = 0;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &NbufQue->BufList) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ if (Len >= Nbuf->TotalSize) {
+ Trimmed += Nbuf->TotalSize;
+ Len -= Nbuf->TotalSize;
+
+ RemoveEntryList (Entry);
+ NetbufFree (Nbuf);
+
+ NbufQue->BufNum--;
+
+ if (Len == 0) {
+ break;
+ }
+
+ } else {
+ Trimmed += NetbufTrim (Nbuf, Len, NET_BUF_HEAD);
+ break;
+ }
+ }
+
+ return Trimmed;
+}
+
+
+/**
+ Flush the net buffer queue.
+
+ @param[in, out] NbufQue Pointer to the queue to be flushed.
+
+**/
+VOID
+EFIAPI
+NetbufQueFlush (
+ IN OUT NET_BUF_QUEUE *NbufQue
+ )
+{
+ NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
+
+ NetbufFreeList (&NbufQue->BufList);
+
+ NbufQue->BufNum = 0;
+ NbufQue->BufSize = 0;
+}
+
+
+/**
+ Compute the checksum for a bulk of data.
+
+ @param[in] Bulk Pointer to the data.
+ @param[in] Len Length of the data, in bytes.
+
+ @return The computed checksum.
+
+**/
+UINT16
+EFIAPI
+NetblockChecksum (
+ IN UINT8 *Bulk,
+ IN UINT32 Len
+ )
+{
+ register UINT32 Sum;
+
+ Sum = 0;
+
+ //
+ // Add left-over byte, if any
+ //
+ if (Len % 2 != 0) {
+ Sum += *(Bulk + Len - 1);
+ }
+
+ while (Len > 1) {
+ Sum += *(UINT16 *) Bulk;
+ Bulk += 2;
+ Len -= 2;
+ }
+
+ //
+ // Fold 32-bit sum to 16 bits
+ //
+ while ((Sum >> 16) != 0) {
+ Sum = (Sum & 0xffff) + (Sum >> 16);
+
+ }
+
+ return (UINT16) Sum;
+}
+
+
+/**
+ Add two checksums.
+
+ @param[in] Checksum1 The first checksum to be added.
+ @param[in] Checksum2 The second checksum to be added.
+
+ @return The new checksum.
+
+**/
+UINT16
+EFIAPI
+NetAddChecksum (
+ IN UINT16 Checksum1,
+ IN UINT16 Checksum2
+ )
+{
+ UINT32 Sum;
+
+ Sum = Checksum1 + Checksum2;
+
+ //
+ // two UINT16 can only add up to a carry of 1.
+ //
+ if ((Sum >> 16) != 0) {
+ Sum = (Sum & 0xffff) + 1;
+
+ }
+
+ return (UINT16) Sum;
+}
+
+
+/**
+ Compute the checksum for a NET_BUF.
+
+ @param[in] Nbuf Pointer to the net buffer.
+
+ @return The computed checksum.
+
+**/
+UINT16
+EFIAPI
+NetbufChecksum (
+ IN NET_BUF *Nbuf
+ )
+{
+ NET_BLOCK_OP *BlockOp;
+ UINT32 Offset;
+ UINT16 TotalSum;
+ UINT16 BlockSum;
+ UINT32 Index;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ TotalSum = 0;
+ Offset = 0;
+ BlockOp = Nbuf->BlockOp;
+
+ for (Index = 0; Index < Nbuf->BlockOpNum; Index++) {
+ if (BlockOp[Index].Size == 0) {
+ continue;
+ }
+
+ BlockSum = NetblockChecksum (BlockOp[Index].Head, BlockOp[Index].Size);
+
+ if ((Offset & 0x01) != 0) {
+ //
+ // The checksum starts with an odd byte, swap
+ // the checksum before added to total checksum
+ //
+ BlockSum = SwapBytes16 (BlockSum);
+ }
+
+ TotalSum = NetAddChecksum (BlockSum, TotalSum);
+ Offset += BlockOp[Index].Size;
+ }
+
+ return TotalSum;
+}
+
+
+/**
+ Compute the checksum for TCP/UDP pseudo header.
+
+ Src and Dst are in network byte order, and Len is in host byte order.
+
+ @param[in] Src The source address of the packet.
+ @param[in] Dst The destination address of the packet.
+ @param[in] Proto The protocol type of the packet.
+ @param[in] Len The length of the packet.
+
+ @return The computed checksum.
+
+**/
+UINT16
+EFIAPI
+NetPseudoHeadChecksum (
+ IN IP4_ADDR Src,
+ IN IP4_ADDR Dst,
+ IN UINT8 Proto,
+ IN UINT16 Len
+ )
+{
+ NET_PSEUDO_HDR Hdr;
+
+ //
+ // Zero the memory to relieve align problems
+ //
+ ZeroMem (&Hdr, sizeof (Hdr));
+
+ Hdr.SrcIp = Src;
+ Hdr.DstIp = Dst;
+ Hdr.Protocol = Proto;
+ Hdr.Len = HTONS (Len);
+
+ return NetblockChecksum ((UINT8 *) &Hdr, sizeof (Hdr));
+}
+
+/**
+ Compute the checksum for TCP6/UDP6 pseudo header.
+
+ Src and Dst are in network byte order, and Len is in host byte order.
+
+ @param[in] Src The source address of the packet.
+ @param[in] Dst The destination address of the packet.
+ @param[in] NextHeader The protocol type of the packet.
+ @param[in] Len The length of the packet.
+
+ @return The computed checksum.
+
+**/
+UINT16
+EFIAPI
+NetIp6PseudoHeadChecksum (
+ IN EFI_IPv6_ADDRESS *Src,
+ IN EFI_IPv6_ADDRESS *Dst,
+ IN UINT8 NextHeader,
+ IN UINT32 Len
+ )
+{
+ NET_IP6_PSEUDO_HDR Hdr;
+
+ //
+ // Zero the memory to relieve align problems
+ //
+ ZeroMem (&Hdr, sizeof (Hdr));
+
+ IP6_COPY_ADDRESS (&Hdr.SrcIp, Src);
+ IP6_COPY_ADDRESS (&Hdr.DstIp, Dst);
+
+ Hdr.NextHeader = NextHeader;
+ Hdr.Len = HTONL (Len);
+
+ return NetblockChecksum ((UINT8 *) &Hdr, sizeof (Hdr));
+}
+
+/**
+ The function frees the net buffer which allocated by the IP protocol. It releases
+ only the net buffer and doesn't call the external free function.
+
+ This function should be called after finishing the process of mIpSec->ProcessExt()
+ for outbound traffic. The (EFI_IPSEC2_PROTOCOL)->ProcessExt() allocates a new
+ buffer for the ESP, so there needs a function to free the old net buffer.
+
+ @param[in] Nbuf The network buffer to be freed.
+
+**/
+VOID
+NetIpSecNetbufFree (
+ NET_BUF *Nbuf
+ )
+{
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ ASSERT (Nbuf->RefCnt > 0);
+
+ Nbuf->RefCnt--;
+
+ if (Nbuf->RefCnt == 0) {
+
+ //
+ // Update Vector only when NBuf is to be released. That is,
+ // all the sharing of Nbuf increse Vector's RefCnt by one
+ //
+ NET_CHECK_SIGNATURE (Nbuf->Vector, NET_VECTOR_SIGNATURE);
+ ASSERT (Nbuf->Vector->RefCnt > 0);
+
+ Nbuf->Vector->RefCnt--;
+
+ if (Nbuf->Vector->RefCnt > 0) {
+ return;
+ }
+
+ //
+ // If NET_VECTOR_OWN_FIRST is set, release the first block since it is
+ // allocated by us
+ //
+ if ((Nbuf->Vector->Flag & NET_VECTOR_OWN_FIRST) != 0) {
+ FreePool (Nbuf->Vector->Block[0].Bulk);
+ }
+ FreePool (Nbuf->Vector);
+ FreePool (Nbuf);
+ }
+}
+