/** @file | |
Network library. | |
Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> | |
(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <Uefi.h> | |
#include <IndustryStandard/SmBios.h> | |
#include <Protocol/DriverBinding.h> | |
#include <Protocol/ServiceBinding.h> | |
#include <Protocol/SimpleNetwork.h> | |
#include <Protocol/AdapterInformation.h> | |
#include <Protocol/ManagedNetwork.h> | |
#include <Protocol/Ip4Config2.h> | |
#include <Protocol/ComponentName.h> | |
#include <Protocol/ComponentName2.h> | |
#include <Guid/SmBios.h> | |
#include <Library/NetLib.h> | |
#include <Library/BaseLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/UefiRuntimeServicesTableLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/DevicePathLib.h> | |
#include <Library/PrintLib.h> | |
#include <Library/UefiLib.h> | |
#define NIC_ITEM_CONFIG_SIZE (sizeof (NIC_IP4_CONFIG_INFO) + sizeof (EFI_IP4_ROUTE_TABLE) * MAX_IP4_CONFIG_IN_VARIABLE) | |
#define DEFAULT_ZERO_START ((UINTN) ~0) | |
// | |
// All the supported IP4 masks 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 opened. Otherwise NULL | |
**/ | |
EFI_SIMPLE_NETWORK_PROTOCOL * | |
SyslogLocateSnp ( | |
VOID | |
) | |
{ | |
EFI_SIMPLE_NETWORK_PROTOCOL *Snp; | |
EFI_STATUS Status; | |
EFI_HANDLE *Handles; | |
UINTN HandleCount; | |
UINTN Index; | |
// | |
// Locate the handles which has SNP installed. | |
// | |
Handles = NULL; | |
Status = gBS->LocateHandleBuffer ( | |
ByProtocol, | |
&gEfiSimpleNetworkProtocolGuid, | |
NULL, | |
&HandleCount, | |
&Handles | |
); | |
if (EFI_ERROR (Status) || (HandleCount == 0)) { | |
return NULL; | |
} | |
// | |
// Try to open one of the ethernet SNP protocol to send packet | |
// | |
Snp = NULL; | |
for (Index = 0; Index < HandleCount; Index++) { | |
Status = gBS->HandleProtocol ( | |
Handles[Index], | |
&gEfiSimpleNetworkProtocolGuid, | |
(VOID **) &Snp | |
); | |
if ((Status == EFI_SUCCESS) && (Snp != NULL) && | |
(Snp->Mode->IfType == NET_IFTYPE_ETHERNET) && | |
(Snp->Mode->MaxPacketSize >= NET_SYSLOG_PACKET_LEN)) { | |
break; | |
} | |
Snp = NULL; | |
} | |
FreePool (Handles); | |
return Snp; | |
} | |
/** | |
Transmit a syslog packet synchronously through SNP. The Packet | |
already has the ethernet header prepended. This function should | |
fill in the source MAC because it will try to locate a SNP each | |
time it is called to avoid the problem if SNP is unloaded. | |
This code snip is copied from MNP. | |
If Packet is NULL, then ASSERT(). | |
@param[in] Packet The Syslog packet | |
@param[in] Length The length of the packet | |
@retval EFI_DEVICE_ERROR Failed to locate a usable SNP protocol | |
@retval EFI_TIMEOUT Timeout happened to send the packet. | |
@retval EFI_SUCCESS Packet is sent. | |
**/ | |
EFI_STATUS | |
SyslogSendPacket ( | |
IN CHAR8 *Packet, | |
IN UINT32 Length | |
) | |
{ | |
EFI_SIMPLE_NETWORK_PROTOCOL *Snp; | |
ETHER_HEAD *Ether; | |
EFI_STATUS Status; | |
EFI_EVENT TimeoutEvent; | |
UINT8 *TxBuf; | |
ASSERT (Packet != NULL); | |
Snp = SyslogLocateSnp (); | |
if (Snp == NULL) { | |
return EFI_DEVICE_ERROR; | |
} | |
Ether = (ETHER_HEAD *) Packet; | |
CopyMem (Ether->SrcMac, Snp->Mode->CurrentAddress.Addr, NET_ETHER_ADDR_LEN); | |
// | |
// Start the timeout event. | |
// | |
Status = gBS->CreateEvent ( | |
EVT_TIMER, | |
TPL_NOTIFY, | |
NULL, | |
NULL, | |
&TimeoutEvent | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
for (;;) { | |
// | |
// Transmit the packet through SNP. | |
// | |
Status = Snp->Transmit (Snp, 0, Length, Packet, NULL, NULL, NULL); | |
if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) { | |
Status = EFI_DEVICE_ERROR; | |
break; | |
} | |
// | |
// If Status is EFI_SUCCESS, the packet is put in the transmit queue. | |
// if Status is EFI_NOT_READY, the transmit engine of the network | |
// interface is busy. Both need to sync SNP. | |
// | |
TxBuf = NULL; | |
do { | |
// | |
// Get the recycled transmit buffer status. | |
// | |
Snp->GetStatus (Snp, NULL, (VOID **) &TxBuf); | |
if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { | |
Status = EFI_TIMEOUT; | |
break; | |
} | |
} while (TxBuf == NULL); | |
if ((Status == EFI_SUCCESS) || (Status == EFI_TIMEOUT)) { | |
break; | |
} | |
// | |
// Status is EFI_NOT_READY. Restart the timer event and | |
// call Snp->Transmit again. | |
// | |
gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT); | |
} | |
gBS->SetTimer (TimeoutEvent, TimerCancel, 0); | |
ON_EXIT: | |
gBS->CloseEvent (TimeoutEvent); | |
return Status; | |
} | |
/** | |
Build a syslog packet, including the Ethernet/Ip/Udp headers | |
and user's message. | |
@param[in] Level Syslog severity level | |
@param[in] Module The module that generates the log | |
@param[in] File The file that contains the current log | |
@param[in] Line The line of code in the File that contains the current log | |
@param[in] Message The log message | |
@param[in] BufLen The length of the Buf | |
@param[out] Buf The buffer to put the packet data | |
@return The length of the syslog packet built, 0 represents no packet is built. | |
**/ | |
UINT32 | |
SyslogBuildPacket ( | |
IN UINT32 Level, | |
IN UINT8 *Module, | |
IN UINT8 *File, | |
IN UINT32 Line, | |
IN UINT8 *Message, | |
IN UINT32 BufLen, | |
OUT CHAR8 *Buf | |
) | |
{ | |
EFI_STATUS Status; | |
ETHER_HEAD *Ether; | |
IP4_HEAD *Ip4; | |
EFI_UDP_HEADER *Udp4; | |
EFI_TIME Time; | |
UINT32 Pri; | |
UINT32 Len; | |
// | |
// Fill in the Ethernet header. Leave alone the source MAC. | |
// SyslogSendPacket will fill in the address for us. | |
// | |
Ether = (ETHER_HEAD *) Buf; | |
CopyMem (Ether->DstMac, mSyslogDstMac, NET_ETHER_ADDR_LEN); | |
ZeroMem (Ether->SrcMac, NET_ETHER_ADDR_LEN); | |
Ether->EtherType = HTONS (0x0800); // IPv4 protocol | |
Buf += sizeof (ETHER_HEAD); | |
BufLen -= sizeof (ETHER_HEAD); | |
// | |
// Fill in the IP header | |
// | |
Ip4 = (IP4_HEAD *) Buf; | |
Ip4->HeadLen = 5; | |
Ip4->Ver = 4; | |
Ip4->Tos = 0; | |
Ip4->TotalLen = 0; | |
Ip4->Id = (UINT16) mSyslogPacketSeq; | |
Ip4->Fragment = 0; | |
Ip4->Ttl = 16; | |
Ip4->Protocol = 0x11; | |
Ip4->Checksum = 0; | |
Ip4->Src = mSyslogSrcIp; | |
Ip4->Dst = mSyslogDstIp; | |
Buf += sizeof (IP4_HEAD); | |
BufLen -= sizeof (IP4_HEAD); | |
// | |
// Fill in the UDP header, Udp checksum is optional. Leave it zero. | |
// | |
Udp4 = (EFI_UDP_HEADER *) Buf; | |
Udp4->SrcPort = HTONS (514); | |
Udp4->DstPort = HTONS (514); | |
Udp4->Length = 0; | |
Udp4->Checksum = 0; | |
Buf += sizeof (EFI_UDP_HEADER); | |
BufLen -= sizeof (EFI_UDP_HEADER); | |
// | |
// Build the syslog message body with <PRI> Timestamp machine module Message | |
// | |
Pri = ((NET_SYSLOG_FACILITY & 31) << 3) | (Level & 7); | |
Status = gRT->GetTime (&Time, NULL); | |
if (EFI_ERROR (Status)) { | |
return 0; | |
} | |
// | |
// Use %a to format the ASCII strings, %s to format UNICODE strings | |
// | |
Len = 0; | |
Len += (UINT32) AsciiSPrint ( | |
Buf, | |
BufLen, | |
"<%d> %a %d %d:%d:%d ", | |
Pri, | |
mMonthName [Time.Month-1], | |
Time.Day, | |
Time.Hour, | |
Time.Minute, | |
Time.Second | |
); | |
Len += (UINT32) AsciiSPrint ( | |
Buf + Len, | |
BufLen - Len, | |
"Tiano %a: %a (Line: %d File: %a)", | |
Module, | |
Message, | |
Line, | |
File | |
); | |
Len ++; | |
// | |
// OK, patch the IP length/checksum and UDP length fields. | |
// | |
Len += sizeof (EFI_UDP_HEADER); | |
Udp4->Length = HTONS ((UINT16) Len); | |
Len += sizeof (IP4_HEAD); | |
Ip4->TotalLen = HTONS ((UINT16) Len); | |
Ip4->Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Ip4, sizeof (IP4_HEAD))); | |
return Len + sizeof (ETHER_HEAD); | |
} | |
/** | |
Allocate a buffer, then format the message to it. This is a | |
help function for the NET_DEBUG_XXX macros. The PrintArg of | |
these macros treats the variable length print parameters as a | |
single parameter, and pass it to the NetDebugASPrint. For | |
example, NET_DEBUG_TRACE ("Tcp", ("State transit to %a\n", Name)) | |
if extracted to: | |
NetDebugOutput ( | |
NETDEBUG_LEVEL_TRACE, | |
"Tcp", | |
__FILE__, | |
DEBUG_LINE_NUMBER, | |
NetDebugASPrint ("State transit to %a\n", Name) | |
) | |
If Format is NULL, then ASSERT(). | |
@param Format The ASCII format string. | |
@param ... The variable length parameter whose format is determined | |
by the Format string. | |
@return The buffer containing the formatted message, | |
or NULL if failed to allocate memory. | |
**/ | |
CHAR8 * | |
EFIAPI | |
NetDebugASPrint ( | |
IN CHAR8 *Format, | |
... | |
) | |
{ | |
VA_LIST Marker; | |
CHAR8 *Buf; | |
ASSERT (Format != NULL); | |
Buf = (CHAR8 *) AllocatePool (NET_DEBUG_MSG_LEN); | |
if (Buf == NULL) { | |
return NULL; | |
} | |
VA_START (Marker, Format); | |
AsciiVSPrint (Buf, NET_DEBUG_MSG_LEN, Format, Marker); | |
VA_END (Marker); | |
return Buf; | |
} | |
/** | |
Builds an UDP4 syslog packet and send it using SNP. | |
This function will locate a instance of SNP then send the message through it. | |
Because it isn't open the SNP BY_DRIVER, apply caution when using it. | |
@param Level The severity level of the message. | |
@param Module The Module that generates the log. | |
@param File The file that contains the log. | |
@param Line The exact line that contains the log. | |
@param Message The user message to log. | |
@retval EFI_INVALID_PARAMETER Any input parameter is invalid. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet. | |
@retval EFI_DEVICE_ERROR Device error occurs. | |
@retval EFI_SUCCESS The log is discard because that it is more verbose | |
than the mNetDebugLevelMax. Or, it has been sent out. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetDebugOutput ( | |
IN UINT32 Level, | |
IN UINT8 *Module, | |
IN UINT8 *File, | |
IN UINT32 Line, | |
IN UINT8 *Message | |
) | |
{ | |
CHAR8 *Packet; | |
UINT32 Len; | |
EFI_STATUS Status; | |
// | |
// Check whether the message should be sent out | |
// | |
if (Message == NULL || File == NULL || Module == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (Level > mNetDebugLevelMax) { | |
Status = EFI_SUCCESS; | |
goto ON_EXIT; | |
} | |
// | |
// Allocate a maximum of 1024 bytes, the caller should ensure | |
// that the message plus the ethernet/ip/udp header is shorter | |
// than this | |
// | |
Packet = (CHAR8 *) AllocatePool (NET_SYSLOG_PACKET_LEN); | |
if (Packet == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_EXIT; | |
} | |
// | |
// Build the message: Ethernet header + IP header + Udp Header + user data | |
// | |
Len = SyslogBuildPacket ( | |
Level, | |
Module, | |
File, | |
Line, | |
Message, | |
NET_SYSLOG_PACKET_LEN, | |
Packet | |
); | |
if (Len == 0) { | |
Status = EFI_DEVICE_ERROR; | |
} else { | |
mSyslogPacketSeq++; | |
Status = SyslogSendPacket (Packet, Len); | |
} | |
FreePool (Packet); | |
ON_EXIT: | |
FreePool (Message); | |
return Status; | |
} | |
/** | |
Return the length of the mask. | |
Return the length of the mask, the correct value is from 0 to 32. | |
If the mask is invalid, return the invalid length 33, which is IP4_MASK_NUM. | |
NetMask is in the host byte order. | |
@param[in] NetMask The netmask to get the length from. | |
@return The length of the netmask, IP4_MASK_NUM if the mask is invalid. | |
**/ | |
INTN | |
EFIAPI | |
NetGetMaskLength ( | |
IN IP4_ADDR NetMask | |
) | |
{ | |
INTN Index; | |
for (Index = 0; Index <= IP4_MASK_MAX; Index++) { | |
if (NetMask == gIp4AllMasks[Index]) { | |
break; | |
} | |
} | |
return Index; | |
} | |
/** | |
Return the class of the IP address, such as class A, B, C. | |
Addr is in host byte order. | |
[ATTENTION] | |
Classful addressing (IP class A/B/C) has been deprecated according to RFC4632. | |
Caller of this function could only check the returned value against | |
IP4_ADDR_CLASSD (multicast) or IP4_ADDR_CLASSE (reserved) now. | |
The address of class A starts with 0. | |
If the address belong to class A, return IP4_ADDR_CLASSA. | |
The address of class B starts with 10. | |
If the address belong to class B, return IP4_ADDR_CLASSB. | |
The address of class C starts with 110. | |
If the address belong to class C, return IP4_ADDR_CLASSC. | |
The address of class D starts with 1110. | |
If the address belong to class D, return IP4_ADDR_CLASSD. | |
The address of class E starts with 1111. | |
If the address belong to class E, return IP4_ADDR_CLASSE. | |
@param[in] Addr The address to get the class from. | |
@return IP address class, such as IP4_ADDR_CLASSA. | |
**/ | |
INTN | |
EFIAPI | |
NetGetIpClass ( | |
IN IP4_ADDR Addr | |
) | |
{ | |
UINT8 ByteOne; | |
ByteOne = (UINT8) (Addr >> 24); | |
if ((ByteOne & 0x80) == 0) { | |
return IP4_ADDR_CLASSA; | |
} else if ((ByteOne & 0xC0) == 0x80) { | |
return IP4_ADDR_CLASSB; | |
} else if ((ByteOne & 0xE0) == 0xC0) { | |
return IP4_ADDR_CLASSC; | |
} else if ((ByteOne & 0xF0) == 0xE0) { | |
return IP4_ADDR_CLASSD; | |
} else { | |
return IP4_ADDR_CLASSE; | |
} | |
} | |
/** | |
Check whether the IP is a valid unicast address according to | |
the netmask. | |
ASSERT if NetMask is zero. | |
If all bits of the host address of IP are 0 or 1, IP is also not a valid unicast address, | |
except when the originator is one of the endpoints of a point-to-point link with a 31-bit | |
mask (RFC3021), or a 32bit NetMask (all 0xFF) is used for special network environment (e.g. | |
PPP link). | |
@param[in] Ip The IP to check against. | |
@param[in] NetMask The mask of the IP. | |
@return TRUE if IP is a valid unicast address on the network, otherwise FALSE. | |
**/ | |
BOOLEAN | |
EFIAPI | |
NetIp4IsUnicast ( | |
IN IP4_ADDR Ip, | |
IN IP4_ADDR NetMask | |
) | |
{ | |
INTN MaskLength; | |
ASSERT (NetMask != 0); | |
if (Ip == 0 || IP4_IS_LOCAL_BROADCAST (Ip)) { | |
return FALSE; | |
} | |
MaskLength = NetGetMaskLength (NetMask); | |
ASSERT ((MaskLength >= 0) && (MaskLength <= IP4_MASK_NUM)); | |
if (MaskLength < 31) { | |
if (((Ip &~NetMask) == ~NetMask) || ((Ip &~NetMask) == 0)) { | |
return FALSE; | |
} | |
} | |
return TRUE; | |
} | |
/** | |
Check whether the incoming IPv6 address is a valid unicast address. | |
ASSERT if Ip6 is NULL. | |
If the address is a multicast address has binary 0xFF at the start, it is not | |
a valid unicast address. If the address is unspecified ::, it is not a valid | |
unicast address to be assigned to any node. If the address is loopback address | |
::1, it is also not a valid unicast address to be assigned to any physical | |
interface. | |
@param[in] Ip6 The IPv6 address to check against. | |
@return TRUE if Ip6 is a valid unicast address on the network, otherwise FALSE. | |
**/ | |
BOOLEAN | |
EFIAPI | |
NetIp6IsValidUnicast ( | |
IN EFI_IPv6_ADDRESS *Ip6 | |
) | |
{ | |
UINT8 Byte; | |
UINT8 Index; | |
ASSERT (Ip6 != NULL); | |
if (Ip6->Addr[0] == 0xFF) { | |
return FALSE; | |
} | |
for (Index = 0; Index < 15; Index++) { | |
if (Ip6->Addr[Index] != 0) { | |
return TRUE; | |
} | |
} | |
Byte = Ip6->Addr[Index]; | |
if (Byte == 0x0 || Byte == 0x1) { | |
return FALSE; | |
} | |
return TRUE; | |
} | |
/** | |
Check whether the incoming Ipv6 address is the unspecified address or not. | |
ASSERT if Ip6 is NULL. | |
@param[in] Ip6 - Ip6 address, in network order. | |
@retval TRUE - Yes, unspecified | |
@retval FALSE - No | |
**/ | |
BOOLEAN | |
EFIAPI | |
NetIp6IsUnspecifiedAddr ( | |
IN EFI_IPv6_ADDRESS *Ip6 | |
) | |
{ | |
UINT8 Index; | |
ASSERT (Ip6 != NULL); | |
for (Index = 0; Index < 16; Index++) { | |
if (Ip6->Addr[Index] != 0) { | |
return FALSE; | |
} | |
} | |
return TRUE; | |
} | |
/** | |
Check whether the incoming Ipv6 address is a link-local address. | |
ASSERT if Ip6 is NULL. | |
@param[in] Ip6 - Ip6 address, in network order. | |
@retval TRUE - Yes, link-local address | |
@retval FALSE - No | |
**/ | |
BOOLEAN | |
EFIAPI | |
NetIp6IsLinkLocalAddr ( | |
IN EFI_IPv6_ADDRESS *Ip6 | |
) | |
{ | |
UINT8 Index; | |
ASSERT (Ip6 != NULL); | |
if (Ip6->Addr[0] != 0xFE) { | |
return FALSE; | |
} | |
if (Ip6->Addr[1] != 0x80) { | |
return FALSE; | |
} | |
for (Index = 2; Index < 8; Index++) { | |
if (Ip6->Addr[Index] != 0) { | |
return FALSE; | |
} | |
} | |
return TRUE; | |
} | |
/** | |
Check whether the Ipv6 address1 and address2 are on the connected network. | |
ASSERT if Ip1 or Ip2 is NULL. | |
ASSERT if PrefixLength exceeds or equals to IP6_PREFIX_MAX. | |
@param[in] Ip1 - Ip6 address1, in network order. | |
@param[in] Ip2 - Ip6 address2, in network order. | |
@param[in] PrefixLength - The prefix length of the checking net. | |
@retval TRUE - Yes, connected. | |
@retval FALSE - No. | |
**/ | |
BOOLEAN | |
EFIAPI | |
NetIp6IsNetEqual ( | |
EFI_IPv6_ADDRESS *Ip1, | |
EFI_IPv6_ADDRESS *Ip2, | |
UINT8 PrefixLength | |
) | |
{ | |
UINT8 Byte; | |
UINT8 Bit; | |
UINT8 Mask; | |
ASSERT ((Ip1 != NULL) && (Ip2 != NULL) && (PrefixLength < IP6_PREFIX_MAX)); | |
if (PrefixLength == 0) { | |
return TRUE; | |
} | |
Byte = (UINT8) (PrefixLength / 8); | |
Bit = (UINT8) (PrefixLength % 8); | |
if (CompareMem (Ip1, Ip2, Byte) != 0) { | |
return FALSE; | |
} | |
if (Bit > 0) { | |
Mask = (UINT8) (0xFF << (8 - Bit)); | |
ASSERT (Byte < 16); | |
if (Byte >= 16) { | |
return FALSE; | |
} | |
if ((Ip1->Addr[Byte] & Mask) != (Ip2->Addr[Byte] & Mask)) { | |
return FALSE; | |
} | |
} | |
return TRUE; | |
} | |
/** | |
Switches the endianess of an IPv6 address | |
ASSERT if Ip6 is NULL. | |
This function swaps the bytes in a 128-bit IPv6 address to switch the value | |
from little endian to big endian or vice versa. The byte swapped value is | |
returned. | |
@param Ip6 Points to an IPv6 address | |
@return The byte swapped IPv6 address. | |
**/ | |
EFI_IPv6_ADDRESS * | |
EFIAPI | |
Ip6Swap128 ( | |
EFI_IPv6_ADDRESS *Ip6 | |
) | |
{ | |
UINT64 High; | |
UINT64 Low; | |
ASSERT (Ip6 != NULL); | |
CopyMem (&High, Ip6, sizeof (UINT64)); | |
CopyMem (&Low, &Ip6->Addr[8], sizeof (UINT64)); | |
High = SwapBytes64 (High); | |
Low = SwapBytes64 (Low); | |
CopyMem (Ip6, &Low, sizeof (UINT64)); | |
CopyMem (&Ip6->Addr[8], &High, sizeof (UINT64)); | |
return Ip6; | |
} | |
/** | |
Initialize a random seed using current time and monotonic count. | |
Get current time and monotonic count first. Then initialize a random seed | |
based on some basic mathematics operation on the hour, day, minute, second, | |
nanosecond and year of the current time and the monotonic count value. | |
@return The random seed initialized with current time. | |
**/ | |
UINT32 | |
EFIAPI | |
NetRandomInitSeed ( | |
VOID | |
) | |
{ | |
EFI_TIME Time; | |
UINT32 Seed; | |
UINT64 MonotonicCount; | |
gRT->GetTime (&Time, NULL); | |
Seed = (Time.Hour << 24 | Time.Day << 16 | Time.Minute << 8 | Time.Second); | |
Seed ^= Time.Nanosecond; | |
Seed ^= Time.Year << 7; | |
gBS->GetNextMonotonicCount (&MonotonicCount); | |
Seed += (UINT32) MonotonicCount; | |
return Seed; | |
} | |
/** | |
Extract a UINT32 from a byte stream. | |
ASSERT if Buf is NULL. | |
Copy a UINT32 from a byte stream, then converts it from Network | |
byte order to host byte order. Use this function to avoid alignment error. | |
@param[in] Buf The buffer to extract the UINT32. | |
@return The UINT32 extracted. | |
**/ | |
UINT32 | |
EFIAPI | |
NetGetUint32 ( | |
IN UINT8 *Buf | |
) | |
{ | |
UINT32 Value; | |
ASSERT (Buf != NULL); | |
CopyMem (&Value, Buf, sizeof (UINT32)); | |
return NTOHL (Value); | |
} | |
/** | |
Put a UINT32 to the byte stream in network byte order. | |
ASSERT if Buf is NULL. | |
Converts a UINT32 from host byte order to network byte order. Then copy it to the | |
byte stream. | |
@param[in, out] Buf The buffer to put the UINT32. | |
@param[in] Data The data to be converted and put into the byte stream. | |
**/ | |
VOID | |
EFIAPI | |
NetPutUint32 ( | |
IN OUT UINT8 *Buf, | |
IN UINT32 Data | |
) | |
{ | |
ASSERT (Buf != NULL); | |
Data = HTONL (Data); | |
CopyMem (Buf, &Data, sizeof (UINT32)); | |
} | |
/** | |
Remove the first node entry on the list, and return the removed node entry. | |
Removes the first node Entry from a doubly linked list. It is up to the caller of | |
this function to release the memory used by the first node if that is required. On | |
exit, the removed node is returned. | |
If Head is NULL, then ASSERT(). | |
If Head was not initialized, then ASSERT(). | |
If PcdMaximumLinkedListLength is not zero, and the number of nodes in the | |
linked list including the head node is greater than or equal to PcdMaximumLinkedListLength, | |
then ASSERT(). | |
@param[in, out] Head The list header. | |
@return The first node entry that is removed from the list, NULL if the list is empty. | |
**/ | |
LIST_ENTRY * | |
EFIAPI | |
NetListRemoveHead ( | |
IN OUT LIST_ENTRY *Head | |
) | |
{ | |
LIST_ENTRY *First; | |
ASSERT (Head != NULL); | |
if (IsListEmpty (Head)) { | |
return NULL; | |
} | |
First = Head->ForwardLink; | |
Head->ForwardLink = First->ForwardLink; | |
First->ForwardLink->BackLink = Head; | |
DEBUG_CODE ( | |
First->ForwardLink = (LIST_ENTRY *) NULL; | |
First->BackLink = (LIST_ENTRY *) NULL; | |
); | |
return First; | |
} | |
/** | |
Remove the last node entry on the list and and return the removed node entry. | |
Removes the last node entry from a doubly linked list. It is up to the caller of | |
this function to release the memory used by the first node if that is required. On | |
exit, the removed node is returned. | |
If Head is NULL, then ASSERT(). | |
If Head was not initialized, then ASSERT(). | |
If PcdMaximumLinkedListLength is not zero, and the number of nodes in the | |
linked list including the head node is greater than or equal to PcdMaximumLinkedListLength, | |
then ASSERT(). | |
@param[in, out] Head The list head. | |
@return The last node entry that is removed from the list, NULL if the list is empty. | |
**/ | |
LIST_ENTRY * | |
EFIAPI | |
NetListRemoveTail ( | |
IN OUT LIST_ENTRY *Head | |
) | |
{ | |
LIST_ENTRY *Last; | |
ASSERT (Head != NULL); | |
if (IsListEmpty (Head)) { | |
return NULL; | |
} | |
Last = Head->BackLink; | |
Head->BackLink = Last->BackLink; | |
Last->BackLink->ForwardLink = Head; | |
DEBUG_CODE ( | |
Last->ForwardLink = (LIST_ENTRY *) NULL; | |
Last->BackLink = (LIST_ENTRY *) NULL; | |
); | |
return Last; | |
} | |
/** | |
Insert a new node entry after a designated node entry of a doubly linked list. | |
ASSERT if PrevEntry or NewEntry is NULL. | |
Inserts a new node entry donated by NewEntry after the node entry donated by PrevEntry | |
of the doubly linked list. | |
@param[in, out] PrevEntry The previous entry to insert after. | |
@param[in, out] NewEntry The new entry to insert. | |
**/ | |
VOID | |
EFIAPI | |
NetListInsertAfter ( | |
IN OUT LIST_ENTRY *PrevEntry, | |
IN OUT LIST_ENTRY *NewEntry | |
) | |
{ | |
ASSERT (PrevEntry != NULL && NewEntry != NULL); | |
NewEntry->BackLink = PrevEntry; | |
NewEntry->ForwardLink = PrevEntry->ForwardLink; | |
PrevEntry->ForwardLink->BackLink = NewEntry; | |
PrevEntry->ForwardLink = NewEntry; | |
} | |
/** | |
Insert a new node entry before a designated node entry of a doubly linked list. | |
ASSERT if PostEntry or NewEntry is NULL. | |
Inserts a new node entry donated by NewEntry after the node entry donated by PostEntry | |
of the doubly linked list. | |
@param[in, out] PostEntry The entry to insert before. | |
@param[in, out] NewEntry The new entry to insert. | |
**/ | |
VOID | |
EFIAPI | |
NetListInsertBefore ( | |
IN OUT LIST_ENTRY *PostEntry, | |
IN OUT LIST_ENTRY *NewEntry | |
) | |
{ | |
ASSERT (PostEntry != NULL && NewEntry != NULL); | |
NewEntry->ForwardLink = PostEntry; | |
NewEntry->BackLink = PostEntry->BackLink; | |
PostEntry->BackLink->ForwardLink = NewEntry; | |
PostEntry->BackLink = NewEntry; | |
} | |
/** | |
Safe destroy nodes in a linked list, and return the length of the list after all possible operations finished. | |
Destroy network child instance list by list traversals is not safe due to graph dependencies between nodes. | |
This function performs a safe traversal to destroy these nodes by checking to see if the node being destroyed | |
has been removed from the list or not. | |
If it has been removed, then restart the traversal from the head. | |
If it hasn't been removed, then continue with the next node directly. | |
This function will end the iterate and return the CallBack's last return value if error happens, | |
or return EFI_SUCCESS if 2 complete passes are made with no changes in the number of children in the list. | |
@param[in] List The head of the list. | |
@param[in] CallBack Pointer to the callback function to destroy one node in the list. | |
@param[in] Context Pointer to the callback function's context: corresponds to the | |
parameter Context in NET_DESTROY_LINK_LIST_CALLBACK. | |
@param[out] ListLength The length of the link list if the function returns successfully. | |
@retval EFI_SUCCESS Two complete passes are made with no changes in the number of children. | |
@retval EFI_INVALID_PARAMETER The input parameter is invalid. | |
@retval Others Return the CallBack's last return value. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetDestroyLinkList ( | |
IN LIST_ENTRY *List, | |
IN NET_DESTROY_LINK_LIST_CALLBACK CallBack, | |
IN VOID *Context, OPTIONAL | |
OUT UINTN *ListLength OPTIONAL | |
) | |
{ | |
UINTN PreviousLength; | |
LIST_ENTRY *Entry; | |
LIST_ENTRY *Ptr; | |
UINTN Length; | |
EFI_STATUS Status; | |
if (List == NULL || CallBack == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Length = 0; | |
do { | |
PreviousLength = Length; | |
Entry = GetFirstNode (List); | |
while (!IsNull (List, Entry)) { | |
Status = CallBack (Entry, Context); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Walk through the list to see whether the Entry has been removed or not. | |
// If the Entry still exists, just try to destroy the next one. | |
// If not, go back to the start point to iterate the list again. | |
// | |
for (Ptr = List->ForwardLink; Ptr != List; Ptr = Ptr->ForwardLink) { | |
if (Ptr == Entry) { | |
break; | |
} | |
} | |
if (Ptr == Entry) { | |
Entry = GetNextNode (List, Entry); | |
} else { | |
Entry = GetFirstNode (List); | |
} | |
} | |
for (Length = 0, Ptr = List->ForwardLink; Ptr != List; Length++, Ptr = Ptr->ForwardLink); | |
} while (Length != PreviousLength); | |
if (ListLength != NULL) { | |
*ListLength = Length; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
This function checks the input Handle to see if it's one of these handles in ChildHandleBuffer. | |
@param[in] Handle Handle to be checked. | |
@param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. | |
@param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL | |
if NumberOfChildren is 0. | |
@retval TRUE Found the input Handle in ChildHandleBuffer. | |
@retval FALSE Can't find the input Handle in ChildHandleBuffer. | |
**/ | |
BOOLEAN | |
EFIAPI | |
NetIsInHandleBuffer ( | |
IN EFI_HANDLE Handle, | |
IN UINTN NumberOfChildren, | |
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL | |
) | |
{ | |
UINTN Index; | |
if (NumberOfChildren == 0 || ChildHandleBuffer == NULL) { | |
return FALSE; | |
} | |
for (Index = 0; Index < NumberOfChildren; Index++) { | |
if (Handle == ChildHandleBuffer[Index]) { | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
/** | |
Initialize the netmap. Netmap is a reposity to keep the <Key, Value> pairs. | |
Initialize the forward and backward links of two head nodes donated by Map->Used | |
and Map->Recycled of two doubly linked lists. | |
Initializes the count of the <Key, Value> pairs in the netmap to zero. | |
If Map is NULL, then ASSERT(). | |
If the address of Map->Used is NULL, then ASSERT(). | |
If the address of Map->Recycled is NULl, then ASSERT(). | |
@param[in, out] Map The netmap to initialize. | |
**/ | |
VOID | |
EFIAPI | |
NetMapInit ( | |
IN OUT NET_MAP *Map | |
) | |
{ | |
ASSERT (Map != NULL); | |
InitializeListHead (&Map->Used); | |
InitializeListHead (&Map->Recycled); | |
Map->Count = 0; | |
} | |
/** | |
To clean up the netmap, that is, release allocated memories. | |
Removes all nodes of the Used doubly linked list and free memory of all related netmap items. | |
Removes all nodes of the Recycled doubly linked list and free memory of all related netmap items. | |
The number of the <Key, Value> pairs in the netmap is set to be zero. | |
If Map is NULL, then ASSERT(). | |
@param[in, out] Map The netmap to clean up. | |
**/ | |
VOID | |
EFIAPI | |
NetMapClean ( | |
IN OUT NET_MAP *Map | |
) | |
{ | |
NET_MAP_ITEM *Item; | |
LIST_ENTRY *Entry; | |
LIST_ENTRY *Next; | |
ASSERT (Map != NULL); | |
NET_LIST_FOR_EACH_SAFE (Entry, Next, &Map->Used) { | |
Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link); | |
RemoveEntryList (&Item->Link); | |
Map->Count--; | |
gBS->FreePool (Item); | |
} | |
ASSERT ((Map->Count == 0) && IsListEmpty (&Map->Used)); | |
NET_LIST_FOR_EACH_SAFE (Entry, Next, &Map->Recycled) { | |
Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link); | |
RemoveEntryList (&Item->Link); | |
gBS->FreePool (Item); | |
} | |
ASSERT (IsListEmpty (&Map->Recycled)); | |
} | |
/** | |
Test whether the netmap is empty and return true if it is. | |
If the number of the <Key, Value> pairs in the netmap is zero, return TRUE. | |
If Map is NULL, then ASSERT(). | |
@param[in] Map The net map to test. | |
@return TRUE if the netmap is empty, otherwise FALSE. | |
**/ | |
BOOLEAN | |
EFIAPI | |
NetMapIsEmpty ( | |
IN NET_MAP *Map | |
) | |
{ | |
ASSERT (Map != NULL); | |
return (BOOLEAN) (Map->Count == 0); | |
} | |
/** | |
Return the number of the <Key, Value> pairs in the netmap. | |
If Map is NULL, then ASSERT(). | |
@param[in] Map The netmap to get the entry number. | |
@return The entry number in the netmap. | |
**/ | |
UINTN | |
EFIAPI | |
NetMapGetCount ( | |
IN NET_MAP *Map | |
) | |
{ | |
ASSERT (Map != NULL); | |
return Map->Count; | |
} | |
/** | |
Return one allocated item. | |
If the Recycled doubly linked list of the netmap is empty, it will try to allocate | |
a batch of items if there are enough resources and add corresponding nodes to the beginning | |
of the Recycled doubly linked list of the netmap. Otherwise, it will directly remove | |
the fist node entry of the Recycled doubly linked list and return the corresponding item. | |
If Map is NULL, then ASSERT(). | |
@param[in, out] Map The netmap to allocate item for. | |
@return The allocated item. If NULL, the | |
allocation failed due to resource limit. | |
**/ | |
NET_MAP_ITEM * | |
NetMapAllocItem ( | |
IN OUT NET_MAP *Map | |
) | |
{ | |
NET_MAP_ITEM *Item; | |
LIST_ENTRY *Head; | |
UINTN Index; | |
ASSERT (Map != NULL); | |
Head = &Map->Recycled; | |
if (IsListEmpty (Head)) { | |
for (Index = 0; Index < NET_MAP_INCREAMENT; Index++) { | |
Item = AllocatePool (sizeof (NET_MAP_ITEM)); | |
if (Item == NULL) { | |
if (Index == 0) { | |
return NULL; | |
} | |
break; | |
} | |
InsertHeadList (Head, &Item->Link); | |
} | |
} | |
Item = NET_LIST_HEAD (Head, NET_MAP_ITEM, Link); | |
NetListRemoveHead (Head); | |
return Item; | |
} | |
/** | |
Allocate an item to save the <Key, Value> pair to the head of the netmap. | |
Allocate an item to save the <Key, Value> pair and add corresponding node entry | |
to the beginning of the Used doubly linked list. The number of the <Key, Value> | |
pairs in the netmap increase by 1. | |
If Map is NULL, then ASSERT(). | |
If Key is NULL, then ASSERT(). | |
@param[in, out] Map The netmap to insert into. | |
@param[in] Key The user's key. | |
@param[in] Value The user's value for the key. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the item. | |
@retval EFI_SUCCESS The item is inserted to the head. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetMapInsertHead ( | |
IN OUT NET_MAP *Map, | |
IN VOID *Key, | |
IN VOID *Value OPTIONAL | |
) | |
{ | |
NET_MAP_ITEM *Item; | |
ASSERT (Map != NULL && Key != NULL); | |
Item = NetMapAllocItem (Map); | |
if (Item == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Item->Key = Key; | |
Item->Value = Value; | |
InsertHeadList (&Map->Used, &Item->Link); | |
Map->Count++; | |
return EFI_SUCCESS; | |
} | |
/** | |
Allocate an item to save the <Key, Value> pair to the tail of the netmap. | |
Allocate an item to save the <Key, Value> pair and add corresponding node entry | |
to the tail of the Used doubly linked list. The number of the <Key, Value> | |
pairs in the netmap increase by 1. | |
If Map is NULL, then ASSERT(). | |
If Key is NULL, then ASSERT(). | |
@param[in, out] Map The netmap to insert into. | |
@param[in] Key The user's key. | |
@param[in] Value The user's value for the key. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the item. | |
@retval EFI_SUCCESS The item is inserted to the tail. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetMapInsertTail ( | |
IN OUT NET_MAP *Map, | |
IN VOID *Key, | |
IN VOID *Value OPTIONAL | |
) | |
{ | |
NET_MAP_ITEM *Item; | |
ASSERT (Map != NULL && Key != NULL); | |
Item = NetMapAllocItem (Map); | |
if (Item == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Item->Key = Key; | |
Item->Value = Value; | |
InsertTailList (&Map->Used, &Item->Link); | |
Map->Count++; | |
return EFI_SUCCESS; | |
} | |
/** | |
Check whether the item is in the Map and return TRUE if it is. | |
If Map is NULL, then ASSERT(). | |
If Item is NULL, then ASSERT(). | |
@param[in] Map The netmap to search within. | |
@param[in] Item The item to search. | |
@return TRUE if the item is in the netmap, otherwise FALSE. | |
**/ | |
BOOLEAN | |
NetItemInMap ( | |
IN NET_MAP *Map, | |
IN NET_MAP_ITEM *Item | |
) | |
{ | |
LIST_ENTRY *ListEntry; | |
ASSERT (Map != NULL && Item != NULL); | |
NET_LIST_FOR_EACH (ListEntry, &Map->Used) { | |
if (ListEntry == &Item->Link) { | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
/** | |
Find the key in the netmap and returns the point to the item contains the Key. | |
Iterate the Used doubly linked list of the netmap to get every item. Compare the key of every | |
item with the key to search. It returns the point to the item contains the Key if found. | |
If Map is NULL, then ASSERT(). | |
If Key is NULL, then ASSERT(). | |
@param[in] Map The netmap to search within. | |
@param[in] Key The key to search. | |
@return The point to the item contains the Key, or NULL if Key isn't in the map. | |
**/ | |
NET_MAP_ITEM * | |
EFIAPI | |
NetMapFindKey ( | |
IN NET_MAP *Map, | |
IN VOID *Key | |
) | |
{ | |
LIST_ENTRY *Entry; | |
NET_MAP_ITEM *Item; | |
ASSERT (Map != NULL && Key != NULL); | |
NET_LIST_FOR_EACH (Entry, &Map->Used) { | |
Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link); | |
if (Item->Key == Key) { | |
return Item; | |
} | |
} | |
return NULL; | |
} | |
/** | |
Remove the node entry of the item from the netmap and return the key of the removed item. | |
Remove the node entry of the item from the Used doubly linked list of the netmap. | |
The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node | |
entry of the item to the Recycled doubly linked list of the netmap. If Value is not NULL, | |
Value will point to the value of the item. It returns the key of the removed item. | |
If Map is NULL, then ASSERT(). | |
If Item is NULL, then ASSERT(). | |
if item in not in the netmap, then ASSERT(). | |
@param[in, out] Map The netmap to remove the item from. | |
@param[in, out] Item The item to remove. | |
@param[out] Value The variable to receive the value if not NULL. | |
@return The key of the removed item. | |
**/ | |
VOID * | |
EFIAPI | |
NetMapRemoveItem ( | |
IN OUT NET_MAP *Map, | |
IN OUT NET_MAP_ITEM *Item, | |
OUT VOID **Value OPTIONAL | |
) | |
{ | |
ASSERT ((Map != NULL) && (Item != NULL)); | |
ASSERT (NetItemInMap (Map, Item)); | |
RemoveEntryList (&Item->Link); | |
Map->Count--; | |
InsertHeadList (&Map->Recycled, &Item->Link); | |
if (Value != NULL) { | |
*Value = Item->Value; | |
} | |
return Item->Key; | |
} | |
/** | |
Remove the first node entry on the netmap and return the key of the removed item. | |
Remove the first node entry from the Used doubly linked list of the netmap. | |
The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node | |
entry to the Recycled doubly linked list of the netmap. If parameter Value is not NULL, | |
parameter Value will point to the value of the item. It returns the key of the removed item. | |
If Map is NULL, then ASSERT(). | |
If the Used doubly linked list is empty, then ASSERT(). | |
@param[in, out] Map The netmap to remove the head from. | |
@param[out] Value The variable to receive the value if not NULL. | |
@return The key of the item removed. | |
**/ | |
VOID * | |
EFIAPI | |
NetMapRemoveHead ( | |
IN OUT NET_MAP *Map, | |
OUT VOID **Value OPTIONAL | |
) | |
{ | |
NET_MAP_ITEM *Item; | |
// | |
// Often, it indicates a programming error to remove | |
// the first entry in an empty list | |
// | |
ASSERT (Map && !IsListEmpty (&Map->Used)); | |
Item = NET_LIST_HEAD (&Map->Used, NET_MAP_ITEM, Link); | |
RemoveEntryList (&Item->Link); | |
Map->Count--; | |
InsertHeadList (&Map->Recycled, &Item->Link); | |
if (Value != NULL) { | |
*Value = Item->Value; | |
} | |
return Item->Key; | |
} | |
/** | |
Remove the last node entry on the netmap and return the key of the removed item. | |
Remove the last node entry from the Used doubly linked list of the netmap. | |
The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node | |
entry to the Recycled doubly linked list of the netmap. If parameter Value is not NULL, | |
parameter Value will point to the value of the item. It returns the key of the removed item. | |
If Map is NULL, then ASSERT(). | |
If the Used doubly linked list is empty, then ASSERT(). | |
@param[in, out] Map The netmap to remove the tail from. | |
@param[out] Value The variable to receive the value if not NULL. | |
@return The key of the item removed. | |
**/ | |
VOID * | |
EFIAPI | |
NetMapRemoveTail ( | |
IN OUT NET_MAP *Map, | |
OUT VOID **Value OPTIONAL | |
) | |
{ | |
NET_MAP_ITEM *Item; | |
// | |
// Often, it indicates a programming error to remove | |
// the last entry in an empty list | |
// | |
ASSERT (Map && !IsListEmpty (&Map->Used)); | |
Item = NET_LIST_TAIL (&Map->Used, NET_MAP_ITEM, Link); | |
RemoveEntryList (&Item->Link); | |
Map->Count--; | |
InsertHeadList (&Map->Recycled, &Item->Link); | |
if (Value != NULL) { | |
*Value = Item->Value; | |
} | |
return Item->Key; | |
} | |
/** | |
Iterate through the netmap and call CallBack for each item. | |
It will continue the traverse if CallBack returns EFI_SUCCESS, otherwise, break | |
from the loop. It returns the CallBack's last return value. This function is | |
delete safe for the current item. | |
If Map is NULL, then ASSERT(). | |
If CallBack is NULL, then ASSERT(). | |
@param[in] Map The Map to iterate through. | |
@param[in] CallBack The callback function to call for each item. | |
@param[in] Arg The opaque parameter to the callback. | |
@retval EFI_SUCCESS There is no item in the netmap or CallBack for each item | |
return EFI_SUCCESS. | |
@retval Others It returns the CallBack's last return value. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetMapIterate ( | |
IN NET_MAP *Map, | |
IN NET_MAP_CALLBACK CallBack, | |
IN VOID *Arg OPTIONAL | |
) | |
{ | |
LIST_ENTRY *Entry; | |
LIST_ENTRY *Next; | |
LIST_ENTRY *Head; | |
NET_MAP_ITEM *Item; | |
EFI_STATUS Result; | |
ASSERT ((Map != NULL) && (CallBack != NULL)); | |
Head = &Map->Used; | |
if (IsListEmpty (Head)) { | |
return EFI_SUCCESS; | |
} | |
NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) { | |
Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link); | |
Result = CallBack (Map, Item, Arg); | |
if (EFI_ERROR (Result)) { | |
return Result; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
This is the default unload handle for all the network drivers. | |
Disconnect the driver specified by ImageHandle from all the devices in the handle database. | |
Uninstall all the protocols installed in the driver entry point. | |
@param[in] ImageHandle The drivers' driver image. | |
@retval EFI_SUCCESS The image is unloaded. | |
@retval Others Failed to unload the image. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetLibDefaultUnload ( | |
IN EFI_HANDLE ImageHandle | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HANDLE *DeviceHandleBuffer; | |
UINTN DeviceHandleCount; | |
UINTN Index; | |
UINTN Index2; | |
EFI_DRIVER_BINDING_PROTOCOL *DriverBinding; | |
EFI_COMPONENT_NAME_PROTOCOL *ComponentName; | |
EFI_COMPONENT_NAME2_PROTOCOL *ComponentName2; | |
// | |
// Get the list of all the handles in the handle database. | |
// If there is an error getting the list, then the unload | |
// operation fails. | |
// | |
Status = gBS->LocateHandleBuffer ( | |
AllHandles, | |
NULL, | |
NULL, | |
&DeviceHandleCount, | |
&DeviceHandleBuffer | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
for (Index = 0; Index < DeviceHandleCount; Index++) { | |
Status = gBS->HandleProtocol ( | |
DeviceHandleBuffer[Index], | |
&gEfiDriverBindingProtocolGuid, | |
(VOID **) &DriverBinding | |
); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
if (DriverBinding->ImageHandle != ImageHandle) { | |
continue; | |
} | |
// | |
// Disconnect the driver specified by ImageHandle from all | |
// the devices in the handle database. | |
// | |
for (Index2 = 0; Index2 < DeviceHandleCount; Index2++) { | |
Status = gBS->DisconnectController ( | |
DeviceHandleBuffer[Index2], | |
DriverBinding->DriverBindingHandle, | |
NULL | |
); | |
} | |
// | |
// Uninstall all the protocols installed in the driver entry point | |
// | |
gBS->UninstallProtocolInterface ( | |
DriverBinding->DriverBindingHandle, | |
&gEfiDriverBindingProtocolGuid, | |
DriverBinding | |
); | |
Status = gBS->HandleProtocol ( | |
DeviceHandleBuffer[Index], | |
&gEfiComponentNameProtocolGuid, | |
(VOID **) &ComponentName | |
); | |
if (!EFI_ERROR (Status)) { | |
gBS->UninstallProtocolInterface ( | |
DriverBinding->DriverBindingHandle, | |
&gEfiComponentNameProtocolGuid, | |
ComponentName | |
); | |
} | |
Status = gBS->HandleProtocol ( | |
DeviceHandleBuffer[Index], | |
&gEfiComponentName2ProtocolGuid, | |
(VOID **) &ComponentName2 | |
); | |
if (!EFI_ERROR (Status)) { | |
gBS->UninstallProtocolInterface ( | |
DriverBinding->DriverBindingHandle, | |
&gEfiComponentName2ProtocolGuid, | |
ComponentName2 | |
); | |
} | |
} | |
// | |
// Free the buffer containing the list of handles from the handle database | |
// | |
if (DeviceHandleBuffer != NULL) { | |
gBS->FreePool (DeviceHandleBuffer); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Create a child of the service that is identified by ServiceBindingGuid. | |
Get the ServiceBinding Protocol first, then use it to create a child. | |
If ServiceBindingGuid is NULL, then ASSERT(). | |
If ChildHandle is NULL, then ASSERT(). | |
@param[in] Controller The controller which has the service installed. | |
@param[in] Image The image handle used to open service. | |
@param[in] ServiceBindingGuid The service's Guid. | |
@param[in, out] ChildHandle The handle to receive the create child. | |
@retval EFI_SUCCESS The child is successfully created. | |
@retval Others Failed to create the child. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetLibCreateServiceChild ( | |
IN EFI_HANDLE Controller, | |
IN EFI_HANDLE Image, | |
IN EFI_GUID *ServiceBindingGuid, | |
IN OUT EFI_HANDLE *ChildHandle | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SERVICE_BINDING_PROTOCOL *Service; | |
ASSERT ((ServiceBindingGuid != NULL) && (ChildHandle != NULL)); | |
// | |
// Get the ServiceBinding Protocol | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
ServiceBindingGuid, | |
(VOID **) &Service, | |
Image, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Create a child | |
// | |
Status = Service->CreateChild (Service, ChildHandle); | |
return Status; | |
} | |
/** | |
Destroy a child of the service that is identified by ServiceBindingGuid. | |
Get the ServiceBinding Protocol first, then use it to destroy a child. | |
If ServiceBindingGuid is NULL, then ASSERT(). | |
@param[in] Controller The controller which has the service installed. | |
@param[in] Image The image handle used to open service. | |
@param[in] ServiceBindingGuid The service's Guid. | |
@param[in] ChildHandle The child to destroy. | |
@retval EFI_SUCCESS The child is successfully destroyed. | |
@retval Others Failed to destroy the child. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetLibDestroyServiceChild ( | |
IN EFI_HANDLE Controller, | |
IN EFI_HANDLE Image, | |
IN EFI_GUID *ServiceBindingGuid, | |
IN EFI_HANDLE ChildHandle | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SERVICE_BINDING_PROTOCOL *Service; | |
ASSERT (ServiceBindingGuid != NULL); | |
// | |
// Get the ServiceBinding Protocol | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
ServiceBindingGuid, | |
(VOID **) &Service, | |
Image, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// destroy the child | |
// | |
Status = Service->DestroyChild (Service, ChildHandle); | |
return Status; | |
} | |
/** | |
Get handle with Simple Network Protocol installed on it. | |
There should be MNP Service Binding Protocol installed on the input ServiceHandle. | |
If Simple Network Protocol is already installed on the ServiceHandle, the | |
ServiceHandle will be returned. If SNP is not installed on the ServiceHandle, | |
try to find its parent handle with SNP installed. | |
@param[in] ServiceHandle The handle where network service binding protocols are | |
installed on. | |
@param[out] Snp The pointer to store the address of the SNP instance. | |
This is an optional parameter that may be NULL. | |
@return The SNP handle, or NULL if not found. | |
**/ | |
EFI_HANDLE | |
EFIAPI | |
NetLibGetSnpHandle ( | |
IN EFI_HANDLE ServiceHandle, | |
OUT EFI_SIMPLE_NETWORK_PROTOCOL **Snp OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SIMPLE_NETWORK_PROTOCOL *SnpInstance; | |
EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
EFI_HANDLE SnpHandle; | |
// | |
// Try to open SNP from ServiceHandle | |
// | |
SnpInstance = NULL; | |
Status = gBS->HandleProtocol (ServiceHandle, &gEfiSimpleNetworkProtocolGuid, (VOID **) &SnpInstance); | |
if (!EFI_ERROR (Status)) { | |
if (Snp != NULL) { | |
*Snp = SnpInstance; | |
} | |
return ServiceHandle; | |
} | |
// | |
// Failed to open SNP, try to get SNP handle by LocateDevicePath() | |
// | |
DevicePath = DevicePathFromHandle (ServiceHandle); | |
if (DevicePath == NULL) { | |
return NULL; | |
} | |
SnpHandle = NULL; | |
Status = gBS->LocateDevicePath (&gEfiSimpleNetworkProtocolGuid, &DevicePath, &SnpHandle); | |
if (EFI_ERROR (Status)) { | |
// | |
// Failed to find SNP handle | |
// | |
return NULL; | |
} | |
Status = gBS->HandleProtocol (SnpHandle, &gEfiSimpleNetworkProtocolGuid, (VOID **) &SnpInstance); | |
if (!EFI_ERROR (Status)) { | |
if (Snp != NULL) { | |
*Snp = SnpInstance; | |
} | |
return SnpHandle; | |
} | |
return NULL; | |
} | |
/** | |
Retrieve VLAN ID of a VLAN device handle. | |
Search VLAN device path node in Device Path of specified ServiceHandle and | |
return its VLAN ID. If no VLAN device path node found, then this ServiceHandle | |
is not a VLAN device handle, and 0 will be returned. | |
@param[in] ServiceHandle The handle where network service binding protocols are | |
installed on. | |
@return VLAN ID of the device handle, or 0 if not a VLAN device. | |
**/ | |
UINT16 | |
EFIAPI | |
NetLibGetVlanId ( | |
IN EFI_HANDLE ServiceHandle | |
) | |
{ | |
EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
EFI_DEVICE_PATH_PROTOCOL *Node; | |
DevicePath = DevicePathFromHandle (ServiceHandle); | |
if (DevicePath == NULL) { | |
return 0; | |
} | |
Node = DevicePath; | |
while (!IsDevicePathEnd (Node)) { | |
if (Node->Type == MESSAGING_DEVICE_PATH && Node->SubType == MSG_VLAN_DP) { | |
return ((VLAN_DEVICE_PATH *) Node)->VlanId; | |
} | |
Node = NextDevicePathNode (Node); | |
} | |
return 0; | |
} | |
/** | |
Find VLAN device handle with specified VLAN ID. | |
The VLAN child device handle is created by VLAN Config Protocol on ControllerHandle. | |
This function will append VLAN device path node to the parent device path, | |
and then use LocateDevicePath() to find the correct VLAN device handle. | |
@param[in] ControllerHandle The handle where network service binding protocols are | |
installed on. | |
@param[in] VlanId The configured VLAN ID for the VLAN device. | |
@return The VLAN device handle, or NULL if not found. | |
**/ | |
EFI_HANDLE | |
EFIAPI | |
NetLibGetVlanHandle ( | |
IN EFI_HANDLE ControllerHandle, | |
IN UINT16 VlanId | |
) | |
{ | |
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; | |
EFI_DEVICE_PATH_PROTOCOL *VlanDevicePath; | |
EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
VLAN_DEVICE_PATH VlanNode; | |
EFI_HANDLE Handle; | |
ParentDevicePath = DevicePathFromHandle (ControllerHandle); | |
if (ParentDevicePath == NULL) { | |
return NULL; | |
} | |
// | |
// Construct VLAN device path | |
// | |
CopyMem (&VlanNode, &mNetVlanDevicePathTemplate, sizeof (VLAN_DEVICE_PATH)); | |
VlanNode.VlanId = VlanId; | |
VlanDevicePath = AppendDevicePathNode ( | |
ParentDevicePath, | |
(EFI_DEVICE_PATH_PROTOCOL *) &VlanNode | |
); | |
if (VlanDevicePath == NULL) { | |
return NULL; | |
} | |
// | |
// Find VLAN device handle | |
// | |
Handle = NULL; | |
DevicePath = VlanDevicePath; | |
gBS->LocateDevicePath ( | |
&gEfiDevicePathProtocolGuid, | |
&DevicePath, | |
&Handle | |
); | |
if (!IsDevicePathEnd (DevicePath)) { | |
// | |
// Device path is not exactly match | |
// | |
Handle = NULL; | |
} | |
FreePool (VlanDevicePath); | |
return Handle; | |
} | |
/** | |
Get MAC address associated with the network service handle. | |
If MacAddress is NULL, then ASSERT(). | |
If AddressSize is NULL, then ASSERT(). | |
There should be MNP Service Binding Protocol installed on the input ServiceHandle. | |
If SNP is installed on the ServiceHandle or its parent handle, MAC address will | |
be retrieved from SNP. If no SNP found, try to get SNP mode data use MNP. | |
@param[in] ServiceHandle The handle where network service binding protocols are | |
installed on. | |
@param[out] MacAddress The pointer to store the returned MAC address. | |
@param[out] AddressSize The length of returned MAC address. | |
@retval EFI_SUCCESS MAC address is returned successfully. | |
@retval Others Failed to get SNP mode data. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetLibGetMacAddress ( | |
IN EFI_HANDLE ServiceHandle, | |
OUT EFI_MAC_ADDRESS *MacAddress, | |
OUT UINTN *AddressSize | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SIMPLE_NETWORK_PROTOCOL *Snp; | |
EFI_SIMPLE_NETWORK_MODE *SnpMode; | |
EFI_SIMPLE_NETWORK_MODE SnpModeData; | |
EFI_MANAGED_NETWORK_PROTOCOL *Mnp; | |
EFI_SERVICE_BINDING_PROTOCOL *MnpSb; | |
EFI_HANDLE SnpHandle; | |
EFI_HANDLE MnpChildHandle; | |
ASSERT (MacAddress != NULL); | |
ASSERT (AddressSize != NULL); | |
// | |
// Try to get SNP handle | |
// | |
Snp = NULL; | |
SnpHandle = NetLibGetSnpHandle (ServiceHandle, &Snp); | |
if (SnpHandle != NULL) { | |
// | |
// SNP found, use it directly | |
// | |
SnpMode = Snp->Mode; | |
} else { | |
// | |
// Failed to get SNP handle, try to get MAC address from MNP | |
// | |
MnpChildHandle = NULL; | |
Status = gBS->HandleProtocol ( | |
ServiceHandle, | |
&gEfiManagedNetworkServiceBindingProtocolGuid, | |
(VOID **) &MnpSb | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Create a MNP child | |
// | |
Status = MnpSb->CreateChild (MnpSb, &MnpChildHandle); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Open MNP protocol | |
// | |
Status = gBS->HandleProtocol ( | |
MnpChildHandle, | |
&gEfiManagedNetworkProtocolGuid, | |
(VOID **) &Mnp | |
); | |
if (EFI_ERROR (Status)) { | |
MnpSb->DestroyChild (MnpSb, MnpChildHandle); | |
return Status; | |
} | |
// | |
// Try to get SNP mode from MNP | |
// | |
Status = Mnp->GetModeData (Mnp, NULL, &SnpModeData); | |
if (EFI_ERROR (Status) && (Status != EFI_NOT_STARTED)) { | |
MnpSb->DestroyChild (MnpSb, MnpChildHandle); | |
return Status; | |
} | |
SnpMode = &SnpModeData; | |
// | |
// Destroy the MNP child | |
// | |
MnpSb->DestroyChild (MnpSb, MnpChildHandle); | |
} | |
*AddressSize = SnpMode->HwAddressSize; | |
CopyMem (MacAddress->Addr, SnpMode->CurrentAddress.Addr, SnpMode->HwAddressSize); | |
return EFI_SUCCESS; | |
} | |
/** | |
Convert MAC address of the NIC associated with specified Service Binding Handle | |
to a unicode string. Callers are responsible for freeing the string storage. | |
If MacString is NULL, then ASSERT(). | |
Locate simple network protocol associated with the Service Binding Handle and | |
get the mac address from SNP. Then convert the mac address into a unicode | |
string. It takes 2 unicode characters to represent a 1 byte binary buffer. | |
Plus one unicode character for the null-terminator. | |
@param[in] ServiceHandle The handle where network service binding protocol is | |
installed on. | |
@param[in] ImageHandle The image handle used to act as the agent handle to | |
get the simple network protocol. This parameter is | |
optional and may be NULL. | |
@param[out] MacString The pointer to store the address of the string | |
representation of the mac address. | |
@retval EFI_SUCCESS Convert the mac address a unicode string successfully. | |
@retval EFI_OUT_OF_RESOURCES There are not enough memory resource. | |
@retval Others Failed to open the simple network protocol. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetLibGetMacString ( | |
IN EFI_HANDLE ServiceHandle, | |
IN EFI_HANDLE ImageHandle, OPTIONAL | |
OUT CHAR16 **MacString | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_MAC_ADDRESS MacAddress; | |
UINT8 *HwAddress; | |
UINTN HwAddressSize; | |
UINT16 VlanId; | |
CHAR16 *String; | |
UINTN Index; | |
UINTN BufferSize; | |
ASSERT (MacString != NULL); | |
// | |
// Get MAC address of the network device | |
// | |
Status = NetLibGetMacAddress (ServiceHandle, &MacAddress, &HwAddressSize); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// It takes 2 unicode characters to represent a 1 byte binary buffer. | |
// If VLAN is configured, it will need extra 5 characters like "\0005". | |
// Plus one unicode character for the null-terminator. | |
// | |
BufferSize = (2 * HwAddressSize + 5 + 1) * sizeof (CHAR16); | |
String = AllocateZeroPool (BufferSize); | |
if (String == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
*MacString = String; | |
// | |
// Convert the MAC address into a unicode string. | |
// | |
HwAddress = &MacAddress.Addr[0]; | |
for (Index = 0; Index < HwAddressSize; Index++) { | |
UnicodeValueToStringS ( | |
String, | |
BufferSize - ((UINTN)String - (UINTN)*MacString), | |
PREFIX_ZERO | RADIX_HEX, | |
*(HwAddress++), | |
2 | |
); | |
String += StrnLenS (String, (BufferSize - ((UINTN)String - (UINTN)*MacString)) / sizeof (CHAR16)); | |
} | |
// | |
// Append VLAN ID if any | |
// | |
VlanId = NetLibGetVlanId (ServiceHandle); | |
if (VlanId != 0) { | |
*String++ = L'\\'; | |
UnicodeValueToStringS ( | |
String, | |
BufferSize - ((UINTN)String - (UINTN)*MacString), | |
PREFIX_ZERO | RADIX_HEX, | |
VlanId, | |
4 | |
); | |
String += StrnLenS (String, (BufferSize - ((UINTN)String - (UINTN)*MacString)) / sizeof (CHAR16)); | |
} | |
// | |
// Null terminate the Unicode string | |
// | |
*String = L'\0'; | |
return EFI_SUCCESS; | |
} | |
/** | |
Detect media status for specified network device. | |
If MediaPresent is NULL, then ASSERT(). | |
The underlying UNDI driver may or may not support reporting media status from | |
GET_STATUS command (PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED). This routine | |
will try to invoke Snp->GetStatus() to get the media status: if media already | |
present, it return directly; if media not present, it will stop SNP and then | |
restart SNP to get the latest media status, this give chance to get the correct | |
media status for old UNDI driver which doesn't support reporting media status | |
from GET_STATUS command. | |
Note: there will be two limitations for current algorithm: | |
1) for UNDI with this capability, in case of cable is not attached, there will | |
be an redundant Stop/Start() process; | |
2) for UNDI without this capability, in case that network cable is attached when | |
Snp->Initialize() is invoked while network cable is unattached later, | |
NetLibDetectMedia() will report MediaPresent as TRUE, causing upper layer | |
apps to wait for timeout time. | |
@param[in] ServiceHandle The handle where network service binding protocols are | |
installed on. | |
@param[out] MediaPresent The pointer to store the media status. | |
@retval EFI_SUCCESS Media detection success. | |
@retval EFI_INVALID_PARAMETER ServiceHandle is not valid network device handle. | |
@retval EFI_UNSUPPORTED Network device does not support media detection. | |
@retval EFI_DEVICE_ERROR SNP is in unknown state. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetLibDetectMedia ( | |
IN EFI_HANDLE ServiceHandle, | |
OUT BOOLEAN *MediaPresent | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HANDLE SnpHandle; | |
EFI_SIMPLE_NETWORK_PROTOCOL *Snp; | |
UINT32 InterruptStatus; | |
UINT32 OldState; | |
EFI_MAC_ADDRESS *MCastFilter; | |
UINT32 MCastFilterCount; | |
UINT32 EnableFilterBits; | |
UINT32 DisableFilterBits; | |
BOOLEAN ResetMCastFilters; | |
ASSERT (MediaPresent != NULL); | |
// | |
// Get SNP handle | |
// | |
Snp = NULL; | |
SnpHandle = NetLibGetSnpHandle (ServiceHandle, &Snp); | |
if (SnpHandle == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Check whether SNP support media detection | |
// | |
if (!Snp->Mode->MediaPresentSupported) { | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data | |
// | |
Status = Snp->GetStatus (Snp, &InterruptStatus, NULL); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (Snp->Mode->MediaPresent) { | |
// | |
// Media is present, return directly | |
// | |
*MediaPresent = TRUE; | |
return EFI_SUCCESS; | |
} | |
// | |
// Till now, GetStatus() report no media; while, in case UNDI not support | |
// reporting media status from GetStatus(), this media status may be incorrect. | |
// So, we will stop SNP and then restart it to get the correct media status. | |
// | |
OldState = Snp->Mode->State; | |
if (OldState >= EfiSimpleNetworkMaxState) { | |
return EFI_DEVICE_ERROR; | |
} | |
MCastFilter = NULL; | |
if (OldState == EfiSimpleNetworkInitialized) { | |
// | |
// SNP is already in use, need Shutdown/Stop and then Start/Initialize | |
// | |
// | |
// Backup current SNP receive filter settings | |
// | |
EnableFilterBits = Snp->Mode->ReceiveFilterSetting; | |
DisableFilterBits = Snp->Mode->ReceiveFilterMask ^ EnableFilterBits; | |
ResetMCastFilters = TRUE; | |
MCastFilterCount = Snp->Mode->MCastFilterCount; | |
if (MCastFilterCount != 0) { | |
MCastFilter = AllocateCopyPool ( | |
MCastFilterCount * sizeof (EFI_MAC_ADDRESS), | |
Snp->Mode->MCastFilter | |
); | |
ASSERT (MCastFilter != NULL); | |
if (MCastFilter == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto Exit; | |
} | |
ResetMCastFilters = FALSE; | |
} | |
// | |
// Shutdown/Stop the simple network | |
// | |
Status = Snp->Shutdown (Snp); | |
if (!EFI_ERROR (Status)) { | |
Status = Snp->Stop (Snp); | |
} | |
if (EFI_ERROR (Status)) { | |
goto Exit; | |
} | |
// | |
// Start/Initialize the simple network | |
// | |
Status = Snp->Start (Snp); | |
if (!EFI_ERROR (Status)) { | |
Status = Snp->Initialize (Snp, 0, 0); | |
} | |
if (EFI_ERROR (Status)) { | |
goto Exit; | |
} | |
// | |
// Here we get the correct media status | |
// | |
*MediaPresent = Snp->Mode->MediaPresent; | |
// | |
// Restore SNP receive filter settings | |
// | |
Status = Snp->ReceiveFilters ( | |
Snp, | |
EnableFilterBits, | |
DisableFilterBits, | |
ResetMCastFilters, | |
MCastFilterCount, | |
MCastFilter | |
); | |
if (MCastFilter != NULL) { | |
FreePool (MCastFilter); | |
} | |
return Status; | |
} | |
// | |
// SNP is not in use, it's in state of EfiSimpleNetworkStopped or EfiSimpleNetworkStarted | |
// | |
if (OldState == EfiSimpleNetworkStopped) { | |
// | |
// SNP not start yet, start it | |
// | |
Status = Snp->Start (Snp); | |
if (EFI_ERROR (Status)) { | |
goto Exit; | |
} | |
} | |
// | |
// Initialize the simple network | |
// | |
Status = Snp->Initialize (Snp, 0, 0); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto Exit; | |
} | |
// | |
// Here we get the correct media status | |
// | |
*MediaPresent = Snp->Mode->MediaPresent; | |
// | |
// Shut down the simple network | |
// | |
Snp->Shutdown (Snp); | |
Exit: | |
if (OldState == EfiSimpleNetworkStopped) { | |
// | |
// Original SNP sate is Stopped, restore to original state | |
// | |
Snp->Stop (Snp); | |
} | |
if (MCastFilter != NULL) { | |
FreePool (MCastFilter); | |
} | |
return Status; | |
} | |
/** | |
Detect media state for a network device. This routine will wait for a period of time at | |
a specified checking interval when a certain network is under connecting until connection | |
process finishs or timeout. If Aip protocol is supported by low layer drivers, three kinds | |
of media states can be detected: EFI_SUCCESS, EFI_NOT_READY and EFI_NO_MEDIA, represents | |
connected state, connecting state and no media state respectively. When function detects | |
the current state is EFI_NOT_READY, it will loop to wait for next time's check until state | |
turns to be EFI_SUCCESS or EFI_NO_MEDIA. If Aip protocol is not supported, function will | |
call NetLibDetectMedia() and return state directly. | |
@param[in] ServiceHandle The handle where network service binding protocols are | |
installed on. | |
@param[in] Timeout The maximum number of 100ns units to wait when network | |
is connecting. Zero value means detect once and return | |
immediately. | |
@param[out] MediaState The pointer to the detected media state. | |
@retval EFI_SUCCESS Media detection success. | |
@retval EFI_INVALID_PARAMETER ServiceHandle is not a valid network device handle or | |
MediaState pointer is NULL. | |
@retval EFI_DEVICE_ERROR A device error occurred. | |
@retval EFI_TIMEOUT Network is connecting but timeout. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetLibDetectMediaWaitTimeout ( | |
IN EFI_HANDLE ServiceHandle, | |
IN UINT64 Timeout, | |
OUT EFI_STATUS *MediaState | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HANDLE SnpHandle; | |
EFI_SIMPLE_NETWORK_PROTOCOL *Snp; | |
EFI_ADAPTER_INFORMATION_PROTOCOL *Aip; | |
EFI_ADAPTER_INFO_MEDIA_STATE *MediaInfo; | |
BOOLEAN MediaPresent; | |
UINTN DataSize; | |
EFI_STATUS TimerStatus; | |
EFI_EVENT Timer; | |
UINT64 TimeRemained; | |
if (MediaState == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
*MediaState = EFI_SUCCESS; | |
MediaInfo = NULL; | |
// | |
// Get SNP handle | |
// | |
Snp = NULL; | |
SnpHandle = NetLibGetSnpHandle (ServiceHandle, &Snp); | |
if (SnpHandle == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Status = gBS->HandleProtocol ( | |
SnpHandle, | |
&gEfiAdapterInformationProtocolGuid, | |
(VOID *) &Aip | |
); | |
if (EFI_ERROR (Status)) { | |
MediaPresent = TRUE; | |
Status = NetLibDetectMedia (ServiceHandle, &MediaPresent); | |
if (!EFI_ERROR (Status)) { | |
if (MediaPresent) { | |
*MediaState = EFI_SUCCESS; | |
} else { | |
*MediaState = EFI_NO_MEDIA; | |
} | |
} | |
// | |
// NetLibDetectMedia doesn't support EFI_NOT_READY status, return now! | |
// | |
return Status; | |
} | |
Status = Aip->GetInformation ( | |
Aip, | |
&gEfiAdapterInfoMediaStateGuid, | |
(VOID **) &MediaInfo, | |
&DataSize | |
); | |
if (!EFI_ERROR (Status)) { | |
*MediaState = MediaInfo->MediaState; | |
FreePool (MediaInfo); | |
if (*MediaState != EFI_NOT_READY || Timeout < MEDIA_STATE_DETECT_TIME_INTERVAL) { | |
return EFI_SUCCESS; | |
} | |
} else { | |
if (MediaInfo != NULL) { | |
FreePool (MediaInfo); | |
} | |
if (Status == EFI_UNSUPPORTED) { | |
// | |
// If gEfiAdapterInfoMediaStateGuid is not supported, call NetLibDetectMedia to get media state! | |
// | |
MediaPresent = TRUE; | |
Status = NetLibDetectMedia (ServiceHandle, &MediaPresent); | |
if (!EFI_ERROR (Status)) { | |
if (MediaPresent) { | |
*MediaState = EFI_SUCCESS; | |
} else { | |
*MediaState = EFI_NO_MEDIA; | |
} | |
} | |
return Status; | |
} | |
return Status; | |
} | |
// | |
// Loop to check media state | |
// | |
Timer = NULL; | |
TimeRemained = Timeout; | |
Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
do { | |
Status = gBS->SetTimer ( | |
Timer, | |
TimerRelative, | |
MEDIA_STATE_DETECT_TIME_INTERVAL | |
); | |
if (EFI_ERROR (Status)) { | |
gBS->CloseEvent(Timer); | |
return EFI_DEVICE_ERROR; | |
} | |
do { | |
TimerStatus = gBS->CheckEvent (Timer); | |
if (!EFI_ERROR (TimerStatus)) { | |
TimeRemained -= MEDIA_STATE_DETECT_TIME_INTERVAL; | |
Status = Aip->GetInformation ( | |
Aip, | |
&gEfiAdapterInfoMediaStateGuid, | |
(VOID **) &MediaInfo, | |
&DataSize | |
); | |
if (!EFI_ERROR (Status)) { | |
*MediaState = MediaInfo->MediaState; | |
FreePool (MediaInfo); | |
} else { | |
if (MediaInfo != NULL) { | |
FreePool (MediaInfo); | |
} | |
gBS->CloseEvent(Timer); | |
return Status; | |
} | |
} | |
} while (TimerStatus == EFI_NOT_READY); | |
} while (*MediaState == EFI_NOT_READY && TimeRemained >= MEDIA_STATE_DETECT_TIME_INTERVAL); | |
gBS->CloseEvent(Timer); | |
if (*MediaState == EFI_NOT_READY && TimeRemained < MEDIA_STATE_DETECT_TIME_INTERVAL) { | |
return EFI_TIMEOUT; | |
} else { | |
return EFI_SUCCESS; | |
} | |
} | |
/** | |
Check the default address used by the IPv4 driver is static or dynamic (acquired | |
from DHCP). | |
If the controller handle does not have the EFI_IP4_CONFIG2_PROTOCOL installed, the | |
default address is static. If failed to get the policy from Ip4 Config2 Protocol, | |
the default address is static. Otherwise, get the result from Ip4 Config2 Protocol. | |
@param[in] Controller The controller handle which has the EFI_IP4_CONFIG2_PROTOCOL | |
relative with the default address to judge. | |
@retval TRUE If the default address is static. | |
@retval FALSE If the default address is acquired from DHCP. | |
**/ | |
BOOLEAN | |
NetLibDefaultAddressIsStatic ( | |
IN EFI_HANDLE Controller | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; | |
UINTN DataSize; | |
EFI_IP4_CONFIG2_POLICY Policy; | |
BOOLEAN IsStatic; | |
Ip4Config2 = NULL; | |
DataSize = sizeof (EFI_IP4_CONFIG2_POLICY); | |
IsStatic = TRUE; | |
// | |
// Get Ip4Config2 policy. | |
// | |
Status = gBS->HandleProtocol (Controller, &gEfiIp4Config2ProtocolGuid, (VOID **) &Ip4Config2); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
Status = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypePolicy, &DataSize, &Policy); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
IsStatic = (BOOLEAN) (Policy == Ip4Config2PolicyStatic); | |
ON_EXIT: | |
return IsStatic; | |
} | |
/** | |
Create an IPv4 device path node. | |
If Node is NULL, then ASSERT(). | |
The header type of IPv4 device path node is MESSAGING_DEVICE_PATH. | |
The header subtype of IPv4 device path node is MSG_IPv4_DP. | |
Get other info from parameters to make up the whole IPv4 device path node. | |
@param[in, out] Node Pointer to the IPv4 device path node. | |
@param[in] Controller The controller handle. | |
@param[in] LocalIp The local IPv4 address. | |
@param[in] LocalPort The local port. | |
@param[in] RemoteIp The remote IPv4 address. | |
@param[in] RemotePort The remote port. | |
@param[in] Protocol The protocol type in the IP header. | |
@param[in] UseDefaultAddress Whether this instance is using default address or not. | |
**/ | |
VOID | |
EFIAPI | |
NetLibCreateIPv4DPathNode ( | |
IN OUT IPv4_DEVICE_PATH *Node, | |
IN EFI_HANDLE Controller, | |
IN IP4_ADDR LocalIp, | |
IN UINT16 LocalPort, | |
IN IP4_ADDR RemoteIp, | |
IN UINT16 RemotePort, | |
IN UINT16 Protocol, | |
IN BOOLEAN UseDefaultAddress | |
) | |
{ | |
ASSERT (Node != NULL); | |
Node->Header.Type = MESSAGING_DEVICE_PATH; | |
Node->Header.SubType = MSG_IPv4_DP; | |
SetDevicePathNodeLength (&Node->Header, sizeof (IPv4_DEVICE_PATH)); | |
CopyMem (&Node->LocalIpAddress, &LocalIp, sizeof (EFI_IPv4_ADDRESS)); | |
CopyMem (&Node->RemoteIpAddress, &RemoteIp, sizeof (EFI_IPv4_ADDRESS)); | |
Node->LocalPort = LocalPort; | |
Node->RemotePort = RemotePort; | |
Node->Protocol = Protocol; | |
if (!UseDefaultAddress) { | |
Node->StaticIpAddress = TRUE; | |
} else { | |
Node->StaticIpAddress = NetLibDefaultAddressIsStatic (Controller); | |
} | |
// | |
// Set the Gateway IP address to default value 0:0:0:0. | |
// Set the Subnet mask to default value 255:255:255:0. | |
// | |
ZeroMem (&Node->GatewayIpAddress, sizeof (EFI_IPv4_ADDRESS)); | |
SetMem (&Node->SubnetMask, sizeof (EFI_IPv4_ADDRESS), 0xff); | |
Node->SubnetMask.Addr[3] = 0; | |
} | |
/** | |
Create an IPv6 device path node. | |
If Node is NULL, then ASSERT(). | |
If LocalIp is NULL, then ASSERT(). | |
If RemoteIp is NULL, then ASSERT(). | |
The header type of IPv6 device path node is MESSAGING_DEVICE_PATH. | |
The header subtype of IPv6 device path node is MSG_IPv6_DP. | |
Get other info from parameters to make up the whole IPv6 device path node. | |
@param[in, out] Node Pointer to the IPv6 device path node. | |
@param[in] Controller The controller handle. | |
@param[in] LocalIp The local IPv6 address. | |
@param[in] LocalPort The local port. | |
@param[in] RemoteIp The remote IPv6 address. | |
@param[in] RemotePort The remote port. | |
@param[in] Protocol The protocol type in the IP header. | |
**/ | |
VOID | |
EFIAPI | |
NetLibCreateIPv6DPathNode ( | |
IN OUT IPv6_DEVICE_PATH *Node, | |
IN EFI_HANDLE Controller, | |
IN EFI_IPv6_ADDRESS *LocalIp, | |
IN UINT16 LocalPort, | |
IN EFI_IPv6_ADDRESS *RemoteIp, | |
IN UINT16 RemotePort, | |
IN UINT16 Protocol | |
) | |
{ | |
ASSERT (Node != NULL && LocalIp != NULL && RemoteIp != NULL); | |
Node->Header.Type = MESSAGING_DEVICE_PATH; | |
Node->Header.SubType = MSG_IPv6_DP; | |
SetDevicePathNodeLength (&Node->Header, sizeof (IPv6_DEVICE_PATH)); | |
CopyMem (&Node->LocalIpAddress, LocalIp, sizeof (EFI_IPv6_ADDRESS)); | |
CopyMem (&Node->RemoteIpAddress, RemoteIp, sizeof (EFI_IPv6_ADDRESS)); | |
Node->LocalPort = LocalPort; | |
Node->RemotePort = RemotePort; | |
Node->Protocol = Protocol; | |
// | |
// Set default value to IPAddressOrigin, PrefixLength. | |
// Set the Gateway IP address to unspecified address. | |
// | |
Node->IpAddressOrigin = 0; | |
Node->PrefixLength = IP6_PREFIX_LENGTH; | |
ZeroMem (&Node->GatewayIpAddress, sizeof (EFI_IPv6_ADDRESS)); | |
} | |
/** | |
Find the UNDI/SNP handle from controller and protocol GUID. | |
If ProtocolGuid is NULL, then ASSERT(). | |
For example, IP will open a MNP child to transmit/receive | |
packets, when MNP is stopped, IP should also be stopped. IP | |
needs to find its own private data which is related the IP's | |
service binding instance that is install on UNDI/SNP handle. | |
Now, the controller is either a MNP or ARP child handle. But | |
IP opens these handle BY_DRIVER, use that info, we can get the | |
UNDI/SNP handle. | |
@param[in] Controller Then protocol handle to check. | |
@param[in] ProtocolGuid The protocol that is related with the handle. | |
@return The UNDI/SNP handle or NULL for errors. | |
**/ | |
EFI_HANDLE | |
EFIAPI | |
NetLibGetNicHandle ( | |
IN EFI_HANDLE Controller, | |
IN EFI_GUID *ProtocolGuid | |
) | |
{ | |
EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenBuffer; | |
EFI_HANDLE Handle; | |
EFI_STATUS Status; | |
UINTN OpenCount; | |
UINTN Index; | |
ASSERT (ProtocolGuid != NULL); | |
Status = gBS->OpenProtocolInformation ( | |
Controller, | |
ProtocolGuid, | |
&OpenBuffer, | |
&OpenCount | |
); | |
if (EFI_ERROR (Status)) { | |
return NULL; | |
} | |
Handle = NULL; | |
for (Index = 0; Index < OpenCount; Index++) { | |
if ((OpenBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) != 0) { | |
Handle = OpenBuffer[Index].ControllerHandle; | |
break; | |
} | |
} | |
gBS->FreePool (OpenBuffer); | |
return Handle; | |
} | |
/** | |
Convert one Null-terminated ASCII string (decimal dotted) to EFI_IPv4_ADDRESS. | |
@param[in] String The pointer to the Ascii string. | |
@param[out] Ip4Address The pointer to the converted IPv4 address. | |
@retval EFI_SUCCESS Convert to IPv4 address successfully. | |
@retval EFI_INVALID_PARAMETER The string is malformatted or Ip4Address is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetLibAsciiStrToIp4 ( | |
IN CONST CHAR8 *String, | |
OUT EFI_IPv4_ADDRESS *Ip4Address | |
) | |
{ | |
RETURN_STATUS Status; | |
CHAR8 *EndPointer; | |
Status = AsciiStrToIpv4Address (String, &EndPointer, Ip4Address, NULL); | |
if (RETURN_ERROR (Status) || (*EndPointer != '\0')) { | |
return EFI_INVALID_PARAMETER; | |
} else { | |
return EFI_SUCCESS; | |
} | |
} | |
/** | |
Convert one Null-terminated ASCII string to EFI_IPv6_ADDRESS. The format of the | |
string is defined in RFC 4291 - Text Representation of Addresses. | |
@param[in] String The pointer to the Ascii string. | |
@param[out] Ip6Address The pointer to the converted IPv6 address. | |
@retval EFI_SUCCESS Convert to IPv6 address successfully. | |
@retval EFI_INVALID_PARAMETER The string is malformatted or Ip6Address is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetLibAsciiStrToIp6 ( | |
IN CONST CHAR8 *String, | |
OUT EFI_IPv6_ADDRESS *Ip6Address | |
) | |
{ | |
RETURN_STATUS Status; | |
CHAR8 *EndPointer; | |
Status = AsciiStrToIpv6Address (String, &EndPointer, Ip6Address, NULL); | |
if (RETURN_ERROR (Status) || (*EndPointer != '\0')) { | |
return EFI_INVALID_PARAMETER; | |
} else { | |
return EFI_SUCCESS; | |
} | |
} | |
/** | |
Convert one Null-terminated Unicode string (decimal dotted) to EFI_IPv4_ADDRESS. | |
@param[in] String The pointer to the Ascii string. | |
@param[out] Ip4Address The pointer to the converted IPv4 address. | |
@retval EFI_SUCCESS Convert to IPv4 address successfully. | |
@retval EFI_INVALID_PARAMETER The string is malformatted or Ip4Address is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetLibStrToIp4 ( | |
IN CONST CHAR16 *String, | |
OUT EFI_IPv4_ADDRESS *Ip4Address | |
) | |
{ | |
RETURN_STATUS Status; | |
CHAR16 *EndPointer; | |
Status = StrToIpv4Address (String, &EndPointer, Ip4Address, NULL); | |
if (RETURN_ERROR (Status) || (*EndPointer != L'\0')) { | |
return EFI_INVALID_PARAMETER; | |
} else { | |
return EFI_SUCCESS; | |
} | |
} | |
/** | |
Convert one Null-terminated Unicode string to EFI_IPv6_ADDRESS. The format of | |
the string is defined in RFC 4291 - Text Representation of Addresses. | |
@param[in] String The pointer to the Ascii string. | |
@param[out] Ip6Address The pointer to the converted IPv6 address. | |
@retval EFI_SUCCESS Convert to IPv6 address successfully. | |
@retval EFI_INVALID_PARAMETER The string is malformatted or Ip6Address is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetLibStrToIp6 ( | |
IN CONST CHAR16 *String, | |
OUT EFI_IPv6_ADDRESS *Ip6Address | |
) | |
{ | |
RETURN_STATUS Status; | |
CHAR16 *EndPointer; | |
Status = StrToIpv6Address (String, &EndPointer, Ip6Address, NULL); | |
if (RETURN_ERROR (Status) || (*EndPointer != L'\0')) { | |
return EFI_INVALID_PARAMETER; | |
} else { | |
return EFI_SUCCESS; | |
} | |
} | |
/** | |
Convert one Null-terminated Unicode string to EFI_IPv6_ADDRESS and prefix length. | |
The format of the string is defined in RFC 4291 - Text Representation of Addresses | |
Prefixes: ipv6-address/prefix-length. | |
@param[in] String The pointer to the Ascii string. | |
@param[out] Ip6Address The pointer to the converted IPv6 address. | |
@param[out] PrefixLength The pointer to the converted prefix length. | |
@retval EFI_SUCCESS Convert to IPv6 address successfully. | |
@retval EFI_INVALID_PARAMETER The string is malformatted or Ip6Address is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetLibStrToIp6andPrefix ( | |
IN CONST CHAR16 *String, | |
OUT EFI_IPv6_ADDRESS *Ip6Address, | |
OUT UINT8 *PrefixLength | |
) | |
{ | |
RETURN_STATUS Status; | |
CHAR16 *EndPointer; | |
Status = StrToIpv6Address (String, &EndPointer, Ip6Address, PrefixLength); | |
if (RETURN_ERROR (Status) || (*EndPointer != L'\0')) { | |
return EFI_INVALID_PARAMETER; | |
} else { | |
return EFI_SUCCESS; | |
} | |
} | |
/** | |
Convert one EFI_IPv6_ADDRESS to Null-terminated Unicode string. | |
The text representation of address is defined in RFC 4291. | |
@param[in] Ip6Address The pointer to the IPv6 address. | |
@param[out] String The buffer to return the converted string. | |
@param[in] StringSize The length in bytes of the input String. | |
@retval EFI_SUCCESS Convert to string successfully. | |
@retval EFI_INVALID_PARAMETER The input parameter is invalid. | |
@retval EFI_BUFFER_TOO_SMALL The BufferSize is too small for the result. BufferSize has been | |
updated with the size needed to complete the request. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetLibIp6ToStr ( | |
IN EFI_IPv6_ADDRESS *Ip6Address, | |
OUT CHAR16 *String, | |
IN UINTN StringSize | |
) | |
{ | |
UINT16 Ip6Addr[8]; | |
UINTN Index; | |
UINTN LongestZerosStart; | |
UINTN LongestZerosLength; | |
UINTN CurrentZerosStart; | |
UINTN CurrentZerosLength; | |
CHAR16 Buffer[sizeof"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"]; | |
CHAR16 *Ptr; | |
if (Ip6Address == NULL || String == NULL || StringSize == 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Convert the UINT8 array to an UINT16 array for easy handling. | |
// | |
ZeroMem (Ip6Addr, sizeof (Ip6Addr)); | |
for (Index = 0; Index < 16; Index++) { | |
Ip6Addr[Index / 2] |= (Ip6Address->Addr[Index] << ((1 - (Index % 2)) << 3)); | |
} | |
// | |
// Find the longest zeros and mark it. | |
// | |
CurrentZerosStart = DEFAULT_ZERO_START; | |
CurrentZerosLength = 0; | |
LongestZerosStart = DEFAULT_ZERO_START; | |
LongestZerosLength = 0; | |
for (Index = 0; Index < 8; Index++) { | |
if (Ip6Addr[Index] == 0) { | |
if (CurrentZerosStart == DEFAULT_ZERO_START) { | |
CurrentZerosStart = Index; | |
CurrentZerosLength = 1; | |
} else { | |
CurrentZerosLength++; | |
} | |
} else { | |
if (CurrentZerosStart != DEFAULT_ZERO_START) { | |
if (CurrentZerosLength > 2 && (LongestZerosStart == (DEFAULT_ZERO_START) || CurrentZerosLength > LongestZerosLength)) { | |
LongestZerosStart = CurrentZerosStart; | |
LongestZerosLength = CurrentZerosLength; | |
} | |
CurrentZerosStart = DEFAULT_ZERO_START; | |
CurrentZerosLength = 0; | |
} | |
} | |
} | |
if (CurrentZerosStart != DEFAULT_ZERO_START && CurrentZerosLength > 2) { | |
if (LongestZerosStart == DEFAULT_ZERO_START || LongestZerosLength < CurrentZerosLength) { | |
LongestZerosStart = CurrentZerosStart; | |
LongestZerosLength = CurrentZerosLength; | |
} | |
} | |
Ptr = Buffer; | |
for (Index = 0; Index < 8; Index++) { | |
if (LongestZerosStart != DEFAULT_ZERO_START && Index >= LongestZerosStart && Index < LongestZerosStart + LongestZerosLength) { | |
if (Index == LongestZerosStart) { | |
*Ptr++ = L':'; | |
} | |
continue; | |
} | |
if (Index != 0) { | |
*Ptr++ = L':'; | |
} | |
Ptr += UnicodeSPrint(Ptr, 10, L"%x", Ip6Addr[Index]); | |
} | |
if (LongestZerosStart != DEFAULT_ZERO_START && LongestZerosStart + LongestZerosLength == 8) { | |
*Ptr++ = L':'; | |
} | |
*Ptr = L'\0'; | |
if ((UINTN)Ptr - (UINTN)Buffer > StringSize) { | |
return EFI_BUFFER_TOO_SMALL; | |
} | |
StrCpyS (String, StringSize / sizeof (CHAR16), Buffer); | |
return EFI_SUCCESS; | |
} | |
/** | |
This function obtains the system guid from the smbios table. | |
If SystemGuid is NULL, then ASSERT(). | |
@param[out] SystemGuid The pointer of the returned system guid. | |
@retval EFI_SUCCESS Successfully obtained the system guid. | |
@retval EFI_NOT_FOUND Did not find the SMBIOS table. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NetLibGetSystemGuid ( | |
OUT EFI_GUID *SystemGuid | |
) | |
{ | |
EFI_STATUS Status; | |
SMBIOS_TABLE_ENTRY_POINT *SmbiosTable; | |
SMBIOS_TABLE_3_0_ENTRY_POINT *Smbios30Table; | |
SMBIOS_STRUCTURE_POINTER Smbios; | |
SMBIOS_STRUCTURE_POINTER SmbiosEnd; | |
CHAR8 *String; | |
ASSERT (SystemGuid != NULL); | |
SmbiosTable = NULL; | |
Status = EfiGetSystemConfigurationTable (&gEfiSmbios3TableGuid, (VOID **) &Smbios30Table); | |
if (!(EFI_ERROR (Status) || Smbios30Table == NULL)) { | |
Smbios.Hdr = (SMBIOS_STRUCTURE *) (UINTN) Smbios30Table->TableAddress; | |
SmbiosEnd.Raw = (UINT8 *) (UINTN) (Smbios30Table->TableAddress + Smbios30Table->TableMaximumSize); | |
} else { | |
Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SmbiosTable); | |
if (EFI_ERROR (Status) || SmbiosTable == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
Smbios.Hdr = (SMBIOS_STRUCTURE *) (UINTN) SmbiosTable->TableAddress; | |
SmbiosEnd.Raw = (UINT8 *) ((UINTN) SmbiosTable->TableAddress + SmbiosTable->TableLength); | |
} | |
do { | |
if (Smbios.Hdr->Type == 1) { | |
if (Smbios.Hdr->Length < 0x19) { | |
// | |
// Older version did not support UUID. | |
// | |
return EFI_NOT_FOUND; | |
} | |
// | |
// SMBIOS tables are byte packed so we need to do a byte copy to | |
// prevend alignment faults on Itanium-based platform. | |
// | |
CopyMem (SystemGuid, &Smbios.Type1->Uuid, sizeof (EFI_GUID)); | |
return EFI_SUCCESS; | |
} | |
// | |
// Go to the next SMBIOS structure. Each SMBIOS structure may include 2 parts: | |
// 1. Formatted section; 2. Unformatted string section. So, 2 steps are needed | |
// to skip one SMBIOS structure. | |
// | |
// | |
// Step 1: Skip over formatted section. | |
// | |
String = (CHAR8 *) (Smbios.Raw + Smbios.Hdr->Length); | |
// | |
// Step 2: Skip over unformatted string section. | |
// | |
do { | |
// | |
// Each string is terminated with a NULL(00h) BYTE and the sets of strings | |
// is terminated with an additional NULL(00h) BYTE. | |
// | |
for ( ; *String != 0; String++) { | |
} | |
if (*(UINT8*)++String == 0) { | |
// | |
// Pointer to the next SMBIOS structure. | |
// | |
Smbios.Raw = (UINT8 *)++String; | |
break; | |
} | |
} while (TRUE); | |
} while (Smbios.Raw < SmbiosEnd.Raw); | |
return EFI_NOT_FOUND; | |
} | |
/** | |
Create Dns QName according the queried domain name. | |
If DomainName is NULL, then ASSERT(). | |
QName is a domain name represented as a sequence of labels, | |
where each label consists of a length octet followed by that | |
number of octets. The QName terminates with the zero | |
length octet for the null label of the root. Caller should | |
take responsibility to free the buffer in returned pointer. | |
@param DomainName The pointer to the queried domain name string. | |
@retval NULL Failed to fill QName. | |
@return QName filled successfully. | |
**/ | |
CHAR8 * | |
EFIAPI | |
NetLibCreateDnsQName ( | |
IN CHAR16 *DomainName | |
) | |
{ | |
CHAR8 *QueryName; | |
UINTN QueryNameSize; | |
CHAR8 *Header; | |
CHAR8 *Tail; | |
UINTN Len; | |
UINTN Index; | |
ASSERT (DomainName != NULL); | |
QueryName = NULL; | |
QueryNameSize = 0; | |
Header = NULL; | |
Tail = NULL; | |
// | |
// One byte for first label length, one byte for terminated length zero. | |
// | |
QueryNameSize = StrLen (DomainName) + 2; | |
if (QueryNameSize > DNS_MAX_NAME_SIZE) { | |
return NULL; | |
} | |
QueryName = AllocateZeroPool (QueryNameSize); | |
if (QueryName == NULL) { | |
return NULL; | |
} | |
Header = QueryName; | |
Tail = Header + 1; | |
Len = 0; | |
for (Index = 0; DomainName[Index] != 0; Index++) { | |
*Tail = (CHAR8) DomainName[Index]; | |
if (*Tail == '.') { | |
*Header = (CHAR8) Len; | |
Header = Tail; | |
Tail ++; | |
Len = 0; | |
} else { | |
Tail++; | |
Len++; | |
} | |
} | |
*Header = (CHAR8) Len; | |
*Tail = 0; | |
return QueryName; | |
} |