diff options
author | hhtian <hhtian@6f19259b-4bc3-4df7-8a09-765794883524> | 2010-08-27 08:44:10 +0000 |
---|---|---|
committer | hhtian <hhtian@6f19259b-4bc3-4df7-8a09-765794883524> | 2010-08-27 08:44:10 +0000 |
commit | 29bb53b888c4b98c605a58078847bcf8aaad13c6 (patch) | |
tree | 4fffbfb73290193b766ab58a45fa98f3b2dd9a24 | |
parent | 1e7f35aa64076d20539db0ab5d5f8eae3e507487 (diff) | |
download | edk2-29bb53b888c4b98c605a58078847bcf8aaad13c6.tar.gz |
sync to trunk r10785
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/branches/UDK2010@10840 6f19259b-4bc3-4df7-8a09-765794883524
-rw-r--r-- | MdeModulePkg/Library/DxeNetLib/DxeNetLib.c | 3157 | ||||
-rw-r--r-- | MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf | 62 | ||||
-rw-r--r-- | MdeModulePkg/Library/DxeNetLib/NetBuffer.c | 1844 |
3 files changed, 5063 insertions, 0 deletions
diff --git a/MdeModulePkg/Library/DxeNetLib/DxeNetLib.c b/MdeModulePkg/Library/DxeNetLib/DxeNetLib.c new file mode 100644 index 0000000000..55c72027fc --- /dev/null +++ b/MdeModulePkg/Library/DxeNetLib/DxeNetLib.c @@ -0,0 +1,3157 @@ +/** @file
+ Network library.
+
+Copyright (c) 2005 - 2010, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+**/
+
+#include <Uefi.h>
+
+#include <Protocol/DriverBinding.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/ManagedNetwork.h>
+#include <Protocol/HiiConfigRouting.h>
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/HiiConfigAccess.h>
+
+#include <Guid/NicIp4ConfigNvData.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/HiiLib.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
+
+//
+// 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.
+
+ @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;
+
+ 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 servity 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.
+
+**/
+UINT32
+SyslogBuildPacket (
+ IN UINT32 Level,
+ IN UINT8 *Module,
+ IN UINT8 *File,
+ IN UINT32 Line,
+ IN UINT8 *Message,
+ IN UINT32 BufLen,
+ OUT CHAR8 *Buf
+ )
+{
+ 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);
+ gRT->GetTime (&Time, NULL);
+ ASSERT ((Time.Month <= 12) && (Time.Month >= 1));
+
+ //
+ // 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--;
+
+ 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)
+ )
+
+ @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;
+
+ 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 servity level of the message.
+ @param Module The Moudle that generates the log.
+ @param File The file that contains the log.
+ @param Line The exact line that contains the log.
+ @param Message The user message to log.
+
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet
+ @retval EFI_SUCCESS The log is discard because that it is more verbose
+ than the mNetDebugLevelMax. Or, it has been sent out.
+**/
+EFI_STATUS
+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) {
+ 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
+ );
+
+ 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_NUM; 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.
+
+ 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. If NetMask is zero, use the IP address's class to get the default mask.
+
+ If Ip is 0, IP is not a valid unicast address.
+ Class D address is used for multicasting and class E address is reserved for future. If Ip
+ belongs to class D or class E, IP is not a valid unicast address.
+ If all bits of the host address of IP are 0 or 1, IP is also not a valid unicast address.
+
+ @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 Class;
+
+ Class = NetGetIpClass (Ip);
+
+ if ((Ip == 0) || (Class >= IP4_ADDR_CLASSD)) {
+ return FALSE;
+ }
+
+ if (NetMask == 0) {
+ NetMask = gIp4AllMasks[Class << 3];
+ }
+
+ if (((Ip &~NetMask) == ~NetMask) || ((Ip &~NetMask) == 0)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Check whether the incoming IPv6 address is a valid unicast address.
+
+ 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;
+
+ 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.
+
+ @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;
+
+ 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.
+
+ @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.
+
+ @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_NUM));
+
+ 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 ((Ip1->Addr[Byte] & Mask) != (Ip2->Addr[Byte] & Mask)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Switches the endianess of an IPv6 address
+
+ 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;
+
+ 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.
+
+ Get current time 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.
+
+ @return The random seed initialized with current time.
+
+**/
+UINT32
+EFIAPI
+NetRandomInitSeed (
+ VOID
+ )
+{
+ EFI_TIME Time;
+ UINT32 Seed;
+
+ gRT->GetTime (&Time, NULL);
+ Seed = (~Time.Hour << 24 | Time.Day << 16 | Time.Minute << 8 | Time.Second);
+ Seed ^= Time.Nanosecond;
+ Seed ^= Time.Year << 7;
+
+ return Seed;
+}
+
+
+/**
+ Extract a UINT32 from a byte stream.
+
+ 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;
+
+ CopyMem (&Value, Buf, sizeof (UINT32));
+ return NTOHL (Value);
+}
+
+
+/**
+ Put a UINT32 to the byte stream in network byte order.
+
+ 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
+ )
+{
+ 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.
+
+ 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
+ )
+{
+ 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.
+
+ 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
+ )
+{
+ NewEntry->ForwardLink = PostEntry;
+ NewEntry->BackLink = PostEntry->BackLink;
+ PostEntry->BackLink->ForwardLink = NewEntry;
+ PostEntry->BackLink = NewEntry;
+}
+
+
+/**
+ 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.
+
+ @param[in] Map The netmap to get the entry number.
+
+ @return The entry number in the netmap.
+
+**/
+UINTN
+EFIAPI
+NetMapGetCount (
+ IN NET_MAP *Map
+ )
+{
+ 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().
+
+ @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);
+
+ 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().
+
+ @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);
+
+ 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.
+
+ @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;
+
+ 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().
+
+ @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);
+
+ 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 contiue 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;
+}
+
+
+/**
+ Internal function to get the child handle of the NIC handle.
+
+ @param[in] Controller NIC controller handle.
+ @param[out] ChildHandle Returned child handle.
+
+ @retval EFI_SUCCESS Successfully to get child handle.
+ @retval Others Failed to get child handle.
+
+**/
+EFI_STATUS
+NetGetChildHandle (
+ IN EFI_HANDLE Controller,
+ OUT EFI_HANDLE *ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *Handles;
+ UINTN HandleCount;
+ UINTN Index;
+ EFI_DEVICE_PATH_PROTOCOL *ChildDeviceDevicePath;
+ VENDOR_DEVICE_PATH *VendorDeviceNode;
+
+ //
+ // Locate all EFI Hii Config Access protocols
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiHiiConfigAccessProtocolGuid,
+ NULL,
+ &HandleCount,
+ &Handles
+ );
+ if (EFI_ERROR (Status) || (HandleCount == 0)) {
+ return Status;
+ }
+
+ Status = EFI_NOT_FOUND;
+
+ for (Index = 0; Index < HandleCount; Index++) {
+
+ Status = EfiTestChildHandle (Controller, Handles[Index], &gEfiManagedNetworkServiceBindingProtocolGuid);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Get device path on the child handle
+ //
+ Status = gBS->HandleProtocol (
+ Handles[Index],
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &ChildDeviceDevicePath
+ );
+
+ if (!EFI_ERROR (Status)) {
+ while (!IsDevicePathEnd (ChildDeviceDevicePath)) {
+ ChildDeviceDevicePath = NextDevicePathNode (ChildDeviceDevicePath);
+ //
+ // Parse one instance
+ //
+ if (ChildDeviceDevicePath->Type == HARDWARE_DEVICE_PATH &&
+ ChildDeviceDevicePath->SubType == HW_VENDOR_DP) {
+ VendorDeviceNode = (VENDOR_DEVICE_PATH *) ChildDeviceDevicePath;
+ if (CompareMem (&VendorDeviceNode->Guid, &gEfiNicIp4ConfigVariableGuid, sizeof (EFI_GUID)) == 0) {
+ //
+ // Found item matched gEfiNicIp4ConfigVariableGuid
+ //
+ *ChildHandle = Handles[Index];
+ FreePool (Handles);
+ return EFI_SUCCESS;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ FreePool (Handles);
+ return Status;
+}
+
+
+/**
+ 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;
+ 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;
+ }
+
+ //
+ // Disconnect the driver specified by ImageHandle from all
+ // the devices in the handle database.
+ //
+ for (Index = 0; Index < DeviceHandleCount; Index++) {
+ Status = gBS->DisconnectController (
+ DeviceHandleBuffer[Index],
+ ImageHandle,
+ NULL
+ );
+ }
+
+ //
+ // Uninstall all the protocols installed in the driver entry point
+ //
+ for (Index = 0; Index < DeviceHandleCount; Index++) {
+ Status = gBS->HandleProtocol (
+ DeviceHandleBuffer[Index],
+ &gEfiDriverBindingProtocolGuid,
+ (VOID **) &DriverBinding
+ );
+
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ if (DriverBinding->ImageHandle != ImageHandle) {
+ continue;
+ }
+
+ gBS->UninstallProtocolInterface (
+ ImageHandle,
+ &gEfiDriverBindingProtocolGuid,
+ DriverBinding
+ );
+ Status = gBS->HandleProtocol (
+ DeviceHandleBuffer[Index],
+ &gEfiComponentNameProtocolGuid,
+ (VOID **) &ComponentName
+ );
+ if (!EFI_ERROR (Status)) {
+ gBS->UninstallProtocolInterface (
+ ImageHandle,
+ &gEfiComponentNameProtocolGuid,
+ ComponentName
+ );
+ }
+
+ Status = gBS->HandleProtocol (
+ DeviceHandleBuffer[Index],
+ &gEfiComponentName2ProtocolGuid,
+ (VOID **) &ComponentName2
+ );
+ if (!EFI_ERROR (Status)) {
+ gBS->UninstallProtocolInterface (
+ ImageHandle,
+ &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;
+}
+
+
+/**
+ Destory 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 destory.
+
+ @retval EFI_SUCCESS The child is successfully destoried.
+ @retval Others Failed to destory 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;
+ }
+
+ //
+ // destory 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.
+
+ 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)) {
+ return Status;
+ }
+
+ //
+ // Try to get SNP mode from MNP
+ //
+ Status = Mnp->GetModeData (Mnp, NULL, &SnpModeData);
+ if (EFI_ERROR (Status)) {
+ 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.
+
+ 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.
+ @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,
+ OUT CHAR16 **MacString
+ )
+{
+ EFI_STATUS Status;
+ EFI_MAC_ADDRESS MacAddress;
+ UINT8 *HwAddress;
+ UINTN HwAddressSize;
+ UINT16 VlanId;
+ CHAR16 *String;
+ UINTN Index;
+
+ 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.
+ //
+ String = AllocateZeroPool ((2 * HwAddressSize + 5 + 1) * sizeof (CHAR16));
+ 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++) {
+ String += UnicodeValueToString (String, PREFIX_ZERO | RADIX_HEX, *(HwAddress++), 2);
+ }
+
+ //
+ // Append VLAN ID if any
+ //
+ VlanId = NetLibGetVlanId (ServiceHandle);
+ if (VlanId != 0) {
+ *String++ = L'\\';
+ String += UnicodeValueToString (String, PREFIX_ZERO | RADIX_HEX, VlanId, 4);
+ }
+
+ //
+ // Null terminate the Unicode string
+ //
+ *String = L'\0';
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Detect media status for specified network device.
+
+ 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);
+
+ 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;
+}
+
+/**
+ Check the default address used by the IPv4 driver is static or dynamic (acquired
+ from DHCP).
+
+ If the controller handle does not have the NIC Ip4 Config Protocol installed, the
+ default address is static. If the EFI variable to save the configuration is not found,
+ the default address is static. Otherwise, get the result from the EFI variable which
+ saving the configuration.
+
+ @param[in] Controller The controller handle which has the NIC Ip4 Config 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_HII_CONFIG_ROUTING_PROTOCOL *HiiConfigRouting;
+ UINTN Len;
+ NIC_IP4_CONFIG_INFO *ConfigInfo;
+ BOOLEAN IsStatic;
+ EFI_STRING ConfigHdr;
+ EFI_STRING ConfigResp;
+ EFI_STRING AccessProgress;
+ EFI_STRING AccessResults;
+ EFI_STRING String;
+ EFI_HANDLE ChildHandle;
+
+ ConfigInfo = NULL;
+ ConfigHdr = NULL;
+ ConfigResp = NULL;
+ AccessProgress = NULL;
+ AccessResults = NULL;
+ IsStatic = TRUE;
+
+ Status = gBS->LocateProtocol (
+ &gEfiHiiConfigRoutingProtocolGuid,
+ NULL,
+ (VOID **) &HiiConfigRouting
+ );
+ if (EFI_ERROR (Status)) {
+ return TRUE;
+ }
+
+ Status = NetGetChildHandle (Controller, &ChildHandle);
+ if (EFI_ERROR (Status)) {
+ return TRUE;
+ }
+
+ //
+ // Construct config request string header
+ //
+ ConfigHdr = HiiConstructConfigHdr (&gEfiNicIp4ConfigVariableGuid, EFI_NIC_IP4_CONFIG_VARIABLE, ChildHandle);
+ if (ConfigHdr == NULL) {
+ return TRUE;
+ }
+
+ Len = StrLen (ConfigHdr);
+ ConfigResp = AllocateZeroPool ((Len + NIC_ITEM_CONFIG_SIZE * 2 + 100) * sizeof (CHAR16));
+ if (ConfigResp == NULL) {
+ goto ON_EXIT;
+ }
+ StrCpy (ConfigResp, ConfigHdr);
+
+ String = ConfigResp + Len;
+ UnicodeSPrint (
+ String,
+ (8 + 4 + 7 + 4 + 1) * sizeof (CHAR16),
+ L"&OFFSET=%04X&WIDTH=%04X",
+ OFFSET_OF (NIC_IP4_CONFIG_INFO, Source),
+ sizeof (UINT32)
+ );
+
+ Status = HiiConfigRouting->ExtractConfig (
+ HiiConfigRouting,
+ ConfigResp,
+ &AccessProgress,
+ &AccessResults
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ ConfigInfo = AllocateZeroPool (NIC_ITEM_CONFIG_SIZE);
+ if (ConfigInfo == NULL) {
+ goto ON_EXIT;
+ }
+
+ ConfigInfo->Source = IP4_CONFIG_SOURCE_STATIC;
+ Len = NIC_ITEM_CONFIG_SIZE;
+ Status = HiiConfigRouting->ConfigToBlock (
+ HiiConfigRouting,
+ AccessResults,
+ (UINT8 *) ConfigInfo,
+ &Len,
+ &AccessProgress
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ IsStatic = (BOOLEAN) (ConfigInfo->Source == IP4_CONFIG_SOURCE_STATIC);
+
+ON_EXIT:
+
+ if (AccessResults != NULL) {
+ FreePool (AccessResults);
+ }
+ if (ConfigInfo != NULL) {
+ FreePool (ConfigInfo);
+ }
+ if (ConfigResp != NULL) {
+ FreePool (ConfigResp);
+ }
+ if (ConfigHdr != NULL) {
+ FreePool (ConfigHdr);
+ }
+
+ return IsStatic;
+}
+
+/**
+ Create an IPv4 device path node.
+
+ The header type of IPv4 device path node is MESSAGING_DEVICE_PATH.
+ The header subtype of IPv4 device path node is MSG_IPv4_DP.
+ The length of the IPv4 device path node in bytes is 19.
+ 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
+ )
+{
+ Node->Header.Type = MESSAGING_DEVICE_PATH;
+ Node->Header.SubType = MSG_IPv4_DP;
+ SetDevicePathNodeLength (&Node->Header, 19);
+
+ 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);
+ }
+}
+
+/**
+ Create an IPv6 device path node.
+
+ 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
+ )
+{
+ 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;
+ Node->StaticIpAddress = FALSE;
+}
+
+/**
+ Find the UNDI/SNP handle from controller and protocol GUID.
+
+ 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;
+
+ 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
+ )
+{
+ UINT8 Index;
+ CHAR8 *Ip4Str;
+ CHAR8 *TempStr;
+ UINTN NodeVal;
+
+ if ((String == NULL) || (Ip4Address == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Ip4Str = (CHAR8 *) String;
+
+ for (Index = 0; Index < 4; Index++) {
+ TempStr = Ip4Str;
+
+ while ((*Ip4Str != '\0') && (*Ip4Str != '.')) {
+ Ip4Str++;
+ }
+
+ //
+ // The IPv4 address is X.X.X.X
+ //
+ if (*Ip4Str == '.') {
+ if (Index == 3) {
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ if (Index != 3) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Convert the string to IPv4 address. AsciiStrDecimalToUintn stops at the
+ // first character that is not a valid decimal character, '.' or '\0' here.
+ //
+ NodeVal = AsciiStrDecimalToUintn (TempStr);
+ if (NodeVal > 0xFF) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Ip4Address->Addr[Index] = (UINT8) NodeVal;
+
+ Ip4Str++;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Convert one Null-terminated ASCII string to EFI_IPv6_ADDRESS. The format of the
+ string is defined in RFC 4291 - Text Pepresentation 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
+ )
+{
+ UINT8 Index;
+ CHAR8 *Ip6Str;
+ CHAR8 *TempStr;
+ CHAR8 *TempStr2;
+ UINT8 NodeCnt;
+ UINT8 TailNodeCnt;
+ UINT8 AllowedCnt;
+ UINTN NodeVal;
+ BOOLEAN Short;
+ BOOLEAN Update;
+ BOOLEAN LeadZero;
+ UINT8 LeadZeroCnt;
+ UINT8 Cnt;
+
+ if ((String == NULL) || (Ip6Address == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Ip6Str = (CHAR8 *) String;
+ AllowedCnt = 6;
+ LeadZeroCnt = 0;
+
+ //
+ // An IPv6 address leading with : looks strange.
+ //
+ if (*Ip6Str == ':') {
+ if (*(Ip6Str + 1) != ':') {
+ return EFI_INVALID_PARAMETER;
+ } else {
+ AllowedCnt = 7;
+ }
+ }
+
+ ZeroMem (Ip6Address, sizeof (EFI_IPv6_ADDRESS));
+
+ NodeCnt = 0;
+ TailNodeCnt = 0;
+ Short = FALSE;
+ Update = FALSE;
+ LeadZero = FALSE;
+
+ for (Index = 0; Index < 15; Index = (UINT8) (Index + 2)) {
+ TempStr = Ip6Str;
+
+ while ((*Ip6Str != '\0') && (*Ip6Str != ':')) {
+ Ip6Str++;
+ }
+
+ if ((*Ip6Str == '\0') && (Index != 14)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*Ip6Str == ':') {
+ if (*(Ip6Str + 1) == ':') {
+ if ((NodeCnt > 6) ||
+ ((*(Ip6Str + 2) != '\0') && (AsciiStrHexToUintn (Ip6Str + 2) == 0))) {
+ //
+ // ::0 looks strange. report error to user.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ if ((NodeCnt == 6) && (*(Ip6Str + 2) != '\0') &&
+ (AsciiStrHexToUintn (Ip6Str + 2) != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Skip the abbreviation part of IPv6 address.
+ //
+ TempStr2 = Ip6Str + 2;
+ while ((*TempStr2 != '\0')) {
+ if (*TempStr2 == ':') {
+ if (*(TempStr2 + 1) == ':') {
+ //
+ // :: can only appear once in IPv6 address.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TailNodeCnt++;
+ if (TailNodeCnt >= (AllowedCnt - NodeCnt)) {
+ //
+ // :: indicates one or more groups of 16 bits of zeros.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ TempStr2++;
+ }
+
+ Short = TRUE;
+ Update = TRUE;
+
+ Ip6Str = Ip6Str + 2;
+ } else {
+ if (*(Ip6Str + 1) == '\0') {
+ return EFI_INVALID_PARAMETER;
+ }
+ Ip6Str++;
+ NodeCnt++;
+ if ((Short && (NodeCnt > 6)) || (!Short && (NodeCnt > 7))) {
+ //
+ // There are more than 8 groups of 16 bits of zeros.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ //
+ // Convert the string to IPv6 address. AsciiStrHexToUintn stops at the first
+ // character that is not a valid hexadecimal character, ':' or '\0' here.
+ //
+ NodeVal = AsciiStrHexToUintn (TempStr);
+ if ((NodeVal > 0xFFFF) || (Index > 14)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ if (NodeVal != 0) {
+ if ((*TempStr == '0') &&
+ ((*(TempStr + 2) == ':') || (*(TempStr + 3) == ':') ||
+ (*(TempStr + 2) == '\0') || (*(TempStr + 3) == '\0'))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ if ((*TempStr == '0') && (*(TempStr + 4) != '\0') &&
+ (*(TempStr + 4) != ':')) {
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ if (((*TempStr == '0') && (*(TempStr + 1) == '0') &&
+ ((*(TempStr + 2) == ':') || (*(TempStr + 2) == '\0'))) ||
+ ((*TempStr == '0') && (*(TempStr + 1) == '0') && (*(TempStr + 2) == '0') &&
+ ((*(TempStr + 3) == ':') || (*(TempStr + 3) == '\0')))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ Cnt = 0;
+ while ((TempStr[Cnt] != ':') && (TempStr[Cnt] != '\0')) {
+ Cnt++;
+ }
+ if (LeadZeroCnt == 0) {
+ if ((Cnt == 4) && (*TempStr == '0')) {
+ LeadZero = TRUE;
+ LeadZeroCnt++;
+ }
+ if ((Cnt != 0) && (Cnt < 4)) {
+ LeadZero = FALSE;
+ LeadZeroCnt++;
+ }
+ } else {
+ if ((Cnt == 4) && (*TempStr == '0') && !LeadZero) {
+ return EFI_INVALID_PARAMETER;
+ }
+ if ((Cnt != 0) && (Cnt < 4) && LeadZero) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ Ip6Address->Addr[Index] = (UINT8) (NodeVal >> 8);
+ Ip6Address->Addr[Index + 1] = (UINT8) (NodeVal & 0xFF);
+
+ //
+ // Skip the groups of zeros by ::
+ //
+ if (Short && Update) {
+ Index = (UINT8) (16 - (TailNodeCnt + 2) * 2);
+ Update = FALSE;
+ }
+ }
+
+ if ((!Short && Index != 16) || (*Ip6Str != '\0')) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ 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.
+ @retval EFI_OUT_OF_RESOURCES Fail to perform the operation due to lack of resource.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibStrToIp4 (
+ IN CONST CHAR16 *String,
+ OUT EFI_IPv4_ADDRESS *Ip4Address
+ )
+{
+ CHAR8 *Ip4Str;
+ EFI_STATUS Status;
+
+ if ((String == NULL) || (Ip4Address == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Ip4Str = (CHAR8 *) AllocatePool ((StrLen (String) + 1) * sizeof (CHAR8));
+ if (Ip4Str == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ UnicodeStrToAsciiStr (String, Ip4Str);
+
+ Status = NetLibAsciiStrToIp4 (Ip4Str, Ip4Address);
+
+ FreePool (Ip4Str);
+
+ return Status;
+}
+
+
+/**
+ Convert one Null-terminated Unicode string to EFI_IPv6_ADDRESS. The format of
+ the string is defined in RFC 4291 - Text Pepresentation 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.
+ @retval EFI_OUT_OF_RESOURCES Fail to perform the operation due to lack of resource.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibStrToIp6 (
+ IN CONST CHAR16 *String,
+ OUT EFI_IPv6_ADDRESS *Ip6Address
+ )
+{
+ CHAR8 *Ip6Str;
+ EFI_STATUS Status;
+
+ if ((String == NULL) || (Ip6Address == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Ip6Str = (CHAR8 *) AllocatePool ((StrLen (String) + 1) * sizeof (CHAR8));
+ if (Ip6Str == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ UnicodeStrToAsciiStr (String, Ip6Str);
+
+ Status = NetLibAsciiStrToIp6 (Ip6Str, Ip6Address);
+
+ FreePool (Ip6Str);
+
+ return Status;
+}
+
+/**
+ Convert one Null-terminated Unicode string to EFI_IPv6_ADDRESS and prefix length.
+ The format of the string is defined in RFC 4291 - Text Pepresentation 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.
+ @retval EFI_OUT_OF_RESOURCES Fail to perform the operation due to lack of resource.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibStrToIp6andPrefix (
+ IN CONST CHAR16 *String,
+ OUT EFI_IPv6_ADDRESS *Ip6Address,
+ OUT UINT8 *PrefixLength
+ )
+{
+ CHAR8 *Ip6Str;
+ CHAR8 *PrefixStr;
+ CHAR8 *TempStr;
+ EFI_STATUS Status;
+ UINT8 Length;
+
+ if ((String == NULL) || (Ip6Address == NULL) || (PrefixLength == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Ip6Str = (CHAR8 *) AllocatePool ((StrLen (String) + 1) * sizeof (CHAR8));
+ if (Ip6Str == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ UnicodeStrToAsciiStr (String, Ip6Str);
+
+ //
+ // Get the sub string describing prefix length.
+ //
+ TempStr = Ip6Str;
+ while (*TempStr != '\0' && (*TempStr != '/')) {
+ TempStr++;
+ }
+
+ if (*TempStr == '/') {
+ PrefixStr = TempStr + 1;
+ } else {
+ PrefixStr = NULL;
+ }
+
+ //
+ // Get the sub string describing IPv6 address and convert it.
+ //
+ *TempStr = '\0';
+
+ Status = NetLibAsciiStrToIp6 (Ip6Str, Ip6Address);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // If input string doesn't indicate the prefix length, return 0xff.
+ //
+ Length = 0xFF;
+
+ //
+ // Convert the string to prefix length
+ //
+ if (PrefixStr != NULL) {
+
+ Status = EFI_INVALID_PARAMETER;
+ Length = 0;
+ while (*PrefixStr != '\0') {
+ if (NET_IS_DIGIT (*PrefixStr)) {
+ Length = (UINT8) (Length * 10 + (*PrefixStr - '0'));
+ if (Length >= IP6_PREFIX_NUM) {
+ goto Exit;
+ }
+ } else {
+ goto Exit;
+ }
+
+ PrefixStr++;
+ }
+ }
+
+ *PrefixLength = Length;
+ Status = EFI_SUCCESS;
+
+Exit:
+
+ FreePool (Ip6Str);
+ return Status;
+}
+
diff --git a/MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf b/MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf new file mode 100644 index 0000000000..9b11c8211d --- /dev/null +++ b/MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf @@ -0,0 +1,62 @@ +## @file
+# Instance of DxeNetLib.
+#
+# Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxeNetLib
+ 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_SAL_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 IPF EBC
+#
+
+[Sources]
+ DxeNetLib.c
+ NetBuffer.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ BaseMemoryLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ UefiLib
+ MemoryAllocationLib
+ DevicePathLib
+ HiiLib
+ PrintLib
+
+[Guids]
+ gEfiNicIp4ConfigVariableGuid
+
+[Protocols]
+ gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiManagedNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiManagedNetworkServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiComponentNameProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiComponentName2ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiHiiConfigRoutingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiHiiConfigAccessProtocolGuid # PROTOCOL ALWAYS_CONSUMED
diff --git a/MdeModulePkg/Library/DxeNetLib/NetBuffer.c b/MdeModulePkg/Library/DxeNetLib/NetBuffer.c new file mode 100644 index 0000000000..43bcae6600 --- /dev/null +++ b/MdeModulePkg/Library/DxeNetLib/NetBuffer.c @@ -0,0 +1,1844 @@ +/** @file
+ Network library functions providing net buffer operation support.
+
+Copyright (c) 2005 - 2010, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+**/
+
+#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 > 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;
+
+ while (Len > 1) {
+ Sum += *(UINT16 *) Bulk;
+ Bulk += 2;
+ Len -= 2;
+ }
+
+ //
+ // Add left-over byte, if any
+ //
+ if (Len > 0) {
+ Sum += *(UINT8 *) Bulk;
+ }
+
+ //
+ // Fold 32-bit sum to 16 bits
+ //
+ while ((Sum >> 16) != 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));
+}
+
|