summaryrefslogtreecommitdiffstats
path: root/NetworkPkg/Library
diff options
context:
space:
mode:
authorLiming Gao <liming.gao@intel.com>2019-05-15 20:02:18 +0800
committerLiming Gao <liming.gao@intel.com>2019-05-27 09:25:18 +0800
commit4542f8b8135f1f1ee5654e25139be9769e139ddd (patch)
tree6e05d7c624d89b3df27d0d3437815a2587f0640f /NetworkPkg/Library
parentc0fd7f734e2d33e22215899b40a47b843129541d (diff)
downloadedk2-4542f8b8135f1f1ee5654e25139be9769e139ddd.tar.gz
edk2-4542f8b8135f1f1ee5654e25139be9769e139ddd.tar.bz2
edk2-4542f8b8135f1f1ee5654e25139be9769e139ddd.zip
NetworkPkg: Move Network library and drivers from MdeModulePkg to NetworkPkg
Signed-off-by: Liming Gao <liming.gao@intel.com> Cc: Siyuan Fu <siyuan.fu@intel.com> Cc: Jiaxin Wu <jiaxin.wu@intel.com> Reviewed-by: Jiaxin Wu <jiaxin.wu@intel.com> Reviewed-by: Siyuan Fu <siyuan.fu@intel.com>
Diffstat (limited to 'NetworkPkg/Library')
-rw-r--r--NetworkPkg/Library/DxeDpcLib/DpcLib.c94
-rw-r--r--NetworkPkg/Library/DxeDpcLib/DxeDpcLib.inf40
-rw-r--r--NetworkPkg/Library/DxeDpcLib/DxeDpcLib.uni16
-rw-r--r--NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c2084
-rw-r--r--NetworkPkg/Library/DxeHttpLib/DxeHttpLib.h85
-rw-r--r--NetworkPkg/Library/DxeHttpLib/DxeHttpLib.inf42
-rw-r--r--NetworkPkg/Library/DxeHttpLib/DxeHttpLib.uni16
-rw-r--r--NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.c2291
-rw-r--r--NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.inf46
-rw-r--r--NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.uni16
-rw-r--r--NetworkPkg/Library/DxeNetLib/DxeNetLib.c3394
-rw-r--r--NetworkPkg/Library/DxeNetLib/DxeNetLib.inf61
-rw-r--r--NetworkPkg/Library/DxeNetLib/DxeNetLib.uni16
-rw-r--r--NetworkPkg/Library/DxeNetLib/NetBuffer.c1890
-rw-r--r--NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.c1011
-rw-r--r--NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.inf44
-rw-r--r--NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.uni16
-rw-r--r--NetworkPkg/Library/DxeUdpIoLib/DxeUdpIoLib.c1119
-rw-r--r--NetworkPkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf46
-rw-r--r--NetworkPkg/Library/DxeUdpIoLib/DxeUpdIoLib.uni16
20 files changed, 12343 insertions, 0 deletions
diff --git a/NetworkPkg/Library/DxeDpcLib/DpcLib.c b/NetworkPkg/Library/DxeDpcLib/DpcLib.c
new file mode 100644
index 0000000000..29d810776e
--- /dev/null
+++ b/NetworkPkg/Library/DxeDpcLib/DpcLib.c
@@ -0,0 +1,94 @@
+/** @file
+ Help functions to access UDP service.
+
+Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/Dpc.h>
+
+//
+// Pointer to the DPC Protocol
+//
+EFI_DPC_PROTOCOL *mDpc;
+
+/**
+ This constructor function caches the EFI_DPC_PROTOCOL pointer.
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The constructor always return EFI_SUCCESS.
+
+**/
+EFI_STATUS
+EFIAPI
+DpcLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Locate the EFI_DPC_PROTOCOL in the handle database
+ //
+ Status = gBS->LocateProtocol (&gEfiDpcProtocolGuid, NULL, (VOID **)&mDpc);
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
+
+/**
+ Add a Deferred Procedure Call to the end of the DPC queue.
+
+ @param[in] DpcTpl The EFI_TPL that the DPC should be invoked.
+ @param[in] DpcProcedure Pointer to the DPC's function.
+ @param[in] DpcContext Pointer to the DPC's context. Passed to DpcProcedure
+ when DpcProcedure is invoked.
+
+ @retval EFI_SUCCESS The DPC was queued.
+ @retval EFI_INVALID_PARAMETER DpcTpl is not a valid EFI_TPL.
+ @retval EFI_INVALID_PARAMETER DpcProcedure is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to
+ add the DPC to the queue.
+
+**/
+EFI_STATUS
+EFIAPI
+QueueDpc (
+ IN EFI_TPL DpcTpl,
+ IN EFI_DPC_PROCEDURE DpcProcedure,
+ IN VOID *DpcContext OPTIONAL
+ )
+{
+ //
+ // Call the EFI_DPC_PROTOCOL to queue the DPC
+ //
+ return mDpc->QueueDpc (mDpc, DpcTpl, DpcProcedure, DpcContext);
+}
+
+/**
+ Dispatch the queue of DPCs. ALL DPCs that have been queued with a DpcTpl
+ value greater than or equal to the current TPL are invoked in the order that
+ they were queued. DPCs with higher DpcTpl values are invoked before DPCs with
+ lower DpcTpl values.
+
+ @retval EFI_SUCCESS One or more DPCs were invoked.
+ @retval EFI_NOT_FOUND No DPCs were invoked.
+
+**/
+EFI_STATUS
+EFIAPI
+DispatchDpc (
+ VOID
+ )
+{
+ //
+ // Call the EFI_DPC_PROTOCOL to dispatch previously queued DPCs
+ //
+ return mDpc->DispatchDpc (mDpc);
+}
diff --git a/NetworkPkg/Library/DxeDpcLib/DxeDpcLib.inf b/NetworkPkg/Library/DxeDpcLib/DxeDpcLib.inf
new file mode 100644
index 0000000000..1c23729ce3
--- /dev/null
+++ b/NetworkPkg/Library/DxeDpcLib/DxeDpcLib.inf
@@ -0,0 +1,40 @@
+## @file
+# This library instance provides DPC service by consuming EFI DPC Protocol.
+#
+# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxeDpcLib
+ MODULE_UNI_FILE = DxeDpcLib.uni
+ FILE_GUID = 38897D86-FF36-4472-AE64-1DB9AE715C81
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = DpcLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER
+ CONSTRUCTOR = DpcLibConstructor
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ DpcLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ DebugLib
+ UefiBootServicesTableLib
+
+[Protocols]
+ gEfiDpcProtocolGuid ## CONSUMES
+
+[Depex.common.DXE_DRIVER, Depex.common.DXE_RUNTIME_DRIVER, Depex.common.DXE_SAL_DRIVER, Depex.common.DXE_SMM_DRIVER]
+ gEfiDpcProtocolGuid
diff --git a/NetworkPkg/Library/DxeDpcLib/DxeDpcLib.uni b/NetworkPkg/Library/DxeDpcLib/DxeDpcLib.uni
new file mode 100644
index 0000000000..3105bbcfe0
--- /dev/null
+++ b/NetworkPkg/Library/DxeDpcLib/DxeDpcLib.uni
@@ -0,0 +1,16 @@
+// /** @file
+// This library instance provides DPC service by consuming EFI DPC Protocol.
+//
+// This library instance provides the DPC service by consuming EFI DPC Protocol.
+//
+// Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Provides the DPC service by consuming EFI DPC Protocol"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This library instance provides the DPC service by consuming EFI DPC Protocol."
+
diff --git a/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c b/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c
new file mode 100644
index 0000000000..8b74554cd9
--- /dev/null
+++ b/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c
@@ -0,0 +1,2084 @@
+/** @file
+ This library is used to share code between UEFI network stack modules.
+ It provides the helper routines to parse the HTTP message byte stream.
+
+Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "DxeHttpLib.h"
+
+
+
+/**
+ Decode a percent-encoded URI component to the ASCII character.
+
+ Decode the input component in Buffer according to RFC 3986. The caller is responsible to make
+ sure ResultBuffer points to a buffer with size equal or greater than ((AsciiStrSize (Buffer))
+ in bytes.
+
+ @param[in] Buffer The pointer to a percent-encoded URI component.
+ @param[in] BufferLength Length of Buffer in bytes.
+ @param[out] ResultBuffer Point to the buffer to store the decode result.
+ @param[out] ResultLength Length of decoded string in ResultBuffer in bytes.
+
+ @retval EFI_SUCCESS Successfully decoded the URI.
+ @retval EFI_INVALID_PARAMETER Buffer is not a valid percent-encoded string.
+
+**/
+EFI_STATUS
+EFIAPI
+UriPercentDecode (
+ IN CHAR8 *Buffer,
+ IN UINT32 BufferLength,
+ OUT CHAR8 *ResultBuffer,
+ OUT UINT32 *ResultLength
+ )
+{
+ UINTN Index;
+ UINTN Offset;
+ CHAR8 HexStr[3];
+
+ if (Buffer == NULL || BufferLength == 0 || ResultBuffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Index = 0;
+ Offset = 0;
+ HexStr[2] = '\0';
+ while (Index < BufferLength) {
+ if (Buffer[Index] == '%') {
+ if (Index + 1 >= BufferLength || Index + 2 >= BufferLength ||
+ !NET_IS_HEX_CHAR (Buffer[Index+1]) || !NET_IS_HEX_CHAR (Buffer[Index+2])) {
+ return EFI_INVALID_PARAMETER;
+ }
+ HexStr[0] = Buffer[Index+1];
+ HexStr[1] = Buffer[Index+2];
+ ResultBuffer[Offset] = (CHAR8) AsciiStrHexToUintn (HexStr);
+ Index += 3;
+ } else {
+ ResultBuffer[Offset] = Buffer[Index];
+ Index++;
+ }
+ Offset++;
+ }
+
+ *ResultLength = (UINT32) Offset;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function return the updated state according to the input state and next character of
+ the authority.
+
+ @param[in] Char Next character.
+ @param[in] State Current value of the parser state machine.
+ @param[in] IsRightBracket TRUE if there is an sign ']' in the authority component and
+ indicates the next part is ':' before Port.
+
+ @return Updated state value.
+**/
+HTTP_URL_PARSE_STATE
+NetHttpParseAuthorityChar (
+ IN CHAR8 Char,
+ IN HTTP_URL_PARSE_STATE State,
+ IN BOOLEAN *IsRightBracket
+ )
+{
+
+ //
+ // RFC 3986:
+ // The authority component is preceded by a double slash ("//") and is
+ // terminated by the next slash ("/"), question mark ("?"), or number
+ // sign ("#") character, or by the end of the URI.
+ //
+ if (Char == ' ' || Char == '\r' || Char == '\n') {
+ return UrlParserStateMax;
+ }
+
+ //
+ // authority = [ userinfo "@" ] host [ ":" port ]
+ //
+ switch (State) {
+ case UrlParserUserInfo:
+ if (Char == '@') {
+ return UrlParserHostStart;
+ }
+ break;
+
+ case UrlParserHost:
+ case UrlParserHostStart:
+ if (Char == '[') {
+ return UrlParserHostIpv6;
+ }
+
+ if (Char == ':') {
+ return UrlParserPortStart;
+ }
+
+ return UrlParserHost;
+
+ case UrlParserHostIpv6:
+ if (Char == ']') {
+ *IsRightBracket = TRUE;
+ }
+
+ if (Char == ':' && *IsRightBracket) {
+ return UrlParserPortStart;
+ }
+ return UrlParserHostIpv6;
+
+ case UrlParserPort:
+ case UrlParserPortStart:
+ return UrlParserPort;
+
+ default:
+ break;
+ }
+
+ return State;
+}
+
+/**
+ This function parse the authority component of the input URL and update the parser.
+
+ @param[in] Url The pointer to a HTTP URL string.
+ @param[in] FoundAt TRUE if there is an at sign ('@') in the authority component.
+ @param[in, out] UrlParser Pointer to the buffer of the parse result.
+
+ @retval EFI_SUCCESS Successfully parse the authority.
+ @retval EFI_INVALID_PARAMETER The Url is invalid to parse the authority component.
+
+**/
+EFI_STATUS
+NetHttpParseAuthority (
+ IN CHAR8 *Url,
+ IN BOOLEAN FoundAt,
+ IN OUT HTTP_URL_PARSER *UrlParser
+ )
+{
+ CHAR8 *Char;
+ CHAR8 *Authority;
+ UINT32 Length;
+ HTTP_URL_PARSE_STATE State;
+ UINT32 Field;
+ UINT32 OldField;
+ BOOLEAN IsrightBracket;
+
+ ASSERT ((UrlParser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0);
+
+ //
+ // authority = [ userinfo "@" ] host [ ":" port ]
+ //
+ if (FoundAt) {
+ State = UrlParserUserInfo;
+ } else {
+ State = UrlParserHost;
+ }
+
+ IsrightBracket = FALSE;
+ Field = HTTP_URI_FIELD_MAX;
+ OldField = Field;
+ Authority = Url + UrlParser->FieldData[HTTP_URI_FIELD_AUTHORITY].Offset;
+ Length = UrlParser->FieldData[HTTP_URI_FIELD_AUTHORITY].Length;
+ for (Char = Authority; Char < Authority + Length; Char++) {
+ State = NetHttpParseAuthorityChar (*Char, State, &IsrightBracket);
+ switch (State) {
+ case UrlParserStateMax:
+ return EFI_INVALID_PARAMETER;
+
+ case UrlParserHostStart:
+ case UrlParserPortStart:
+ continue;
+
+ case UrlParserUserInfo:
+ Field = HTTP_URI_FIELD_USERINFO;
+ break;
+
+ case UrlParserHost:
+ Field = HTTP_URI_FIELD_HOST;
+ break;
+
+ case UrlParserHostIpv6:
+ Field = HTTP_URI_FIELD_HOST;
+ break;
+
+ case UrlParserPort:
+ Field = HTTP_URI_FIELD_PORT;
+ break;
+
+ default:
+ ASSERT (FALSE);
+ }
+
+ //
+ // Field not changed, count the length.
+ //
+ ASSERT (Field < HTTP_URI_FIELD_MAX);
+ if (Field == OldField) {
+ UrlParser->FieldData[Field].Length++;
+ continue;
+ }
+
+ //
+ // New field start
+ //
+ UrlParser->FieldBitMap |= BIT (Field);
+ UrlParser->FieldData[Field].Offset = (UINT32) (Char - Url);
+ UrlParser->FieldData[Field].Length = 1;
+ OldField = Field;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function return the updated state according to the input state and next character of a URL.
+
+ @param[in] Char Next character.
+ @param[in] State Current value of the parser state machine.
+
+ @return Updated state value.
+
+**/
+HTTP_URL_PARSE_STATE
+NetHttpParseUrlChar (
+ IN CHAR8 Char,
+ IN HTTP_URL_PARSE_STATE State
+ )
+{
+ if (Char == ' ' || Char == '\r' || Char == '\n') {
+ return UrlParserStateMax;
+ }
+
+ //
+ // http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
+ //
+ // Request-URI = "*" | absolute-URI | path-absolute | authority
+ //
+ // absolute-URI = scheme ":" hier-part [ "?" query ]
+ // path-absolute = "/" [ segment-nz *( "/" segment ) ]
+ // authority = [ userinfo "@" ] host [ ":" port ]
+ //
+ switch (State) {
+ case UrlParserUrlStart:
+ if (Char == '*' || Char == '/') {
+ return UrlParserPath;
+ }
+ return UrlParserScheme;
+
+ case UrlParserScheme:
+ if (Char == ':') {
+ return UrlParserSchemeColon;
+ }
+ break;
+
+ case UrlParserSchemeColon:
+ if (Char == '/') {
+ return UrlParserSchemeColonSlash;
+ }
+ break;
+
+ case UrlParserSchemeColonSlash:
+ if (Char == '/') {
+ return UrlParserSchemeColonSlashSlash;
+ }
+ break;
+
+ case UrlParserAtInAuthority:
+ if (Char == '@') {
+ return UrlParserStateMax;
+ }
+
+ case UrlParserAuthority:
+ case UrlParserSchemeColonSlashSlash:
+ if (Char == '@') {
+ return UrlParserAtInAuthority;
+ }
+ if (Char == '/') {
+ return UrlParserPath;
+ }
+ if (Char == '?') {
+ return UrlParserQueryStart;
+ }
+ if (Char == '#') {
+ return UrlParserFragmentStart;
+ }
+ return UrlParserAuthority;
+
+ case UrlParserPath:
+ if (Char == '?') {
+ return UrlParserQueryStart;
+ }
+ if (Char == '#') {
+ return UrlParserFragmentStart;
+ }
+ break;
+
+ case UrlParserQuery:
+ case UrlParserQueryStart:
+ if (Char == '#') {
+ return UrlParserFragmentStart;
+ }
+ return UrlParserQuery;
+
+ case UrlParserFragmentStart:
+ return UrlParserFragment;
+
+ default:
+ break;
+ }
+
+ return State;
+}
+/**
+ Create a URL parser for the input URL string.
+
+ This function will parse and dereference the input HTTP URL into it components. The original
+ content of the URL won't be modified and the result will be returned in UrlParser, which can
+ be used in other functions like NetHttpUrlGetHostName().
+
+ @param[in] Url The pointer to a HTTP URL string.
+ @param[in] Length Length of Url in bytes.
+ @param[in] IsConnectMethod Whether the Url is used in HTTP CONNECT method or not.
+ @param[out] UrlParser Pointer to the returned buffer to store the parse result.
+
+ @retval EFI_SUCCESS Successfully dereferenced the HTTP URL.
+ @retval EFI_INVALID_PARAMETER UrlParser is NULL or Url is not a valid HTTP URL.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpParseUrl (
+ IN CHAR8 *Url,
+ IN UINT32 Length,
+ IN BOOLEAN IsConnectMethod,
+ OUT VOID **UrlParser
+ )
+{
+ HTTP_URL_PARSE_STATE State;
+ CHAR8 *Char;
+ UINT32 Field;
+ UINT32 OldField;
+ BOOLEAN FoundAt;
+ EFI_STATUS Status;
+ HTTP_URL_PARSER *Parser;
+
+ Parser = NULL;
+
+ if (Url == NULL || Length == 0 || UrlParser == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Parser = AllocateZeroPool (sizeof (HTTP_URL_PARSER));
+ if (Parser == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (IsConnectMethod) {
+ //
+ // According to RFC 2616, the authority form is only used by the CONNECT method.
+ //
+ State = UrlParserAuthority;
+ } else {
+ State = UrlParserUrlStart;
+ }
+
+ Field = HTTP_URI_FIELD_MAX;
+ OldField = Field;
+ FoundAt = FALSE;
+ for (Char = Url; Char < Url + Length; Char++) {
+ //
+ // Update state machine according to next char.
+ //
+ State = NetHttpParseUrlChar (*Char, State);
+
+ switch (State) {
+ case UrlParserStateMax:
+ FreePool (Parser);
+ return EFI_INVALID_PARAMETER;
+
+ case UrlParserSchemeColon:
+ case UrlParserSchemeColonSlash:
+ case UrlParserSchemeColonSlashSlash:
+ case UrlParserQueryStart:
+ case UrlParserFragmentStart:
+ //
+ // Skip all the delimiting char: "://" "?" "@"
+ //
+ continue;
+
+ case UrlParserScheme:
+ Field = HTTP_URI_FIELD_SCHEME;
+ break;
+
+ case UrlParserAtInAuthority:
+ FoundAt = TRUE;
+ case UrlParserAuthority:
+ Field = HTTP_URI_FIELD_AUTHORITY;
+ break;
+
+ case UrlParserPath:
+ Field = HTTP_URI_FIELD_PATH;
+ break;
+
+ case UrlParserQuery:
+ Field = HTTP_URI_FIELD_QUERY;
+ break;
+
+ case UrlParserFragment:
+ Field = HTTP_URI_FIELD_FRAGMENT;
+ break;
+
+ default:
+ ASSERT (FALSE);
+ }
+
+ //
+ // Field not changed, count the length.
+ //
+ ASSERT (Field < HTTP_URI_FIELD_MAX);
+ if (Field == OldField) {
+ Parser->FieldData[Field].Length++;
+ continue;
+ }
+
+ //
+ // New field start
+ //
+ Parser->FieldBitMap |= BIT (Field);
+ Parser->FieldData[Field].Offset = (UINT32) (Char - Url);
+ Parser->FieldData[Field].Length = 1;
+ OldField = Field;
+ }
+
+ //
+ // If has authority component, continue to parse the username, host and port.
+ //
+ if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0) {
+ Status = NetHttpParseAuthority (Url, FoundAt, Parser);
+ if (EFI_ERROR (Status)) {
+ FreePool (Parser);
+ return Status;
+ }
+ }
+
+ *UrlParser = Parser;
+ return EFI_SUCCESS;
+}
+
+/**
+ Get the Hostname from a HTTP URL.
+
+ This function will return the HostName according to the Url and previous parse result ,and
+ it is the caller's responsibility to free the buffer returned in *HostName.
+
+ @param[in] Url The pointer to a HTTP URL string.
+ @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
+ @param[out] HostName Pointer to a buffer to store the HostName.
+
+ @retval EFI_SUCCESS Successfully get the required component.
+ @retval EFI_INVALID_PARAMETER Uri is NULL or HostName is NULL or UrlParser is invalid.
+ @retval EFI_NOT_FOUND No hostName component in the URL.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpUrlGetHostName (
+ IN CHAR8 *Url,
+ IN VOID *UrlParser,
+ OUT CHAR8 **HostName
+ )
+{
+ CHAR8 *Name;
+ EFI_STATUS Status;
+ UINT32 ResultLength;
+ HTTP_URL_PARSER *Parser;
+
+ if (Url == NULL || UrlParser == NULL || HostName == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Parser = (HTTP_URL_PARSER *) UrlParser;
+
+ if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ Name = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1);
+ if (Name == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = UriPercentDecode (
+ Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,
+ Parser->FieldData[HTTP_URI_FIELD_HOST].Length,
+ Name,
+ &ResultLength
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Name);
+ return Status;
+ }
+
+ Name[ResultLength] = '\0';
+ *HostName = Name;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Get the IPv4 address from a HTTP URL.
+
+ This function will return the IPv4 address according to the Url and previous parse result.
+
+ @param[in] Url The pointer to a HTTP URL string.
+ @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
+ @param[out] Ip4Address Pointer to a buffer to store the IP address.
+
+ @retval EFI_SUCCESS Successfully get the required component.
+ @retval EFI_INVALID_PARAMETER Uri is NULL or Ip4Address is NULL or UrlParser is invalid.
+ @retval EFI_NOT_FOUND No IPv4 address component in the URL.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpUrlGetIp4 (
+ IN CHAR8 *Url,
+ IN VOID *UrlParser,
+ OUT EFI_IPv4_ADDRESS *Ip4Address
+ )
+{
+ CHAR8 *Ip4String;
+ EFI_STATUS Status;
+ UINT32 ResultLength;
+ HTTP_URL_PARSER *Parser;
+
+ if (Url == NULL || UrlParser == NULL || Ip4Address == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Parser = (HTTP_URL_PARSER *) UrlParser;
+
+ if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ Ip4String = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1);
+ if (Ip4String == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = UriPercentDecode (
+ Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,
+ Parser->FieldData[HTTP_URI_FIELD_HOST].Length,
+ Ip4String,
+ &ResultLength
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Ip4String);
+ return Status;
+ }
+
+ Ip4String[ResultLength] = '\0';
+ Status = NetLibAsciiStrToIp4 (Ip4String, Ip4Address);
+ FreePool (Ip4String);
+
+ return Status;
+}
+
+/**
+ Get the IPv6 address from a HTTP URL.
+
+ This function will return the IPv6 address according to the Url and previous parse result.
+
+ @param[in] Url The pointer to a HTTP URL string.
+ @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
+ @param[out] Ip6Address Pointer to a buffer to store the IP address.
+
+ @retval EFI_SUCCESS Successfully get the required component.
+ @retval EFI_INVALID_PARAMETER Uri is NULL or Ip6Address is NULL or UrlParser is invalid.
+ @retval EFI_NOT_FOUND No IPv6 address component in the URL.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpUrlGetIp6 (
+ IN CHAR8 *Url,
+ IN VOID *UrlParser,
+ OUT EFI_IPv6_ADDRESS *Ip6Address
+ )
+{
+ CHAR8 *Ip6String;
+ CHAR8 *Ptr;
+ UINT32 Length;
+ EFI_STATUS Status;
+ UINT32 ResultLength;
+ HTTP_URL_PARSER *Parser;
+
+ if (Url == NULL || UrlParser == NULL || Ip6Address == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Parser = (HTTP_URL_PARSER *) UrlParser;
+
+ if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // IP-literal = "[" ( IPv6address / IPvFuture ) "]"
+ //
+ Length = Parser->FieldData[HTTP_URI_FIELD_HOST].Length;
+ if (Length < 2) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Ptr = Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset;
+ if ((Ptr[0] != '[') || (Ptr[Length - 1] != ']')) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Ip6String = AllocatePool (Length);
+ if (Ip6String == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = UriPercentDecode (
+ Ptr + 1,
+ Length - 2,
+ Ip6String,
+ &ResultLength
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Ip6String);
+ return Status;
+ }
+
+ Ip6String[ResultLength] = '\0';
+ Status = NetLibAsciiStrToIp6 (Ip6String, Ip6Address);
+ FreePool (Ip6String);
+
+ return Status;
+}
+
+/**
+ Get the port number from a HTTP URL.
+
+ This function will return the port number according to the Url and previous parse result.
+
+ @param[in] Url The pointer to a HTTP URL string.
+ @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
+ @param[out] Port Pointer to a buffer to store the port number.
+
+ @retval EFI_SUCCESS Successfully get the required component.
+ @retval EFI_INVALID_PARAMETER Uri is NULL or Port is NULL or UrlParser is invalid.
+ @retval EFI_NOT_FOUND No port number in the URL.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpUrlGetPort (
+ IN CHAR8 *Url,
+ IN VOID *UrlParser,
+ OUT UINT16 *Port
+ )
+{
+ CHAR8 *PortString;
+ EFI_STATUS Status;
+ UINTN Index;
+ UINTN Data;
+ UINT32 ResultLength;
+ HTTP_URL_PARSER *Parser;
+
+ if (Url == NULL || UrlParser == NULL || Port == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Port = 0;
+ Index = 0;
+
+ Parser = (HTTP_URL_PARSER *) UrlParser;
+
+ if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PORT)) == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ PortString = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PORT].Length + 1);
+ if (PortString == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = UriPercentDecode (
+ Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset,
+ Parser->FieldData[HTTP_URI_FIELD_PORT].Length,
+ PortString,
+ &ResultLength
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ PortString[ResultLength] = '\0';
+
+ while (Index < ResultLength) {
+ if (!NET_IS_DIGIT (PortString[Index])) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ Index ++;
+ }
+
+ Status = AsciiStrDecimalToUintnS (Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset, (CHAR8 **) NULL, &Data);
+
+ if (Data > HTTP_URI_PORT_MAX_NUM) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ *Port = (UINT16) Data;
+
+ON_EXIT:
+ FreePool (PortString);
+ return Status;
+}
+
+/**
+ Get the Path from a HTTP URL.
+
+ This function will return the Path according to the Url and previous parse result,and
+ it is the caller's responsibility to free the buffer returned in *Path.
+
+ @param[in] Url The pointer to a HTTP URL string.
+ @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
+ @param[out] Path Pointer to a buffer to store the Path.
+
+ @retval EFI_SUCCESS Successfully get the required component.
+ @retval EFI_INVALID_PARAMETER Uri is NULL or HostName is NULL or UrlParser is invalid.
+ @retval EFI_NOT_FOUND No hostName component in the URL.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpUrlGetPath (
+ IN CHAR8 *Url,
+ IN VOID *UrlParser,
+ OUT CHAR8 **Path
+ )
+{
+ CHAR8 *PathStr;
+ EFI_STATUS Status;
+ UINT32 ResultLength;
+ HTTP_URL_PARSER *Parser;
+
+ if (Url == NULL || UrlParser == NULL || Path == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Parser = (HTTP_URL_PARSER *) UrlParser;
+
+ if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PATH)) == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ PathStr = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PATH].Length + 1);
+ if (PathStr == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = UriPercentDecode (
+ Url + Parser->FieldData[HTTP_URI_FIELD_PATH].Offset,
+ Parser->FieldData[HTTP_URI_FIELD_PATH].Length,
+ PathStr,
+ &ResultLength
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (PathStr);
+ return Status;
+ }
+
+ PathStr[ResultLength] = '\0';
+ *Path = PathStr;
+ return EFI_SUCCESS;
+}
+
+/**
+ Release the resource of the URL parser.
+
+ @param[in] UrlParser Pointer to the parser.
+
+**/
+VOID
+EFIAPI
+HttpUrlFreeParser (
+ IN VOID *UrlParser
+ )
+{
+ FreePool (UrlParser);
+}
+
+/**
+ Find a specified header field according to the field name.
+
+ @param[in] HeaderCount Number of HTTP header structures in Headers list.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[in] FieldName Null terminated string which describes a field name.
+
+ @return Pointer to the found header or NULL.
+
+**/
+EFI_HTTP_HEADER *
+EFIAPI
+HttpFindHeader (
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ IN CHAR8 *FieldName
+ )
+{
+ UINTN Index;
+
+ if (HeaderCount == 0 || Headers == NULL || FieldName == NULL) {
+ return NULL;
+ }
+
+ for (Index = 0; Index < HeaderCount; Index++){
+ //
+ // Field names are case-insensitive (RFC 2616).
+ //
+ if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) {
+ return &Headers[Index];
+ }
+ }
+ return NULL;
+}
+
+typedef enum {
+ BodyParserBodyStart,
+ BodyParserBodyIdentity,
+ BodyParserChunkSizeStart,
+ BodyParserChunkSize,
+ BodyParserChunkSizeEndCR,
+ BodyParserChunkExtStart,
+ BodyParserChunkDataStart,
+ BodyParserChunkDataEnd,
+ BodyParserChunkDataEndCR,
+ BodyParserTrailer,
+ BodyParserLastCRLF,
+ BodyParserLastCRLFEnd,
+ BodyParserComplete,
+ BodyParserStateMax
+} HTTP_BODY_PARSE_STATE;
+
+typedef struct {
+ BOOLEAN IgnoreBody; // "MUST NOT" include a message-body
+ BOOLEAN IsChunked; // "chunked" transfer-coding.
+ BOOLEAN ContentLengthIsValid;
+ UINTN ContentLength; // Entity length (not the message-body length), invalid until ContentLengthIsValid is TRUE
+
+ HTTP_BODY_PARSER_CALLBACK Callback;
+ VOID *Context;
+ UINTN ParsedBodyLength;
+ HTTP_BODY_PARSE_STATE State;
+ UINTN CurrentChunkSize;
+ UINTN CurrentChunkParsedSize;
+} HTTP_BODY_PARSER;
+
+/**
+ Convert an hexadecimal char to a value of type UINTN.
+
+ @param[in] Char Ascii character.
+
+ @return Value translated from Char.
+
+**/
+UINTN
+HttpIoHexCharToUintn (
+ IN CHAR8 Char
+ )
+{
+ if (Char >= '0' && Char <= '9') {
+ return Char - '0';
+ }
+
+ return (10 + AsciiCharToUpper (Char) - 'A');
+}
+
+/**
+ Get the value of the content length if there is a "Content-Length" header.
+
+ @param[in] HeaderCount Number of HTTP header structures in Headers.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[out] ContentLength Pointer to save the value of the content length.
+
+ @retval EFI_SUCCESS Successfully get the content length.
+ @retval EFI_NOT_FOUND No "Content-Length" header in the Headers.
+
+**/
+EFI_STATUS
+HttpIoParseContentLengthHeader (
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ OUT UINTN *ContentLength
+ )
+{
+ EFI_HTTP_HEADER *Header;
+
+ Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_LENGTH);
+ if (Header == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ return AsciiStrDecimalToUintnS (Header->FieldValue, (CHAR8 **) NULL, ContentLength);
+}
+
+/**
+
+ Check whether the HTTP message is using the "chunked" transfer-coding.
+
+ @param[in] HeaderCount Number of HTTP header structures in Headers.
+ @param[in] Headers Array containing list of HTTP headers.
+
+ @return The message is "chunked" transfer-coding (TRUE) or not (FALSE).
+
+**/
+BOOLEAN
+HttpIoIsChunked (
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers
+ )
+{
+ EFI_HTTP_HEADER *Header;
+
+
+ Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_TRANSFER_ENCODING);
+ if (Header == NULL) {
+ return FALSE;
+ }
+
+ if (AsciiStriCmp (Header->FieldValue, "identity") != 0) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Check whether the HTTP message should have a message-body.
+
+ @param[in] Method The HTTP method (e.g. GET, POST) for this HTTP message.
+ @param[in] StatusCode Response status code returned by the remote host.
+
+ @return The message should have a message-body (FALSE) or not (TRUE).
+
+**/
+BOOLEAN
+HttpIoNoMessageBody (
+ IN EFI_HTTP_METHOD Method,
+ IN EFI_HTTP_STATUS_CODE StatusCode
+ )
+{
+ //
+ // RFC 2616:
+ // All responses to the HEAD request method
+ // MUST NOT include a message-body, even though the presence of entity-
+ // header fields might lead one to believe they do. All 1xx
+ // (informational), 204 (no content), and 304 (not modified) responses
+ // MUST NOT include a message-body. All other responses do include a
+ // message-body, although it MAY be of zero length.
+ //
+ if (Method == HttpMethodHead) {
+ return TRUE;
+ }
+
+ if ((StatusCode == HTTP_STATUS_100_CONTINUE) ||
+ (StatusCode == HTTP_STATUS_101_SWITCHING_PROTOCOLS) ||
+ (StatusCode == HTTP_STATUS_204_NO_CONTENT) ||
+ (StatusCode == HTTP_STATUS_304_NOT_MODIFIED))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Initialize a HTTP message-body parser.
+
+ This function will create and initialize a HTTP message parser according to caller provided HTTP message
+ header information. It is the caller's responsibility to free the buffer returned in *UrlParser by HttpFreeMsgParser().
+
+ @param[in] Method The HTTP method (e.g. GET, POST) for this HTTP message.
+ @param[in] StatusCode Response status code returned by the remote host.
+ @param[in] HeaderCount Number of HTTP header structures in Headers.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[in] Callback Callback function that is invoked when parsing the HTTP message-body,
+ set to NULL to ignore all events.
+ @param[in] Context Pointer to the context that will be passed to Callback.
+ @param[out] MsgParser Pointer to the returned buffer to store the message parser.
+
+ @retval EFI_SUCCESS Successfully initialized the parser.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
+ @retval EFI_INVALID_PARAMETER MsgParser is NULL or HeaderCount is not NULL but Headers is NULL.
+ @retval Others Failed to initialize the parser.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpInitMsgParser (
+ IN EFI_HTTP_METHOD Method,
+ IN EFI_HTTP_STATUS_CODE StatusCode,
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ IN HTTP_BODY_PARSER_CALLBACK Callback,
+ IN VOID *Context,
+ OUT VOID **MsgParser
+ )
+{
+ EFI_STATUS Status;
+ HTTP_BODY_PARSER *Parser;
+
+ if (HeaderCount != 0 && Headers == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (MsgParser == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Parser = AllocateZeroPool (sizeof (HTTP_BODY_PARSER));
+ if (Parser == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Parser->State = BodyParserBodyStart;
+
+ //
+ // Determine the message length according to RFC 2616.
+ // 1. Check whether the message "MUST NOT" have a message-body.
+ //
+ Parser->IgnoreBody = HttpIoNoMessageBody (Method, StatusCode);
+ //
+ // 2. Check whether the message using "chunked" transfer-coding.
+ //
+ Parser->IsChunked = HttpIoIsChunked (HeaderCount, Headers);
+ //
+ // 3. Check whether the message has a Content-Length header field.
+ //
+ Status = HttpIoParseContentLengthHeader (HeaderCount, Headers, &Parser->ContentLength);
+ if (!EFI_ERROR (Status)) {
+ Parser->ContentLengthIsValid = TRUE;
+ }
+ //
+ // 4. Range header is not supported now, so we won't meet media type "multipart/byteranges".
+ // 5. By server closing the connection
+ //
+
+ //
+ // Set state to skip body parser if the message shouldn't have a message body.
+ //
+ if (Parser->IgnoreBody) {
+ Parser->State = BodyParserComplete;
+ } else {
+ Parser->Callback = Callback;
+ Parser->Context = Context;
+ }
+
+ *MsgParser = Parser;
+ return EFI_SUCCESS;
+}
+
+/**
+ Parse message body.
+
+ Parse BodyLength of message-body. This function can be called repeatedly to parse the message-body partially.
+
+ @param[in, out] MsgParser Pointer to the message parser.
+ @param[in] BodyLength Length in bytes of the Body.
+ @param[in] Body Pointer to the buffer of the message-body to be parsed.
+
+ @retval EFI_SUCCESS Successfully parse the message-body.
+ @retval EFI_INVALID_PARAMETER MsgParser is NULL or Body is NULL or BodyLength is 0.
+ @retval EFI_ABORTED Operation aborted.
+ @retval Other Error happened while parsing message body.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpParseMessageBody (
+ IN OUT VOID *MsgParser,
+ IN UINTN BodyLength,
+ IN CHAR8 *Body
+ )
+{
+ CHAR8 *Char;
+ UINTN RemainderLengthInThis;
+ UINTN LengthForCallback;
+ EFI_STATUS Status;
+ HTTP_BODY_PARSER *Parser;
+
+ if (BodyLength == 0 || Body == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (MsgParser == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Parser = (HTTP_BODY_PARSER *) MsgParser;
+
+ if (Parser->IgnoreBody) {
+ Parser->State = BodyParserComplete;
+ if (Parser->Callback != NULL) {
+ Status = Parser->Callback (
+ BodyParseEventOnComplete,
+ Body,
+ 0,
+ Parser->Context
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ return EFI_SUCCESS;
+ }
+
+ if (Parser->State == BodyParserBodyStart) {
+ Parser->ParsedBodyLength = 0;
+ if (Parser->IsChunked) {
+ Parser->State = BodyParserChunkSizeStart;
+ } else {
+ Parser->State = BodyParserBodyIdentity;
+ }
+ }
+
+ //
+ // The message body might be truncated in anywhere, so we need to parse is byte-by-byte.
+ //
+ for (Char = Body; Char < Body + BodyLength; ) {
+
+ switch (Parser->State) {
+ case BodyParserStateMax:
+ return EFI_ABORTED;
+
+ case BodyParserBodyIdentity:
+ //
+ // Identity transfer-coding, just notify user to save the body data.
+ //
+ if (Parser->Callback != NULL) {
+ Status = Parser->Callback (
+ BodyParseEventOnData,
+ Char,
+ MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength),
+ Parser->Context
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ Char += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);
+ Parser->ParsedBodyLength += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);
+ if (Parser->ParsedBodyLength == Parser->ContentLength) {
+ Parser->State = BodyParserComplete;
+ if (Parser->Callback != NULL) {
+ Status = Parser->Callback (
+ BodyParseEventOnComplete,
+ Char,
+ 0,
+ Parser->Context
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ }
+ break;
+
+ case BodyParserChunkSizeStart:
+ //
+ // First byte of chunk-size, the chunk-size might be truncated.
+ //
+ Parser->CurrentChunkSize = 0;
+ Parser->State = BodyParserChunkSize;
+ case BodyParserChunkSize:
+ if (!NET_IS_HEX_CHAR (*Char)) {
+ if (*Char == ';') {
+ Parser->State = BodyParserChunkExtStart;
+ Char++;
+ } else if (*Char == '\r') {
+ Parser->State = BodyParserChunkSizeEndCR;
+ Char++;
+ } else {
+ Parser->State = BodyParserStateMax;
+ }
+ break;
+ }
+
+ if (Parser->CurrentChunkSize > (((~((UINTN) 0)) - 16) / 16)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ Parser->CurrentChunkSize = Parser->CurrentChunkSize * 16 + HttpIoHexCharToUintn (*Char);
+ Char++;
+ break;
+
+ case BodyParserChunkExtStart:
+ //
+ // Ignore all the chunk extensions.
+ //
+ if (*Char == '\r') {
+ Parser->State = BodyParserChunkSizeEndCR;
+ }
+ Char++;
+ break;
+
+ case BodyParserChunkSizeEndCR:
+ if (*Char != '\n') {
+ Parser->State = BodyParserStateMax;
+ break;
+ }
+ Char++;
+ if (Parser->CurrentChunkSize == 0) {
+ //
+ // The last chunk has been parsed and now assumed the state
+ // of HttpBodyParse is ParserLastCRLF. So it need to decide
+ // whether the rest message is trailer or last CRLF in the next round.
+ //
+ Parser->ContentLengthIsValid = TRUE;
+ Parser->State = BodyParserLastCRLF;
+ break;
+ }
+ Parser->State = BodyParserChunkDataStart;
+ Parser->CurrentChunkParsedSize = 0;
+ break;
+
+ case BodyParserLastCRLF:
+ //
+ // Judge the byte is belong to the Last CRLF or trailer, and then
+ // configure the state of HttpBodyParse to corresponding state.
+ //
+ if (*Char == '\r') {
+ Char++;
+ Parser->State = BodyParserLastCRLFEnd;
+ break;
+ } else {
+ Parser->State = BodyParserTrailer;
+ break;
+ }
+
+ case BodyParserLastCRLFEnd:
+ if (*Char == '\n') {
+ Parser->State = BodyParserComplete;
+ Char++;
+ if (Parser->Callback != NULL) {
+ Status = Parser->Callback (
+ BodyParseEventOnComplete,
+ Char,
+ 0,
+ Parser->Context
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ break;
+ } else {
+ Parser->State = BodyParserStateMax;
+ break;
+ }
+
+ case BodyParserTrailer:
+ if (*Char == '\r') {
+ Parser->State = BodyParserChunkSizeEndCR;
+ }
+ Char++;
+ break;
+
+ case BodyParserChunkDataStart:
+ //
+ // First byte of chunk-data, the chunk data also might be truncated.
+ //
+ RemainderLengthInThis = BodyLength - (Char - Body);
+ LengthForCallback = MIN (Parser->CurrentChunkSize - Parser->CurrentChunkParsedSize, RemainderLengthInThis);
+ if (Parser->Callback != NULL) {
+ Status = Parser->Callback (
+ BodyParseEventOnData,
+ Char,
+ LengthForCallback,
+ Parser->Context
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ Char += LengthForCallback;
+ Parser->ContentLength += LengthForCallback;
+ Parser->CurrentChunkParsedSize += LengthForCallback;
+ if (Parser->CurrentChunkParsedSize == Parser->CurrentChunkSize) {
+ Parser->State = BodyParserChunkDataEnd;
+ }
+ break;
+
+ case BodyParserChunkDataEnd:
+ if (*Char == '\r') {
+ Parser->State = BodyParserChunkDataEndCR;
+ } else {
+ Parser->State = BodyParserStateMax;
+ }
+ Char++;
+ break;
+
+ case BodyParserChunkDataEndCR:
+ if (*Char != '\n') {
+ Parser->State = BodyParserStateMax;
+ break;
+ }
+ Char++;
+ Parser->State = BodyParserChunkSizeStart;
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+ if (Parser->State == BodyParserStateMax) {
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Check whether the message-body is complete or not.
+
+ @param[in] MsgParser Pointer to the message parser.
+
+ @retval TRUE Message-body is complete.
+ @retval FALSE Message-body is not complete.
+
+**/
+BOOLEAN
+EFIAPI
+HttpIsMessageComplete (
+ IN VOID *MsgParser
+ )
+{
+ HTTP_BODY_PARSER *Parser;
+
+ if (MsgParser == NULL) {
+ return FALSE;
+ }
+
+ Parser = (HTTP_BODY_PARSER *) MsgParser;
+
+ if (Parser->State == BodyParserComplete) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ Get the content length of the entity.
+
+ Note that in trunk transfer, the entity length is not valid until the whole message body is received.
+
+ @param[in] MsgParser Pointer to the message parser.
+ @param[out] ContentLength Pointer to store the length of the entity.
+
+ @retval EFI_SUCCESS Successfully to get the entity length.
+ @retval EFI_NOT_READY Entity length is not valid yet.
+ @retval EFI_INVALID_PARAMETER MsgParser is NULL or ContentLength is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpGetEntityLength (
+ IN VOID *MsgParser,
+ OUT UINTN *ContentLength
+ )
+{
+ HTTP_BODY_PARSER *Parser;
+
+ if (MsgParser == NULL || ContentLength == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Parser = (HTTP_BODY_PARSER *) MsgParser;
+
+ if (!Parser->ContentLengthIsValid) {
+ return EFI_NOT_READY;
+ }
+
+ *ContentLength = Parser->ContentLength;
+ return EFI_SUCCESS;
+}
+
+/**
+ Release the resource of the message parser.
+
+ @param[in] MsgParser Pointer to the message parser.
+
+**/
+VOID
+EFIAPI
+HttpFreeMsgParser (
+ IN VOID *MsgParser
+ )
+{
+ FreePool (MsgParser);
+}
+
+
+/**
+ Get the next string, which is distinguished by specified separator.
+
+ @param[in] String Pointer to the string.
+ @param[in] Separator Specified separator used to distinguish where is the beginning
+ of next string.
+
+ @return Pointer to the next string.
+ @return NULL if not find or String is NULL.
+
+**/
+CHAR8 *
+AsciiStrGetNextToken (
+ IN CONST CHAR8 *String,
+ IN CHAR8 Separator
+ )
+{
+ CONST CHAR8 *Token;
+
+ Token = String;
+ while (TRUE) {
+ if (*Token == 0) {
+ return NULL;
+ }
+ if (*Token == Separator) {
+ return (CHAR8 *)(Token + 1);
+ }
+ Token++;
+ }
+}
+
+/**
+ Set FieldName and FieldValue into specified HttpHeader.
+
+ @param[in,out] HttpHeader Specified HttpHeader.
+ @param[in] FieldName FieldName of this HttpHeader, a NULL terminated ASCII string.
+ @param[in] FieldValue FieldValue of this HttpHeader, a NULL terminated ASCII string.
+
+
+ @retval EFI_SUCCESS The FieldName and FieldValue are set into HttpHeader successfully.
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpSetFieldNameAndValue (
+ IN OUT EFI_HTTP_HEADER *HttpHeader,
+ IN CONST CHAR8 *FieldName,
+ IN CONST CHAR8 *FieldValue
+ )
+{
+ UINTN FieldNameSize;
+ UINTN FieldValueSize;
+
+ if (HttpHeader == NULL || FieldName == NULL || FieldValue == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (HttpHeader->FieldName != NULL) {
+ FreePool (HttpHeader->FieldName);
+ }
+ if (HttpHeader->FieldValue != NULL) {
+ FreePool (HttpHeader->FieldValue);
+ }
+
+ FieldNameSize = AsciiStrSize (FieldName);
+ HttpHeader->FieldName = AllocateZeroPool (FieldNameSize);
+ if (HttpHeader->FieldName == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (HttpHeader->FieldName, FieldName, FieldNameSize);
+ HttpHeader->FieldName[FieldNameSize - 1] = 0;
+
+ FieldValueSize = AsciiStrSize (FieldValue);
+ HttpHeader->FieldValue = AllocateZeroPool (FieldValueSize);
+ if (HttpHeader->FieldValue == NULL) {
+ FreePool (HttpHeader->FieldName);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (HttpHeader->FieldValue, FieldValue, FieldValueSize);
+ HttpHeader->FieldValue[FieldValueSize - 1] = 0;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get one key/value header pair from the raw string.
+
+ @param[in] String Pointer to the raw string.
+ @param[out] FieldName Points directly to field name within 'HttpHeader'.
+ @param[out] FieldValue Points directly to field value within 'HttpHeader'.
+
+ @return Pointer to the next raw string.
+ @return NULL if no key/value header pair from this raw string.
+
+**/
+CHAR8 *
+EFIAPI
+HttpGetFieldNameAndValue (
+ IN CHAR8 *String,
+ OUT CHAR8 **FieldName,
+ OUT CHAR8 **FieldValue
+ )
+{
+ CHAR8 *FieldNameStr;
+ CHAR8 *FieldValueStr;
+ CHAR8 *StrPtr;
+ CHAR8 *EndofHeader;
+
+ if (String == NULL || FieldName == NULL || FieldValue == NULL) {
+ return NULL;
+ }
+
+ *FieldName = NULL;
+ *FieldValue = NULL;
+ FieldNameStr = NULL;
+ FieldValueStr = NULL;
+ StrPtr = NULL;
+ EndofHeader = NULL;
+
+
+ //
+ // Check whether the raw HTTP header string is valid or not.
+ //
+ EndofHeader = AsciiStrStr (String, "\r\n\r\n");
+ if (EndofHeader == NULL) {
+ return NULL;
+ }
+
+ //
+ // Each header field consists of a name followed by a colon (":") and the field value.
+ // The field value MAY be preceded by any amount of LWS, though a single SP is preferred.
+ //
+ // message-header = field-name ":" [ field-value ]
+ // field-name = token
+ // field-value = *( field-content | LWS )
+ //
+ // Note: "*(element)" allows any number element, including zero; "1*(element)" requires at least one element.
+ // [element] means element is optional.
+ // LWS = [CRLF] 1*(SP|HT), it can be ' ' or '\t' or '\r\n ' or '\r\n\t'.
+ // CRLF = '\r\n'.
+ // SP = ' '.
+ // HT = '\t' (Tab).
+ //
+ FieldNameStr = String;
+ FieldValueStr = AsciiStrGetNextToken (FieldNameStr, ':');
+ if (FieldValueStr == NULL) {
+ return NULL;
+ }
+
+ //
+ // Replace ':' with 0, then FieldName has been retrived from String.
+ //
+ *(FieldValueStr - 1) = 0;
+
+ //
+ // Handle FieldValueStr, skip all the preceded LWS.
+ //
+ while (TRUE) {
+ if (*FieldValueStr == ' ' || *FieldValueStr == '\t') {
+ //
+ // Boundary condition check.
+ //
+ if ((UINTN) EndofHeader - (UINTN) FieldValueStr < 1) {
+ //
+ // Wrong String format!
+ //
+ return NULL;
+ }
+
+ FieldValueStr ++;
+ } else if (*FieldValueStr == '\r') {
+ //
+ // Boundary condition check.
+ //
+ if ((UINTN) EndofHeader - (UINTN) FieldValueStr < 3) {
+ //
+ // No more preceded LWS, so break here.
+ //
+ break;
+ }
+
+ if (*(FieldValueStr + 1) == '\n' ) {
+ if (*(FieldValueStr + 2) == ' ' || *(FieldValueStr + 2) == '\t') {
+ FieldValueStr = FieldValueStr + 3;
+ } else {
+ //
+ // No more preceded LWS, so break here.
+ //
+ break;
+ }
+ } else {
+ //
+ // Wrong String format!
+ //
+ return NULL;
+ }
+ } else {
+ //
+ // No more preceded LWS, so break here.
+ //
+ break;
+ }
+ }
+
+ StrPtr = FieldValueStr;
+ do {
+ //
+ // Handle the LWS within the field value.
+ //
+ StrPtr = AsciiStrGetNextToken (StrPtr, '\r');
+ if (StrPtr == NULL || *StrPtr != '\n') {
+ //
+ // Wrong String format!
+ //
+ return NULL;
+ }
+
+ StrPtr++;
+ } while (*StrPtr == ' ' || *StrPtr == '\t');
+
+ //
+ // Replace '\r' with 0
+ //
+ *(StrPtr - 2) = 0;
+
+ //
+ // Get FieldName and FieldValue.
+ //
+ *FieldName = FieldNameStr;
+ *FieldValue = FieldValueStr;
+
+ return StrPtr;
+}
+
+/**
+ Free existing HeaderFields.
+
+ @param[in] HeaderFields Pointer to array of key/value header pairs waitting for free.
+ @param[in] FieldCount The number of header pairs in HeaderFields.
+
+**/
+VOID
+EFIAPI
+HttpFreeHeaderFields (
+ IN EFI_HTTP_HEADER *HeaderFields,
+ IN UINTN FieldCount
+ )
+{
+ UINTN Index;
+
+ if (HeaderFields != NULL) {
+ for (Index = 0; Index < FieldCount; Index++) {
+ if (HeaderFields[Index].FieldName != NULL) {
+ FreePool (HeaderFields[Index].FieldName);
+ }
+ if (HeaderFields[Index].FieldValue != NULL) {
+ FreePool (HeaderFields[Index].FieldValue);
+ }
+ }
+
+ FreePool (HeaderFields);
+ }
+}
+
+/**
+ Generate HTTP request message.
+
+ This function will allocate memory for the whole HTTP message and generate a
+ well formatted HTTP Request message in it, include the Request-Line, header
+ fields and also the message body. It is the caller's responsibility to free
+ the buffer returned in *RequestMsg.
+
+ @param[in] Message Pointer to the EFI_HTTP_MESSAGE structure which
+ contains the required information to generate
+ the HTTP request message.
+ @param[in] Url The URL of a remote host.
+ @param[out] RequestMsg Pointer to the created HTTP request message.
+ NULL if any error occured.
+ @param[out] RequestMsgSize Size of the RequestMsg (in bytes).
+
+ @retval EFI_SUCCESS If HTTP request string was created successfully.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+ @retval EFI_INVALID_PARAMETER The input arguments are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpGenRequestMessage (
+ IN CONST EFI_HTTP_MESSAGE *Message,
+ IN CONST CHAR8 *Url,
+ OUT CHAR8 **RequestMsg,
+ OUT UINTN *RequestMsgSize
+ )
+{
+ EFI_STATUS Status;
+ UINTN StrLength;
+ CHAR8 *RequestPtr;
+ UINTN HttpHdrSize;
+ UINTN MsgSize;
+ BOOLEAN Success;
+ VOID *HttpHdr;
+ EFI_HTTP_HEADER **AppendList;
+ UINTN Index;
+ EFI_HTTP_UTILITIES_PROTOCOL *HttpUtilitiesProtocol;
+
+ Status = EFI_SUCCESS;
+ HttpHdrSize = 0;
+ MsgSize = 0;
+ Success = FALSE;
+ HttpHdr = NULL;
+ AppendList = NULL;
+ HttpUtilitiesProtocol = NULL;
+
+ //
+ // 1. If we have a Request, we cannot have a NULL Url
+ // 2. If we have a Request, HeaderCount can not be non-zero
+ // 3. If we do not have a Request, HeaderCount should be zero
+ // 4. If we do not have Request and Headers, we need at least a message-body
+ //
+ if ((Message == NULL || RequestMsg == NULL || RequestMsgSize == NULL) ||
+ (Message->Data.Request != NULL && Url == NULL) ||
+ (Message->Data.Request != NULL && Message->HeaderCount == 0) ||
+ (Message->Data.Request == NULL && Message->HeaderCount != 0) ||
+ (Message->Data.Request == NULL && Message->HeaderCount == 0 && Message->BodyLength == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Message->HeaderCount != 0) {
+ //
+ // Locate the HTTP_UTILITIES protocol.
+ //
+ Status = gBS->LocateProtocol (
+ &gEfiHttpUtilitiesProtocolGuid,
+ NULL,
+ (VOID **) &HttpUtilitiesProtocol
+ );
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR,"Failed to locate Http Utilities protocol. Status = %r.\n", Status));
+ return Status;
+ }
+
+ //
+ // Build AppendList to send into HttpUtilitiesBuild
+ //
+ AppendList = AllocateZeroPool (sizeof (EFI_HTTP_HEADER *) * (Message->HeaderCount));
+ if (AppendList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for(Index = 0; Index < Message->HeaderCount; Index++){
+ AppendList[Index] = &Message->Headers[Index];
+ }
+
+ //
+ // Build raw HTTP Headers
+ //
+ Status = HttpUtilitiesProtocol->Build (
+ HttpUtilitiesProtocol,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ Message->HeaderCount,
+ AppendList,
+ &HttpHdrSize,
+ &HttpHdr
+ );
+
+ FreePool (AppendList);
+
+ if (EFI_ERROR (Status) || HttpHdr == NULL){
+ return Status;
+ }
+ }
+
+ //
+ // If we have headers to be sent, account for it.
+ //
+ if (Message->HeaderCount != 0) {
+ MsgSize = HttpHdrSize;
+ }
+
+ //
+ // If we have a request line, account for the fields.
+ //
+ if (Message->Data.Request != NULL) {
+ MsgSize += HTTP_METHOD_MAXIMUM_LEN + AsciiStrLen (HTTP_VERSION_CRLF_STR) + AsciiStrLen (Url);
+ }
+
+
+ //
+ // If we have a message body to be sent, account for it.
+ //
+ MsgSize += Message->BodyLength;
+
+ //
+ // memory for the string that needs to be sent to TCP
+ //
+ *RequestMsg = NULL;
+ *RequestMsg = AllocateZeroPool (MsgSize);
+ if (*RequestMsg == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ RequestPtr = *RequestMsg;
+ //
+ // Construct header request
+ //
+ if (Message->Data.Request != NULL) {
+ switch (Message->Data.Request->Method) {
+ case HttpMethodGet:
+ StrLength = sizeof (HTTP_METHOD_GET) - 1;
+ CopyMem (RequestPtr, HTTP_METHOD_GET, StrLength);
+ RequestPtr += StrLength;
+ break;
+ case HttpMethodPut:
+ StrLength = sizeof (HTTP_METHOD_PUT) - 1;
+ CopyMem (RequestPtr, HTTP_METHOD_PUT, StrLength);
+ RequestPtr += StrLength;
+ break;
+ case HttpMethodPatch:
+ StrLength = sizeof (HTTP_METHOD_PATCH) - 1;
+ CopyMem (RequestPtr, HTTP_METHOD_PATCH, StrLength);
+ RequestPtr += StrLength;
+ break;
+ case HttpMethodPost:
+ StrLength = sizeof (HTTP_METHOD_POST) - 1;
+ CopyMem (RequestPtr, HTTP_METHOD_POST, StrLength);
+ RequestPtr += StrLength;
+ break;
+ case HttpMethodHead:
+ StrLength = sizeof (HTTP_METHOD_HEAD) - 1;
+ CopyMem (RequestPtr, HTTP_METHOD_HEAD, StrLength);
+ RequestPtr += StrLength;
+ break;
+ case HttpMethodDelete:
+ StrLength = sizeof (HTTP_METHOD_DELETE) - 1;
+ CopyMem (RequestPtr, HTTP_METHOD_DELETE, StrLength);
+ RequestPtr += StrLength;
+ break;
+ default:
+ ASSERT (FALSE);
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ StrLength = AsciiStrLen(EMPTY_SPACE);
+ CopyMem (RequestPtr, EMPTY_SPACE, StrLength);
+ RequestPtr += StrLength;
+
+ StrLength = AsciiStrLen (Url);
+ CopyMem (RequestPtr, Url, StrLength);
+ RequestPtr += StrLength;
+
+ StrLength = sizeof (HTTP_VERSION_CRLF_STR) - 1;
+ CopyMem (RequestPtr, HTTP_VERSION_CRLF_STR, StrLength);
+ RequestPtr += StrLength;
+
+ if (HttpHdr != NULL) {
+ //
+ // Construct header
+ //
+ CopyMem (RequestPtr, HttpHdr, HttpHdrSize);
+ RequestPtr += HttpHdrSize;
+ }
+ }
+
+ //
+ // Construct body
+ //
+ if (Message->Body != NULL) {
+ CopyMem (RequestPtr, Message->Body, Message->BodyLength);
+ RequestPtr += Message->BodyLength;
+ }
+
+ //
+ // Done
+ //
+ (*RequestMsgSize) = (UINTN)(RequestPtr) - (UINTN)(*RequestMsg);
+ Success = TRUE;
+
+Exit:
+
+ if (!Success) {
+ if (*RequestMsg != NULL) {
+ FreePool (*RequestMsg);
+ }
+ *RequestMsg = NULL;
+ return Status;
+ }
+
+ if (HttpHdr != NULL) {
+ FreePool (HttpHdr);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Translate the status code in HTTP message to EFI_HTTP_STATUS_CODE defined
+ in UEFI 2.5 specification.
+
+ @param[in] StatusCode The status code value in HTTP message.
+
+ @return Value defined in EFI_HTTP_STATUS_CODE .
+
+**/
+EFI_HTTP_STATUS_CODE
+EFIAPI
+HttpMappingToStatusCode (
+ IN UINTN StatusCode
+ )
+{
+ switch (StatusCode) {
+ case 100:
+ return HTTP_STATUS_100_CONTINUE;
+ case 101:
+ return HTTP_STATUS_101_SWITCHING_PROTOCOLS;
+ case 200:
+ return HTTP_STATUS_200_OK;
+ case 201:
+ return HTTP_STATUS_201_CREATED;
+ case 202:
+ return HTTP_STATUS_202_ACCEPTED;
+ case 203:
+ return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION;
+ case 204:
+ return HTTP_STATUS_204_NO_CONTENT;
+ case 205:
+ return HTTP_STATUS_205_RESET_CONTENT;
+ case 206:
+ return HTTP_STATUS_206_PARTIAL_CONTENT;
+ case 300:
+ return HTTP_STATUS_300_MULTIPLE_CHOICES;
+ case 301:
+ return HTTP_STATUS_301_MOVED_PERMANENTLY;
+ case 302:
+ return HTTP_STATUS_302_FOUND;
+ case 303:
+ return HTTP_STATUS_303_SEE_OTHER;
+ case 304:
+ return HTTP_STATUS_304_NOT_MODIFIED;
+ case 305:
+ return HTTP_STATUS_305_USE_PROXY;
+ case 307:
+ return HTTP_STATUS_307_TEMPORARY_REDIRECT;
+ case 308:
+ return HTTP_STATUS_308_PERMANENT_REDIRECT;
+ case 400:
+ return HTTP_STATUS_400_BAD_REQUEST;
+ case 401:
+ return HTTP_STATUS_401_UNAUTHORIZED;
+ case 402:
+ return HTTP_STATUS_402_PAYMENT_REQUIRED;
+ case 403:
+ return HTTP_STATUS_403_FORBIDDEN;
+ case 404:
+ return HTTP_STATUS_404_NOT_FOUND;
+ case 405:
+ return HTTP_STATUS_405_METHOD_NOT_ALLOWED;
+ case 406:
+ return HTTP_STATUS_406_NOT_ACCEPTABLE;
+ case 407:
+ return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED;
+ case 408:
+ return HTTP_STATUS_408_REQUEST_TIME_OUT;
+ case 409:
+ return HTTP_STATUS_409_CONFLICT;
+ case 410:
+ return HTTP_STATUS_410_GONE;
+ case 411:
+ return HTTP_STATUS_411_LENGTH_REQUIRED;
+ case 412:
+ return HTTP_STATUS_412_PRECONDITION_FAILED;
+ case 413:
+ return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE;
+ case 414:
+ return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE;
+ case 415:
+ return HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE;
+ case 416:
+ return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED;
+ case 417:
+ return HTTP_STATUS_417_EXPECTATION_FAILED;
+ case 500:
+ return HTTP_STATUS_500_INTERNAL_SERVER_ERROR;
+ case 501:
+ return HTTP_STATUS_501_NOT_IMPLEMENTED;
+ case 502:
+ return HTTP_STATUS_502_BAD_GATEWAY;
+ case 503:
+ return HTTP_STATUS_503_SERVICE_UNAVAILABLE;
+ case 504:
+ return HTTP_STATUS_504_GATEWAY_TIME_OUT;
+ case 505:
+ return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED;
+
+ default:
+ return HTTP_STATUS_UNSUPPORTED_STATUS;
+ }
+}
+
+/**
+ Check whether header field called FieldName is in DeleteList.
+
+ @param[in] DeleteList Pointer to array of key/value header pairs.
+ @param[in] DeleteCount The number of header pairs.
+ @param[in] FieldName Pointer to header field's name.
+
+ @return TRUE if FieldName is not in DeleteList, that means this header field is valid.
+ @return FALSE if FieldName is in DeleteList, that means this header field is invalid.
+
+**/
+BOOLEAN
+EFIAPI
+HttpIsValidHttpHeader (
+ IN CHAR8 *DeleteList[],
+ IN UINTN DeleteCount,
+ IN CHAR8 *FieldName
+ )
+{
+ UINTN Index;
+
+ if (FieldName == NULL) {
+ return FALSE;
+ }
+
+ for (Index = 0; Index < DeleteCount; Index++) {
+ if (DeleteList[Index] == NULL) {
+ continue;
+ }
+
+ if (AsciiStrCmp (FieldName, DeleteList[Index]) == 0) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
diff --git a/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.h b/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.h
new file mode 100644
index 0000000000..d064a98438
--- /dev/null
+++ b/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.h
@@ -0,0 +1,85 @@
+/** @file
+Header file for HttpLib.
+
+ Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _DXE_HTTP_LIB_H_
+#define _DXE_HTTP_LIB_H_
+
+#include <Uefi.h>
+#include <Library/NetLib.h>
+#include <Library/HttpLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <IndustryStandard/Http11.h>
+#include <Protocol/HttpUtilities.h>
+
+#define BIT(x) (1 << x)
+
+#define HTTP_VERSION_CRLF_STR " HTTP/1.1\r\n"
+#define EMPTY_SPACE " "
+
+#define NET_IS_HEX_CHAR(Ch) \
+ ((('0' <= (Ch)) && ((Ch) <= '9')) || \
+ (('A' <= (Ch)) && ((Ch) <= 'F')) || \
+ (('a' <= (Ch)) && ((Ch) <= 'f')))
+
+//
+// Field index of the HTTP URL parse result.
+//
+#define HTTP_URI_FIELD_SCHEME 0
+#define HTTP_URI_FIELD_AUTHORITY 1
+#define HTTP_URI_FIELD_PATH 2
+#define HTTP_URI_FIELD_QUERY 3
+#define HTTP_URI_FIELD_FRAGMENT 4
+#define HTTP_URI_FIELD_USERINFO 5
+#define HTTP_URI_FIELD_HOST 6
+#define HTTP_URI_FIELD_PORT 7
+#define HTTP_URI_FIELD_MAX 8
+
+#define HTTP_URI_PORT_MAX_NUM 65535
+
+//
+// Structure to store the parse result of a HTTP URL.
+//
+typedef struct {
+ UINT32 Offset;
+ UINT32 Length;
+} HTTP_URL_FILED_DATA;
+
+typedef struct {
+ UINT16 FieldBitMap;
+ HTTP_URL_FILED_DATA FieldData[HTTP_URI_FIELD_MAX];
+} HTTP_URL_PARSER;
+
+typedef enum {
+ UrlParserUrlStart,
+ UrlParserScheme,
+ UrlParserSchemeColon, // ":"
+ UrlParserSchemeColonSlash, // ":/"
+ UrlParserSchemeColonSlashSlash, // "://"
+ UrlParserAuthority,
+ UrlParserAtInAuthority,
+ UrlParserPath,
+ UrlParserQueryStart, // "?"
+ UrlParserQuery,
+ UrlParserFragmentStart, // "#"
+ UrlParserFragment,
+ UrlParserUserInfo,
+ UrlParserHostStart, // "@"
+ UrlParserHost,
+ UrlParserHostIpv6, // "["(Ipv6 address) "]"
+ UrlParserPortStart, // ":"
+ UrlParserPort,
+ UrlParserStateMax
+} HTTP_URL_PARSE_STATE;
+
+#endif
+
diff --git a/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.inf b/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.inf
new file mode 100644
index 0000000000..c613dc1718
--- /dev/null
+++ b/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.inf
@@ -0,0 +1,42 @@
+## @file
+# It provides the helper routines to parse the HTTP message byte stream.
+#
+# Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+# (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxeHttpLib
+ MODULE_UNI_FILE = DxeHttpLib.uni
+ FILE_GUID = ABBAB4CD-EA88-45b9-8234-C8A7450531FC
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = HttpLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ DxeHttpLib.c
+ DxeHttpLib.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ NetLib
+
+[Protocols]
+ gEfiHttpUtilitiesProtocolGuid ## SOMETIMES_CONSUMES
diff --git a/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.uni b/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.uni
new file mode 100644
index 0000000000..a2954c023e
--- /dev/null
+++ b/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.uni
@@ -0,0 +1,16 @@
+// /** @file
+// Provides the helper routines for HTTP.
+//
+// This library instance provides the helper routines to parse the HTTP message byte stream.
+//
+// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Provides the helper routines for HTTP"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This library instance provides the helper routines to parse the HTTP message byte stream."
+
diff --git a/NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.c b/NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.c
new file mode 100644
index 0000000000..d45f0070b3
--- /dev/null
+++ b/NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.c
@@ -0,0 +1,2291 @@
+/** @file
+ IpIo Library.
+
+(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <Protocol/Udp4.h>
+
+#include <Library/IpIoLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DpcLib.h>
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED LIST_ENTRY mActiveIpIoList = {
+ &mActiveIpIoList,
+ &mActiveIpIoList
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_IP4_CONFIG_DATA mIp4IoDefaultIpConfigData = {
+ EFI_IP_PROTO_UDP,
+ FALSE,
+ TRUE,
+ FALSE,
+ FALSE,
+ FALSE,
+ {{0, 0, 0, 0}},
+ {{0, 0, 0, 0}},
+ 0,
+ 255,
+ FALSE,
+ FALSE,
+ 0,
+ 0
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_IP6_CONFIG_DATA mIp6IoDefaultIpConfigData = {
+ EFI_IP_PROTO_UDP,
+ FALSE,
+ TRUE,
+ FALSE,
+ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+ 0,
+ 255,
+ 0,
+ 0,
+ 0
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED ICMP_ERROR_INFO mIcmpErrMap[10] = {
+ {FALSE, TRUE }, // ICMP_ERR_UNREACH_NET
+ {FALSE, TRUE }, // ICMP_ERR_UNREACH_HOST
+ {TRUE, TRUE }, // ICMP_ERR_UNREACH_PROTOCOL
+ {TRUE, TRUE }, // ICMP_ERR_UNREACH_PORT
+ {TRUE, TRUE }, // ICMP_ERR_MSGSIZE
+ {FALSE, TRUE }, // ICMP_ERR_UNREACH_SRCFAIL
+ {FALSE, TRUE }, // ICMP_ERR_TIMXCEED_INTRANS
+ {FALSE, TRUE }, // ICMP_ERR_TIMEXCEED_REASS
+ {FALSE, FALSE}, // ICMP_ERR_QUENCH
+ {FALSE, TRUE } // ICMP_ERR_PARAMPROB
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED ICMP_ERROR_INFO mIcmp6ErrMap[10] = {
+ {FALSE, TRUE}, // ICMP6_ERR_UNREACH_NET
+ {FALSE, TRUE}, // ICMP6_ERR_UNREACH_HOST
+ {TRUE, TRUE}, // ICMP6_ERR_UNREACH_PROTOCOL
+ {TRUE, TRUE}, // ICMP6_ERR_UNREACH_PORT
+ {TRUE, TRUE}, // ICMP6_ERR_PACKAGE_TOOBIG
+ {FALSE, TRUE}, // ICMP6_ERR_TIMXCEED_HOPLIMIT
+ {FALSE, TRUE}, // ICMP6_ERR_TIMXCEED_REASS
+ {FALSE, TRUE}, // ICMP6_ERR_PARAMPROB_HEADER
+ {FALSE, TRUE}, // ICMP6_ERR_PARAMPROB_NEXHEADER
+ {FALSE, TRUE} // ICMP6_ERR_PARAMPROB_IPV6OPTION
+};
+
+
+/**
+ Notify function for IP transmit token.
+
+ @param[in] Context The context passed in by the event notifier.
+
+**/
+VOID
+EFIAPI
+IpIoTransmitHandlerDpc (
+ IN VOID *Context
+ );
+
+
+/**
+ Notify function for IP transmit token.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The context passed in by the event notifier.
+
+**/
+VOID
+EFIAPI
+IpIoTransmitHandler (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+
+/**
+ This function create an IP child ,open the IP protocol, and return the opened
+ IP protocol as Interface.
+
+ @param[in] ControllerHandle The controller handle.
+ @param[in] ImageHandle The image handle.
+ @param[in] ChildHandle Pointer to the buffer to save the IP child handle.
+ @param[in] IpVersion The version of the IP protocol to use, either
+ IPv4 or IPv6.
+ @param[out] Interface Pointer used to get the IP protocol interface.
+
+ @retval EFI_SUCCESS The IP child is created and the IP protocol
+ interface is retrieved.
+ @retval EFI_UNSUPPORTED Upsupported IpVersion.
+ @retval Others The required operation failed.
+
+**/
+EFI_STATUS
+IpIoCreateIpChildOpenProtocol (
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE *ChildHandle,
+ IN UINT8 IpVersion,
+ OUT VOID **Interface
+ )
+{
+ EFI_STATUS Status;
+ EFI_GUID *ServiceBindingGuid;
+ EFI_GUID *IpProtocolGuid;
+
+ if (IpVersion == IP_VERSION_4) {
+ ServiceBindingGuid = &gEfiIp4ServiceBindingProtocolGuid;
+ IpProtocolGuid = &gEfiIp4ProtocolGuid;
+ } else if (IpVersion == IP_VERSION_6){
+ ServiceBindingGuid = &gEfiIp6ServiceBindingProtocolGuid;
+ IpProtocolGuid = &gEfiIp6ProtocolGuid;
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Create an IP child.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ ImageHandle,
+ ServiceBindingGuid,
+ ChildHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Open the IP protocol installed on the *ChildHandle.
+ //
+ Status = gBS->OpenProtocol (
+ *ChildHandle,
+ IpProtocolGuid,
+ Interface,
+ ImageHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // On failure, destroy the IP child.
+ //
+ NetLibDestroyServiceChild (
+ ControllerHandle,
+ ImageHandle,
+ ServiceBindingGuid,
+ *ChildHandle
+ );
+ }
+
+ return Status;
+}
+
+
+/**
+ This function close the previously openned IP protocol and destroy the IP child.
+
+ @param[in] ControllerHandle The controller handle.
+ @param[in] ImageHandle The image handle.
+ @param[in] ChildHandle The child handle of the IP child.
+ @param[in] IpVersion The version of the IP protocol to use, either
+ IPv4 or IPv6.
+
+ @retval EFI_SUCCESS The IP protocol is closed and the relevant IP child
+ is destroyed.
+ @retval EFI_UNSUPPORTED Upsupported IpVersion.
+ @retval Others The required operation failed.
+
+**/
+EFI_STATUS
+IpIoCloseProtocolDestroyIpChild (
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ChildHandle,
+ IN UINT8 IpVersion
+ )
+{
+ EFI_STATUS Status;
+ EFI_GUID *ServiceBindingGuid;
+ EFI_GUID *IpProtocolGuid;
+
+ if (IpVersion == IP_VERSION_4) {
+ ServiceBindingGuid = &gEfiIp4ServiceBindingProtocolGuid;
+ IpProtocolGuid = &gEfiIp4ProtocolGuid;
+ } else if (IpVersion == IP_VERSION_6) {
+ ServiceBindingGuid = &gEfiIp6ServiceBindingProtocolGuid;
+ IpProtocolGuid = &gEfiIp6ProtocolGuid;
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Close the previously openned IP protocol.
+ //
+ Status = gBS->CloseProtocol (
+ ChildHandle,
+ IpProtocolGuid,
+ ImageHandle,
+ ControllerHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Destroy the IP child.
+ //
+ return NetLibDestroyServiceChild (
+ ControllerHandle,
+ ImageHandle,
+ ServiceBindingGuid,
+ ChildHandle
+ );
+}
+
+/**
+ This function handles ICMPv4 packets. It is the worker function of
+ IpIoIcmpHandler.
+
+ @param[in] IpIo Pointer to the IP_IO instance.
+ @param[in, out] Pkt Pointer to the ICMPv4 packet.
+ @param[in] Session Pointer to the net session of this ICMPv4 packet.
+
+ @retval EFI_SUCCESS The ICMPv4 packet is handled successfully.
+ @retval EFI_ABORTED This type of ICMPv4 packet is not supported.
+
+**/
+EFI_STATUS
+IpIoIcmpv4Handler (
+ IN IP_IO *IpIo,
+ IN OUT NET_BUF *Pkt,
+ IN EFI_NET_SESSION_DATA *Session
+ )
+{
+ IP4_ICMP_ERROR_HEAD *IcmpHdr;
+ EFI_IP4_HEADER *IpHdr;
+ UINT8 IcmpErr;
+ UINT8 *PayLoadHdr;
+ UINT8 Type;
+ UINT8 Code;
+ UINT32 TrimBytes;
+
+ ASSERT (IpIo != NULL);
+ ASSERT (Pkt != NULL);
+ ASSERT (Session != NULL);
+ ASSERT (IpIo->IpVersion == IP_VERSION_4);
+
+ //
+ // Check the ICMP packet length.
+ //
+ if (Pkt->TotalSize < sizeof (IP4_ICMP_ERROR_HEAD)) {
+ return EFI_ABORTED;
+ }
+
+ IcmpHdr = NET_PROTO_HDR (Pkt, IP4_ICMP_ERROR_HEAD);
+ IpHdr = (EFI_IP4_HEADER *) (&IcmpHdr->IpHead);
+
+ if (Pkt->TotalSize < ICMP_ERRLEN (IpHdr)) {
+
+ return EFI_ABORTED;
+ }
+
+ Type = IcmpHdr->Head.Type;
+ Code = IcmpHdr->Head.Code;
+
+ //
+ // Analyze the ICMP Error in this ICMP pkt
+ //
+ switch (Type) {
+ case ICMP_TYPE_UNREACH:
+ switch (Code) {
+ case ICMP_CODE_UNREACH_NET:
+ case ICMP_CODE_UNREACH_HOST:
+ case ICMP_CODE_UNREACH_PROTOCOL:
+ case ICMP_CODE_UNREACH_PORT:
+ case ICMP_CODE_UNREACH_SRCFAIL:
+ IcmpErr = (UINT8) (ICMP_ERR_UNREACH_NET + Code);
+
+ break;
+
+ case ICMP_CODE_UNREACH_NEEDFRAG:
+ IcmpErr = ICMP_ERR_MSGSIZE;
+
+ break;
+
+ case ICMP_CODE_UNREACH_NET_UNKNOWN:
+ case ICMP_CODE_UNREACH_NET_PROHIB:
+ case ICMP_CODE_UNREACH_TOSNET:
+ IcmpErr = ICMP_ERR_UNREACH_NET;
+
+ break;
+
+ case ICMP_CODE_UNREACH_HOST_UNKNOWN:
+ case ICMP_CODE_UNREACH_ISOLATED:
+ case ICMP_CODE_UNREACH_HOST_PROHIB:
+ case ICMP_CODE_UNREACH_TOSHOST:
+ IcmpErr = ICMP_ERR_UNREACH_HOST;
+
+ break;
+
+ default:
+ return EFI_ABORTED;
+ }
+
+ break;
+
+ case ICMP_TYPE_TIMXCEED:
+ if (Code > 1) {
+ return EFI_ABORTED;
+ }
+
+ IcmpErr = (UINT8) (Code + ICMP_ERR_TIMXCEED_INTRANS);
+
+ break;
+
+ case ICMP_TYPE_PARAMPROB:
+ if (Code > 1) {
+ return EFI_ABORTED;
+ }
+
+ IcmpErr = ICMP_ERR_PARAMPROB;
+
+ break;
+
+ case ICMP_TYPE_SOURCEQUENCH:
+ if (Code != 0) {
+ return EFI_ABORTED;
+ }
+
+ IcmpErr = ICMP_ERR_QUENCH;
+
+ break;
+
+ default:
+ return EFI_ABORTED;
+ }
+
+ //
+ // Notify user the ICMP pkt only containing payload except
+ // IP and ICMP header
+ //
+ PayLoadHdr = (UINT8 *) ((UINT8 *) IpHdr + EFI_IP4_HEADER_LEN (IpHdr));
+ TrimBytes = (UINT32) (PayLoadHdr - (UINT8 *) IcmpHdr);
+
+ NetbufTrim (Pkt, TrimBytes, TRUE);
+
+ //
+ // If the input packet has invalid format, and TrimBytes is larger than
+ // the packet size, the NetbufTrim might trim the packet to zero.
+ //
+ if (Pkt->TotalSize != 0) {
+ IpIo->PktRcvdNotify (EFI_ICMP_ERROR, IcmpErr, Session, Pkt, IpIo->RcvdContext);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function handles ICMPv6 packets. It is the worker function of
+ IpIoIcmpHandler.
+
+ @param[in] IpIo Pointer to the IP_IO instance.
+ @param[in, out] Pkt Pointer to the ICMPv6 packet.
+ @param[in] Session Pointer to the net session of this ICMPv6 packet.
+
+ @retval EFI_SUCCESS The ICMPv6 packet is handled successfully.
+ @retval EFI_ABORTED This type of ICMPv6 packet is not supported.
+
+**/
+EFI_STATUS
+IpIoIcmpv6Handler (
+ IN IP_IO *IpIo,
+ IN OUT NET_BUF *Pkt,
+ IN EFI_NET_SESSION_DATA *Session
+ )
+{
+ IP6_ICMP_ERROR_HEAD *IcmpHdr;
+ EFI_IP6_HEADER *IpHdr;
+ UINT8 IcmpErr;
+ UINT8 *PayLoadHdr;
+ UINT8 Type;
+ UINT8 Code;
+ UINT8 NextHeader;
+ UINT32 TrimBytes;
+ BOOLEAN Flag;
+
+ ASSERT (IpIo != NULL);
+ ASSERT (Pkt != NULL);
+ ASSERT (Session != NULL);
+ ASSERT (IpIo->IpVersion == IP_VERSION_6);
+
+ //
+ // Check the ICMPv6 packet length.
+ //
+ if (Pkt->TotalSize < sizeof (IP6_ICMP_ERROR_HEAD)) {
+
+ return EFI_ABORTED;
+ }
+
+ IcmpHdr = NET_PROTO_HDR (Pkt, IP6_ICMP_ERROR_HEAD);
+ Type = IcmpHdr->Head.Type;
+ Code = IcmpHdr->Head.Code;
+
+ //
+ // Analyze the ICMPv6 Error in this ICMPv6 packet
+ //
+ switch (Type) {
+ case ICMP_V6_DEST_UNREACHABLE:
+ switch (Code) {
+ case ICMP_V6_NO_ROUTE_TO_DEST:
+ case ICMP_V6_BEYOND_SCOPE:
+ case ICMP_V6_ROUTE_REJECTED:
+ IcmpErr = ICMP6_ERR_UNREACH_NET;
+
+ break;
+
+ case ICMP_V6_COMM_PROHIBITED:
+ case ICMP_V6_ADDR_UNREACHABLE:
+ case ICMP_V6_SOURCE_ADDR_FAILED:
+ IcmpErr = ICMP6_ERR_UNREACH_HOST;
+
+ break;
+
+ case ICMP_V6_PORT_UNREACHABLE:
+ IcmpErr = ICMP6_ERR_UNREACH_PORT;
+
+ break;
+
+ default:
+ return EFI_ABORTED;
+ }
+
+ break;
+
+ case ICMP_V6_PACKET_TOO_BIG:
+ if (Code >= 1) {
+ return EFI_ABORTED;
+ }
+
+ IcmpErr = ICMP6_ERR_PACKAGE_TOOBIG;
+
+ break;
+
+ case ICMP_V6_TIME_EXCEEDED:
+ if (Code > 1) {
+ return EFI_ABORTED;
+ }
+
+ IcmpErr = (UINT8) (ICMP6_ERR_TIMXCEED_HOPLIMIT + Code);
+
+ break;
+
+ case ICMP_V6_PARAMETER_PROBLEM:
+ if (Code > 3) {
+ return EFI_ABORTED;
+ }
+
+ IcmpErr = (UINT8) (ICMP6_ERR_PARAMPROB_HEADER + Code);
+
+ break;
+
+ default:
+
+ return EFI_ABORTED;
+ }
+
+ //
+ // Notify user the ICMPv6 packet only containing payload except
+ // IPv6 basic header, extension header and ICMP header
+ //
+
+ IpHdr = (EFI_IP6_HEADER *) (&IcmpHdr->IpHead);
+ NextHeader = IpHdr->NextHeader;
+ PayLoadHdr = (UINT8 *) ((UINT8 *) IcmpHdr + sizeof (IP6_ICMP_ERROR_HEAD));
+ Flag = TRUE;
+
+ do {
+ switch (NextHeader) {
+ case EFI_IP_PROTO_UDP:
+ case EFI_IP_PROTO_TCP:
+ case EFI_IP_PROTO_ICMP:
+ case IP6_NO_NEXT_HEADER:
+ Flag = FALSE;
+
+ break;
+
+ case IP6_HOP_BY_HOP:
+ case IP6_DESTINATION:
+ //
+ // The Hdr Ext Len is 8-bit unsigned integer in 8-octet units, not including
+ // the first 8 octets.
+ //
+ NextHeader = *(PayLoadHdr);
+ PayLoadHdr = (UINT8 *) (PayLoadHdr + (*(PayLoadHdr + 1) + 1) * 8);
+
+ break;
+
+ case IP6_FRAGMENT:
+ //
+ // The Fragment Header Length is 8 octets.
+ //
+ NextHeader = *(PayLoadHdr);
+ PayLoadHdr = (UINT8 *) (PayLoadHdr + 8);
+
+ break;
+
+ default:
+
+ return EFI_ABORTED;
+ }
+ } while (Flag);
+
+ TrimBytes = (UINT32) (PayLoadHdr - (UINT8 *) IcmpHdr);
+
+ NetbufTrim (Pkt, TrimBytes, TRUE);
+
+ //
+ // If the input packet has invalid format, and TrimBytes is larger than
+ // the packet size, the NetbufTrim might trim the packet to zero.
+ //
+ if (Pkt->TotalSize != 0) {
+ IpIo->PktRcvdNotify (EFI_ICMP_ERROR, IcmpErr, Session, Pkt, IpIo->RcvdContext);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function handles ICMP packets.
+
+ @param[in] IpIo Pointer to the IP_IO instance.
+ @param[in, out] Pkt Pointer to the ICMP packet.
+ @param[in] Session Pointer to the net session of this ICMP packet.
+
+ @retval EFI_SUCCESS The ICMP packet is handled successfully.
+ @retval EFI_ABORTED This type of ICMP packet is not supported.
+ @retval EFI_UNSUPPORTED The IP protocol version in IP_IO is not supported.
+
+**/
+EFI_STATUS
+IpIoIcmpHandler (
+ IN IP_IO *IpIo,
+ IN OUT NET_BUF *Pkt,
+ IN EFI_NET_SESSION_DATA *Session
+ )
+{
+
+ if (IpIo->IpVersion == IP_VERSION_4) {
+
+ return IpIoIcmpv4Handler (IpIo, Pkt, Session);
+
+ } else if (IpIo->IpVersion == IP_VERSION_6) {
+
+ return IpIoIcmpv6Handler (IpIo, Pkt, Session);
+
+ } else {
+
+ return EFI_UNSUPPORTED;
+ }
+}
+
+
+/**
+ Free function for receive token of IP_IO. It is used to
+ signal the recycle event to notify IP to recycle the
+ data buffer.
+
+ @param[in] Event The event to be signaled.
+
+**/
+VOID
+EFIAPI
+IpIoExtFree (
+ IN VOID *Event
+ )
+{
+ gBS->SignalEvent ((EFI_EVENT) Event);
+}
+
+
+/**
+ Create a send entry to wrap a packet before sending
+ out it through IP.
+
+ @param[in, out] IpIo Pointer to the IP_IO instance.
+ @param[in, out] Pkt Pointer to the packet.
+ @param[in] Sender Pointer to the IP sender.
+ @param[in] Context Pointer to the context.
+ @param[in] NotifyData Pointer to the notify data.
+ @param[in] Dest Pointer to the destination IP address.
+ @param[in] Override Pointer to the overriden IP_IO data.
+
+ @return Pointer to the data structure created to wrap the packet. If any error occurs,
+ then return NULL.
+
+**/
+IP_IO_SEND_ENTRY *
+IpIoCreateSndEntry (
+ IN OUT IP_IO *IpIo,
+ IN OUT NET_BUF *Pkt,
+ IN IP_IO_IP_PROTOCOL Sender,
+ IN VOID *Context OPTIONAL,
+ IN VOID *NotifyData OPTIONAL,
+ IN EFI_IP_ADDRESS *Dest OPTIONAL,
+ IN IP_IO_OVERRIDE *Override
+ )
+{
+ IP_IO_SEND_ENTRY *SndEntry;
+ EFI_EVENT Event;
+ EFI_STATUS Status;
+ NET_FRAGMENT *ExtFragment;
+ UINT32 FragmentCount;
+ IP_IO_OVERRIDE *OverrideData;
+ IP_IO_IP_TX_DATA *TxData;
+ EFI_IP4_TRANSMIT_DATA *Ip4TxData;
+ EFI_IP6_TRANSMIT_DATA *Ip6TxData;
+
+ if ((IpIo->IpVersion != IP_VERSION_4) && (IpIo->IpVersion != IP_VERSION_6)) {
+ return NULL;
+ }
+
+ Event = NULL;
+ TxData = NULL;
+ OverrideData = NULL;
+
+ //
+ // Allocate resource for SndEntry
+ //
+ SndEntry = AllocatePool (sizeof (IP_IO_SEND_ENTRY));
+ if (NULL == SndEntry) {
+ return NULL;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ IpIoTransmitHandler,
+ SndEntry,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ FragmentCount = Pkt->BlockOpNum;
+
+ //
+ // Allocate resource for TxData
+ //
+ TxData = (IP_IO_IP_TX_DATA *) AllocatePool (
+ sizeof (IP_IO_IP_TX_DATA) + sizeof (NET_FRAGMENT) * (FragmentCount - 1)
+ );
+
+ if (NULL == TxData) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Build a fragment table to contain the fragments in the packet.
+ //
+ if (IpIo->IpVersion == IP_VERSION_4) {
+ ExtFragment = (NET_FRAGMENT *) TxData->Ip4TxData.FragmentTable;
+ } else {
+ ExtFragment = (NET_FRAGMENT *) TxData->Ip6TxData.FragmentTable;
+ }
+
+ NetbufBuildExt (Pkt, ExtFragment, &FragmentCount);
+
+
+ //
+ // Allocate resource for OverrideData if needed
+ //
+ if (NULL != Override) {
+
+ OverrideData = AllocateCopyPool (sizeof (IP_IO_OVERRIDE), Override);
+ if (NULL == OverrideData) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // Set other fields of TxData except the fragment table
+ //
+ if (IpIo->IpVersion == IP_VERSION_4) {
+
+ Ip4TxData = &TxData->Ip4TxData;
+
+ IP4_COPY_ADDRESS (&Ip4TxData->DestinationAddress, Dest);
+
+ Ip4TxData->OverrideData = &OverrideData->Ip4OverrideData;
+ Ip4TxData->OptionsLength = 0;
+ Ip4TxData->OptionsBuffer = NULL;
+ Ip4TxData->TotalDataLength = Pkt->TotalSize;
+ Ip4TxData->FragmentCount = FragmentCount;
+
+ //
+ // Set the fields of SndToken
+ //
+ SndEntry->SndToken.Ip4Token.Event = Event;
+ SndEntry->SndToken.Ip4Token.Packet.TxData = Ip4TxData;
+ } else {
+
+ Ip6TxData = &TxData->Ip6TxData;
+
+ if (Dest != NULL) {
+ CopyMem (&Ip6TxData->DestinationAddress, Dest, sizeof (EFI_IPv6_ADDRESS));
+ } else {
+ ZeroMem (&Ip6TxData->DestinationAddress, sizeof (EFI_IPv6_ADDRESS));
+ }
+
+ Ip6TxData->OverrideData = &OverrideData->Ip6OverrideData;
+ Ip6TxData->DataLength = Pkt->TotalSize;
+ Ip6TxData->FragmentCount = FragmentCount;
+ Ip6TxData->ExtHdrsLength = 0;
+ Ip6TxData->ExtHdrs = NULL;
+
+ //
+ // Set the fields of SndToken
+ //
+ SndEntry->SndToken.Ip6Token.Event = Event;
+ SndEntry->SndToken.Ip6Token.Packet.TxData = Ip6TxData;
+ }
+
+ //
+ // Set the fields of SndEntry
+ //
+ SndEntry->IpIo = IpIo;
+ SndEntry->Ip = Sender;
+ SndEntry->Context = Context;
+ SndEntry->NotifyData = NotifyData;
+
+ SndEntry->Pkt = Pkt;
+ NET_GET_REF (Pkt);
+
+ InsertTailList (&IpIo->PendingSndList, &SndEntry->Entry);
+
+ return SndEntry;
+
+ON_ERROR:
+
+ if (OverrideData != NULL) {
+ FreePool (OverrideData);
+ }
+
+ if (TxData != NULL) {
+ FreePool (TxData);
+ }
+
+ if (SndEntry != NULL) {
+ FreePool (SndEntry);
+ }
+
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ return NULL;
+}
+
+
+/**
+ Destroy the SndEntry.
+
+ This function pairs with IpIoCreateSndEntry().
+
+ @param[in] SndEntry Pointer to the send entry to be destroyed.
+
+**/
+VOID
+IpIoDestroySndEntry (
+ IN IP_IO_SEND_ENTRY *SndEntry
+ )
+{
+ EFI_EVENT Event;
+ IP_IO_IP_TX_DATA *TxData;
+ IP_IO_OVERRIDE *Override;
+
+ if (SndEntry->IpIo->IpVersion == IP_VERSION_4) {
+ Event = SndEntry->SndToken.Ip4Token.Event;
+ TxData = (IP_IO_IP_TX_DATA *) SndEntry->SndToken.Ip4Token.Packet.TxData;
+ Override = (IP_IO_OVERRIDE *) TxData->Ip4TxData.OverrideData;
+ } else if (SndEntry->IpIo->IpVersion == IP_VERSION_6) {
+ Event = SndEntry->SndToken.Ip6Token.Event;
+ TxData = (IP_IO_IP_TX_DATA *) SndEntry->SndToken.Ip6Token.Packet.TxData;
+ Override = (IP_IO_OVERRIDE *) TxData->Ip6TxData.OverrideData;
+ } else {
+ return ;
+ }
+
+ gBS->CloseEvent (Event);
+
+ FreePool (TxData);
+
+ if (NULL != Override) {
+ FreePool (Override);
+ }
+
+ NetbufFree (SndEntry->Pkt);
+
+ RemoveEntryList (&SndEntry->Entry);
+
+ FreePool (SndEntry);
+}
+
+
+/**
+ Notify function for IP transmit token.
+
+ @param[in] Context The context passed in by the event notifier.
+
+**/
+VOID
+EFIAPI
+IpIoTransmitHandlerDpc (
+ IN VOID *Context
+ )
+{
+ IP_IO *IpIo;
+ IP_IO_SEND_ENTRY *SndEntry;
+ EFI_STATUS Status;
+
+ SndEntry = (IP_IO_SEND_ENTRY *) Context;
+
+ IpIo = SndEntry->IpIo;
+
+ if (IpIo->IpVersion == IP_VERSION_4) {
+ Status = SndEntry->SndToken.Ip4Token.Status;
+ } else if (IpIo->IpVersion == IP_VERSION_6){
+ Status = SndEntry->SndToken.Ip6Token.Status;
+ } else {
+ return ;
+ }
+
+ if ((IpIo->PktSentNotify != NULL) && (SndEntry->NotifyData != NULL)) {
+ IpIo->PktSentNotify (
+ Status,
+ SndEntry->Context,
+ SndEntry->Ip,
+ SndEntry->NotifyData
+ );
+ }
+
+ IpIoDestroySndEntry (SndEntry);
+}
+
+
+/**
+ Notify function for IP transmit token.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The context passed in by the event notifier.
+
+**/
+VOID
+EFIAPI
+IpIoTransmitHandler (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request IpIoTransmitHandlerDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, IpIoTransmitHandlerDpc, Context);
+}
+
+
+/**
+ The dummy handler for the dummy IP receive token.
+
+ @param[in] Context The context passed in by the event notifier.
+
+**/
+VOID
+EFIAPI
+IpIoDummyHandlerDpc (
+ IN VOID *Context
+ )
+{
+ IP_IO_IP_INFO *IpInfo;
+ EFI_STATUS Status;
+ EFI_EVENT RecycleEvent;
+
+ IpInfo = (IP_IO_IP_INFO *) Context;
+
+ if ((IpInfo->IpVersion != IP_VERSION_4) && (IpInfo->IpVersion != IP_VERSION_6)) {
+ return ;
+ }
+
+ RecycleEvent = NULL;
+
+ if (IpInfo->IpVersion == IP_VERSION_4) {
+ Status = IpInfo->DummyRcvToken.Ip4Token.Status;
+
+ if (IpInfo->DummyRcvToken.Ip4Token.Packet.RxData != NULL) {
+ RecycleEvent = IpInfo->DummyRcvToken.Ip4Token.Packet.RxData->RecycleSignal;
+ }
+ } else {
+ Status = IpInfo->DummyRcvToken.Ip6Token.Status;
+
+ if (IpInfo->DummyRcvToken.Ip6Token.Packet.RxData != NULL) {
+ RecycleEvent = IpInfo->DummyRcvToken.Ip6Token.Packet.RxData->RecycleSignal;
+ }
+ }
+
+
+
+ if (EFI_ABORTED == Status) {
+ //
+ // The reception is actively aborted by the consumer, directly return.
+ //
+ return;
+ } else if (EFI_SUCCESS == Status) {
+ //
+ // Recycle the RxData.
+ //
+ ASSERT (RecycleEvent != NULL);
+
+ gBS->SignalEvent (RecycleEvent);
+ }
+
+ //
+ // Continue the receive.
+ //
+ if (IpInfo->IpVersion == IP_VERSION_4) {
+ IpInfo->Ip.Ip4->Receive (
+ IpInfo->Ip.Ip4,
+ &IpInfo->DummyRcvToken.Ip4Token
+ );
+ } else {
+ IpInfo->Ip.Ip6->Receive (
+ IpInfo->Ip.Ip6,
+ &IpInfo->DummyRcvToken.Ip6Token
+ );
+ }
+}
+
+
+/**
+ This function add IpIoDummyHandlerDpc to the end of the DPC queue.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The context passed in by the event notifier.
+
+**/
+VOID
+EFIAPI
+IpIoDummyHandler (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request IpIoDummyHandlerDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, IpIoDummyHandlerDpc, Context);
+}
+
+
+/**
+ Notify function for the IP receive token, used to process
+ the received IP packets.
+
+ @param[in] Context The context passed in by the event notifier.
+
+**/
+VOID
+EFIAPI
+IpIoListenHandlerDpc (
+ IN VOID *Context
+ )
+{
+ IP_IO *IpIo;
+ EFI_STATUS Status;
+ IP_IO_IP_RX_DATA *RxData;
+ EFI_NET_SESSION_DATA Session;
+ NET_BUF *Pkt;
+
+ IpIo = (IP_IO *) Context;
+
+ if (IpIo->IpVersion == IP_VERSION_4) {
+ Status = IpIo->RcvToken.Ip4Token.Status;
+ RxData = (IP_IO_IP_RX_DATA *) IpIo->RcvToken.Ip4Token.Packet.RxData;
+ } else if (IpIo->IpVersion == IP_VERSION_6) {
+ Status = IpIo->RcvToken.Ip6Token.Status;
+ RxData = (IP_IO_IP_RX_DATA *) IpIo->RcvToken.Ip6Token.Packet.RxData;
+ } else {
+ return;
+ }
+
+ if (EFI_ABORTED == Status) {
+ //
+ // The reception is actively aborted by the consumer, directly return.
+ //
+ return;
+ }
+
+ if ((EFI_SUCCESS != Status) && (EFI_ICMP_ERROR != Status)) {
+ //
+ // Only process the normal packets and the icmp error packets.
+ //
+ if (RxData != NULL) {
+ goto CleanUp;
+ } else {
+ goto Resume;
+ }
+ }
+
+ //
+ // if RxData is NULL with Status == EFI_SUCCESS or EFI_ICMP_ERROR, this should be a code issue in the low layer (IP).
+ //
+ ASSERT (RxData != NULL);
+ if (RxData == NULL) {
+ goto Resume;
+ }
+
+ if (NULL == IpIo->PktRcvdNotify) {
+ goto CleanUp;
+ }
+
+ if (IpIo->IpVersion == IP_VERSION_4) {
+ ASSERT (RxData->Ip4RxData.Header != NULL);
+ if (IP4_IS_LOCAL_BROADCAST (EFI_IP4 (RxData->Ip4RxData.Header->SourceAddress))) {
+ //
+ // The source address is a broadcast address, discard it.
+ //
+ goto CleanUp;
+ }
+ if ((EFI_IP4 (RxData->Ip4RxData.Header->SourceAddress) != 0) &&
+ (IpIo->SubnetMask != 0) &&
+ IP4_NET_EQUAL (IpIo->StationIp, EFI_NTOHL (((EFI_IP4_RECEIVE_DATA *) RxData)->Header->SourceAddress), IpIo->SubnetMask) &&
+ !NetIp4IsUnicast (EFI_NTOHL (((EFI_IP4_RECEIVE_DATA *) RxData)->Header->SourceAddress), IpIo->SubnetMask)) {
+ //
+ // The source address doesn't match StationIp and it's not a unicast IP address, discard it.
+ //
+ goto CleanUp;
+ }
+
+ if (RxData->Ip4RxData.DataLength == 0) {
+ //
+ // Discard zero length data payload packet.
+ //
+ goto CleanUp;
+ }
+
+ //
+ // The fragment should always be valid for non-zero length packet.
+ //
+ ASSERT (RxData->Ip4RxData.FragmentCount != 0);
+
+ //
+ // Create a netbuffer representing IPv4 packet
+ //
+ Pkt = NetbufFromExt (
+ (NET_FRAGMENT *) RxData->Ip4RxData.FragmentTable,
+ RxData->Ip4RxData.FragmentCount,
+ 0,
+ 0,
+ IpIoExtFree,
+ RxData->Ip4RxData.RecycleSignal
+ );
+ if (NULL == Pkt) {
+ goto CleanUp;
+ }
+
+ //
+ // Create a net session
+ //
+ Session.Source.Addr[0] = EFI_IP4 (RxData->Ip4RxData.Header->SourceAddress);
+ Session.Dest.Addr[0] = EFI_IP4 (RxData->Ip4RxData.Header->DestinationAddress);
+ Session.IpHdr.Ip4Hdr = RxData->Ip4RxData.Header;
+ Session.IpHdrLen = RxData->Ip4RxData.HeaderLength;
+ Session.IpVersion = IP_VERSION_4;
+ } else {
+ ASSERT (RxData->Ip6RxData.Header != NULL);
+ if (!NetIp6IsValidUnicast(&RxData->Ip6RxData.Header->SourceAddress)) {
+ goto CleanUp;
+ }
+
+ if (RxData->Ip6RxData.DataLength == 0) {
+ //
+ // Discard zero length data payload packet.
+ //
+ goto CleanUp;
+ }
+
+ //
+ // The fragment should always be valid for non-zero length packet.
+ //
+ ASSERT (RxData->Ip6RxData.FragmentCount != 0);
+
+ //
+ // Create a netbuffer representing IPv6 packet
+ //
+ Pkt = NetbufFromExt (
+ (NET_FRAGMENT *) RxData->Ip6RxData.FragmentTable,
+ RxData->Ip6RxData.FragmentCount,
+ 0,
+ 0,
+ IpIoExtFree,
+ RxData->Ip6RxData.RecycleSignal
+ );
+ if (NULL == Pkt) {
+ goto CleanUp;
+ }
+
+ //
+ // Create a net session
+ //
+ CopyMem (
+ &Session.Source,
+ &RxData->Ip6RxData.Header->SourceAddress,
+ sizeof(EFI_IPv6_ADDRESS)
+ );
+ CopyMem (
+ &Session.Dest,
+ &RxData->Ip6RxData.Header->DestinationAddress,
+ sizeof(EFI_IPv6_ADDRESS)
+ );
+ Session.IpHdr.Ip6Hdr = RxData->Ip6RxData.Header;
+ Session.IpHdrLen = RxData->Ip6RxData.HeaderLength;
+ Session.IpVersion = IP_VERSION_6;
+ }
+
+ if (EFI_SUCCESS == Status) {
+
+ IpIo->PktRcvdNotify (EFI_SUCCESS, 0, &Session, Pkt, IpIo->RcvdContext);
+ } else {
+ //
+ // Status is EFI_ICMP_ERROR
+ //
+ Status = IpIoIcmpHandler (IpIo, Pkt, &Session);
+ if (EFI_ERROR (Status)) {
+ NetbufFree (Pkt);
+ }
+ }
+
+ goto Resume;
+
+CleanUp:
+
+ if (IpIo->IpVersion == IP_VERSION_4){
+ gBS->SignalEvent (RxData->Ip4RxData.RecycleSignal);
+ } else {
+ gBS->SignalEvent (RxData->Ip6RxData.RecycleSignal);
+ }
+
+Resume:
+
+ if (IpIo->IpVersion == IP_VERSION_4){
+ IpIo->Ip.Ip4->Receive (IpIo->Ip.Ip4, &(IpIo->RcvToken.Ip4Token));
+ } else {
+ IpIo->Ip.Ip6->Receive (IpIo->Ip.Ip6, &(IpIo->RcvToken.Ip6Token));
+ }
+}
+
+/**
+ This function add IpIoListenHandlerDpc to the end of the DPC queue.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The context passed in by the event notifier.
+
+**/
+VOID
+EFIAPI
+IpIoListenHandler (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request IpIoListenHandlerDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, IpIoListenHandlerDpc, Context);
+}
+
+
+/**
+ Create a new IP_IO instance.
+
+ If IpVersion is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().
+
+ This function uses IP4/IP6 service binding protocol in Controller to create
+ an IP4/IP6 child (aka IP4/IP6 instance).
+
+ @param[in] Image The image handle of the driver or application that
+ consumes IP_IO.
+ @param[in] Controller The controller handle that has IP4 or IP6 service
+ binding protocol installed.
+ @param[in] IpVersion The version of the IP protocol to use, either
+ IPv4 or IPv6.
+
+ @return Pointer to a newly created IP_IO instance, or NULL if failed.
+
+**/
+IP_IO *
+EFIAPI
+IpIoCreate (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN UINT8 IpVersion
+ )
+{
+ EFI_STATUS Status;
+ IP_IO *IpIo;
+ EFI_EVENT Event;
+
+ ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6));
+
+ IpIo = AllocateZeroPool (sizeof (IP_IO));
+ if (NULL == IpIo) {
+ return NULL;
+ }
+
+ InitializeListHead (&(IpIo->PendingSndList));
+ InitializeListHead (&(IpIo->IpList));
+ IpIo->Controller = Controller;
+ IpIo->Image = Image;
+ IpIo->IpVersion = IpVersion;
+ Event = NULL;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ IpIoListenHandler,
+ IpIo,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ReleaseIpIo;
+ }
+
+ if (IpVersion == IP_VERSION_4) {
+ IpIo->RcvToken.Ip4Token.Event = Event;
+ } else {
+ IpIo->RcvToken.Ip6Token.Event = Event;
+ }
+
+ //
+ // Create an IP child and open IP protocol
+ //
+ Status = IpIoCreateIpChildOpenProtocol (
+ Controller,
+ Image,
+ &IpIo->ChildHandle,
+ IpVersion,
+ (VOID **) & (IpIo->Ip)
+ );
+ if (EFI_ERROR (Status)) {
+ goto ReleaseIpIo;
+ }
+
+ return IpIo;
+
+ReleaseIpIo:
+
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ gBS->FreePool (IpIo);
+
+ return NULL;
+}
+
+
+/**
+ Open an IP_IO instance for use.
+
+ If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().
+
+ This function is called after IpIoCreate(). It is used for configuring the IP
+ instance and register the callbacks and their context data for sending and
+ receiving IP packets.
+
+ @param[in, out] IpIo Pointer to an IP_IO instance that needs
+ to open.
+ @param[in] OpenData The configuration data and callbacks for
+ the IP_IO instance.
+
+ @retval EFI_SUCCESS The IP_IO instance opened with OpenData
+ successfully.
+ @retval EFI_ACCESS_DENIED The IP_IO instance is configured, avoid to
+ reopen it.
+ @retval EFI_UNSUPPORTED IPv4 RawData mode is no supported.
+ @retval EFI_INVALID_PARAMETER Invalid input parameter.
+ @retval Others Error condition occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+IpIoOpen (
+ IN OUT IP_IO *IpIo,
+ IN IP_IO_OPEN_DATA *OpenData
+ )
+{
+ EFI_STATUS Status;
+ UINT8 IpVersion;
+
+ if (IpIo == NULL || OpenData == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (IpIo->IsConfigured) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ IpVersion = IpIo->IpVersion;
+
+ ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6));
+
+ //
+ // configure ip
+ //
+ if (IpVersion == IP_VERSION_4){
+ //
+ // RawData mode is no supported.
+ //
+ ASSERT (!OpenData->IpConfigData.Ip4CfgData.RawData);
+ if (OpenData->IpConfigData.Ip4CfgData.RawData) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (!OpenData->IpConfigData.Ip4CfgData.UseDefaultAddress) {
+ IpIo->StationIp = EFI_NTOHL (OpenData->IpConfigData.Ip4CfgData.StationAddress);
+ IpIo->SubnetMask = EFI_NTOHL (OpenData->IpConfigData.Ip4CfgData.SubnetMask);
+ }
+
+ Status = IpIo->Ip.Ip4->Configure (
+ IpIo->Ip.Ip4,
+ &OpenData->IpConfigData.Ip4CfgData
+ );
+ } else {
+
+ Status = IpIo->Ip.Ip6->Configure (
+ IpIo->Ip.Ip6,
+ &OpenData->IpConfigData.Ip6CfgData
+ );
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // @bug To delete the default route entry in this Ip, if it is:
+ // @bug (0.0.0.0, 0.0.0.0, 0.0.0.0). Delete this statement if Ip modified
+ // @bug its code
+ //
+ if (IpVersion == IP_VERSION_4){
+ Status = IpIo->Ip.Ip4->Routes (
+ IpIo->Ip.Ip4,
+ TRUE,
+ &mZeroIp4Addr,
+ &mZeroIp4Addr,
+ &mZeroIp4Addr
+ );
+
+ if (EFI_ERROR (Status) && (EFI_NOT_FOUND != Status)) {
+ return Status;
+ }
+ }
+
+ IpIo->PktRcvdNotify = OpenData->PktRcvdNotify;
+ IpIo->PktSentNotify = OpenData->PktSentNotify;
+
+ IpIo->RcvdContext = OpenData->RcvdContext;
+ IpIo->SndContext = OpenData->SndContext;
+
+ if (IpVersion == IP_VERSION_4){
+ IpIo->Protocol = OpenData->IpConfigData.Ip4CfgData.DefaultProtocol;
+
+ //
+ // start to listen incoming packet
+ //
+ Status = IpIo->Ip.Ip4->Receive (
+ IpIo->Ip.Ip4,
+ &(IpIo->RcvToken.Ip4Token)
+ );
+ if (EFI_ERROR (Status)) {
+ IpIo->Ip.Ip4->Configure (IpIo->Ip.Ip4, NULL);
+ return Status;
+ }
+
+ } else {
+
+ IpIo->Protocol = OpenData->IpConfigData.Ip6CfgData.DefaultProtocol;
+ Status = IpIo->Ip.Ip6->Receive (
+ IpIo->Ip.Ip6,
+ &(IpIo->RcvToken.Ip6Token)
+ );
+ if (EFI_ERROR (Status)) {
+ IpIo->Ip.Ip6->Configure (IpIo->Ip.Ip6, NULL);
+ return Status;
+ }
+ }
+
+ IpIo->IsConfigured = TRUE;
+ InsertTailList (&mActiveIpIoList, &IpIo->Entry);
+
+ return Status;
+}
+
+
+/**
+ Stop an IP_IO instance.
+
+ If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().
+
+ This function is paired with IpIoOpen(). The IP_IO will be unconfigured and all
+ the pending send/receive tokens will be canceled.
+
+ @param[in, out] IpIo Pointer to the IP_IO instance that needs to stop.
+
+ @retval EFI_SUCCESS The IP_IO instance stopped successfully.
+ @retval EFI_INVALID_PARAMETER Invalid input parameter.
+ @retval Others Error condition occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+IpIoStop (
+ IN OUT IP_IO *IpIo
+ )
+{
+ EFI_STATUS Status;
+ IP_IO_IP_INFO *IpInfo;
+ UINT8 IpVersion;
+
+ if (IpIo == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IpIo->IsConfigured) {
+ return EFI_SUCCESS;
+ }
+
+ IpVersion = IpIo->IpVersion;
+
+ ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6));
+
+ //
+ // Remove the IpIo from the active IpIo list.
+ //
+ RemoveEntryList (&IpIo->Entry);
+
+ //
+ // Configure NULL Ip
+ //
+ if (IpVersion == IP_VERSION_4) {
+ Status = IpIo->Ip.Ip4->Configure (IpIo->Ip.Ip4, NULL);
+ } else {
+ Status = IpIo->Ip.Ip6->Configure (IpIo->Ip.Ip6, NULL);
+ }
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ IpIo->IsConfigured = FALSE;
+
+ //
+ // Detroy the Ip List used by IpIo
+ //
+
+ while (!IsListEmpty (&(IpIo->IpList))) {
+ IpInfo = NET_LIST_HEAD (&(IpIo->IpList), IP_IO_IP_INFO, Entry);
+
+ IpIoRemoveIp (IpIo, IpInfo);
+ }
+
+ //
+ // All pending send tokens should be flushed by resetting the IP instances.
+ //
+ ASSERT (IsListEmpty (&IpIo->PendingSndList));
+
+ //
+ // Close the receive event.
+ //
+ if (IpVersion == IP_VERSION_4){
+ gBS->CloseEvent (IpIo->RcvToken.Ip4Token.Event);
+ } else {
+ gBS->CloseEvent (IpIo->RcvToken.Ip6Token.Event);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Destroy an IP_IO instance.
+
+ This function is paired with IpIoCreate(). The IP_IO will be closed first.
+ Resource will be freed afterwards. See IpIoCloseProtocolDestroyIpChild().
+
+ @param[in, out] IpIo Pointer to the IP_IO instance that needs to be
+ destroyed.
+
+ @retval EFI_SUCCESS The IP_IO instance destroyed successfully.
+ @retval Others Error condition occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+IpIoDestroy (
+ IN OUT IP_IO *IpIo
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Stop the IpIo.
+ //
+ Status = IpIoStop (IpIo);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Close the IP protocol and destroy the child.
+ //
+ Status = IpIoCloseProtocolDestroyIpChild (
+ IpIo->Controller,
+ IpIo->Image,
+ IpIo->ChildHandle,
+ IpIo->IpVersion
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ gBS->FreePool (IpIo);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Send out an IP packet.
+
+ This function is called after IpIoOpen(). The data to be sent is wrapped in
+ Pkt. The IP instance wrapped in IpIo is used for sending by default but can be
+ overriden by Sender. Other sending configs, like source address and gateway
+ address etc., are specified in OverrideData.
+
+ @param[in, out] IpIo Pointer to an IP_IO instance used for sending IP
+ packet.
+ @param[in, out] Pkt Pointer to the IP packet to be sent.
+ @param[in] Sender The IP protocol instance used for sending.
+ @param[in] Context Optional context data.
+ @param[in] NotifyData Optional notify data.
+ @param[in] Dest The destination IP address to send this packet to.
+ This parameter is optional when using IPv6.
+ @param[in] OverrideData The data to override some configuration of the IP
+ instance used for sending.
+
+ @retval EFI_SUCCESS The operation is completed successfully.
+ @retval EFI_INVALID_PARAMETER The input parameter is not correct.
+ @retval EFI_NOT_STARTED The IpIo is not configured.
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limit.
+ @retval Others Error condition occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+IpIoSend (
+ IN OUT IP_IO *IpIo,
+ IN OUT NET_BUF *Pkt,
+ IN IP_IO_IP_INFO *Sender OPTIONAL,
+ IN VOID *Context OPTIONAL,
+ IN VOID *NotifyData OPTIONAL,
+ IN EFI_IP_ADDRESS *Dest OPTIONAL,
+ IN IP_IO_OVERRIDE *OverrideData OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ IP_IO_IP_PROTOCOL Ip;
+ IP_IO_SEND_ENTRY *SndEntry;
+
+ if ((IpIo == NULL) || (Pkt == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((IpIo->IpVersion == IP_VERSION_4) && (Dest == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IpIo->IsConfigured) {
+ return EFI_NOT_STARTED;
+ }
+
+ Ip = (NULL == Sender) ? IpIo->Ip : Sender->Ip;
+
+ //
+ // create a new SndEntry
+ //
+ SndEntry = IpIoCreateSndEntry (IpIo, Pkt, Ip, Context, NotifyData, Dest, OverrideData);
+ if (NULL == SndEntry) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Send this Packet
+ //
+ if (IpIo->IpVersion == IP_VERSION_4){
+ Status = Ip.Ip4->Transmit (
+ Ip.Ip4,
+ &SndEntry->SndToken.Ip4Token
+ );
+ } else {
+ Status = Ip.Ip6->Transmit (
+ Ip.Ip6,
+ &SndEntry->SndToken.Ip6Token
+ );
+ }
+
+ if (EFI_ERROR (Status)) {
+ IpIoDestroySndEntry (SndEntry);
+ }
+
+ return Status;
+}
+
+
+/**
+ Cancel the IP transmit token which wraps this Packet.
+
+ If IpIo is NULL, then ASSERT().
+ If Packet is NULL, then ASSERT().
+
+ @param[in] IpIo Pointer to the IP_IO instance.
+ @param[in] Packet Pointer to the packet of NET_BUF to cancel.
+
+**/
+VOID
+EFIAPI
+IpIoCancelTxToken (
+ IN IP_IO *IpIo,
+ IN VOID *Packet
+ )
+{
+ LIST_ENTRY *Node;
+ IP_IO_SEND_ENTRY *SndEntry;
+ IP_IO_IP_PROTOCOL Ip;
+
+ ASSERT ((IpIo != NULL) && (Packet != NULL));
+
+ NET_LIST_FOR_EACH (Node, &IpIo->PendingSndList) {
+
+ SndEntry = NET_LIST_USER_STRUCT (Node, IP_IO_SEND_ENTRY, Entry);
+
+ if (SndEntry->Pkt == Packet) {
+
+ Ip = SndEntry->Ip;
+
+ if (IpIo->IpVersion == IP_VERSION_4) {
+ Ip.Ip4->Cancel (
+ Ip.Ip4,
+ &SndEntry->SndToken.Ip4Token
+ );
+ } else {
+ Ip.Ip6->Cancel (
+ Ip.Ip6,
+ &SndEntry->SndToken.Ip6Token
+ );
+ }
+
+ break;
+ }
+ }
+
+}
+
+
+/**
+ Add a new IP instance for sending data.
+
+ If IpIo is NULL, then ASSERT().
+ If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().
+
+ The function is used to add the IP_IO to the IP_IO sending list. The caller
+ can later use IpIoFindSender() to get the IP_IO and call IpIoSend() to send
+ data.
+
+ @param[in, out] IpIo Pointer to a IP_IO instance to add a new IP
+ instance for sending purpose.
+
+ @return Pointer to the created IP_IO_IP_INFO structure, NULL if failed.
+
+**/
+IP_IO_IP_INFO *
+EFIAPI
+IpIoAddIp (
+ IN OUT IP_IO *IpIo
+ )
+{
+ EFI_STATUS Status;
+ IP_IO_IP_INFO *IpInfo;
+ EFI_EVENT Event;
+
+ ASSERT (IpIo != NULL);
+ ASSERT ((IpIo->IpVersion == IP_VERSION_4) || (IpIo->IpVersion == IP_VERSION_6));
+
+ IpInfo = AllocatePool (sizeof (IP_IO_IP_INFO));
+ if (IpInfo == NULL) {
+ return NULL;
+ }
+
+ //
+ // Init this IpInfo, set the Addr and SubnetMask to 0 before we configure the IP
+ // instance.
+ //
+ InitializeListHead (&IpInfo->Entry);
+ IpInfo->ChildHandle = NULL;
+ ZeroMem (&IpInfo->Addr, sizeof (IpInfo->Addr));
+ ZeroMem (&IpInfo->PreMask, sizeof (IpInfo->PreMask));
+
+ IpInfo->RefCnt = 1;
+ IpInfo->IpVersion = IpIo->IpVersion;
+
+ //
+ // Create the IP instance and open the IP protocol.
+ //
+ Status = IpIoCreateIpChildOpenProtocol (
+ IpIo->Controller,
+ IpIo->Image,
+ &IpInfo->ChildHandle,
+ IpInfo->IpVersion,
+ (VOID **) &IpInfo->Ip
+ );
+ if (EFI_ERROR (Status)) {
+ goto ReleaseIpInfo;
+ }
+
+ //
+ // Create the event for the DummyRcvToken.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ IpIoDummyHandler,
+ IpInfo,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ReleaseIpChild;
+ }
+
+ if (IpInfo->IpVersion == IP_VERSION_4) {
+ IpInfo->DummyRcvToken.Ip4Token.Event = Event;
+ } else {
+ IpInfo->DummyRcvToken.Ip6Token.Event = Event;
+ }
+
+ //
+ // Link this IpInfo into the IpIo.
+ //
+ InsertTailList (&IpIo->IpList, &IpInfo->Entry);
+
+ return IpInfo;
+
+ReleaseIpChild:
+
+ IpIoCloseProtocolDestroyIpChild (
+ IpIo->Controller,
+ IpIo->Image,
+ IpInfo->ChildHandle,
+ IpInfo->IpVersion
+ );
+
+ReleaseIpInfo:
+
+ gBS->FreePool (IpInfo);
+
+ return NULL;
+}
+
+
+/**
+ Configure the IP instance of this IpInfo and start the receiving if IpConfigData
+ is not NULL.
+
+ If IpInfo is NULL, then ASSERT().
+ If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().
+
+ @param[in, out] IpInfo Pointer to the IP_IO_IP_INFO instance.
+ @param[in, out] IpConfigData The IP configure data used to configure the IP
+ instance, if NULL the IP instance is reset. If
+ UseDefaultAddress is set to TRUE, and the configure
+ operation succeeds, the default address information
+ is written back in this IpConfigData.
+
+ @retval EFI_SUCCESS The IP instance of this IpInfo is configured successfully
+ or no need to reconfigure it.
+ @retval Others Configuration fails.
+
+**/
+EFI_STATUS
+EFIAPI
+IpIoConfigIp (
+ IN OUT IP_IO_IP_INFO *IpInfo,
+ IN OUT VOID *IpConfigData OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ IP_IO_IP_PROTOCOL Ip;
+ UINT8 IpVersion;
+ EFI_IP4_MODE_DATA Ip4ModeData;
+ EFI_IP6_MODE_DATA Ip6ModeData;
+
+ ASSERT (IpInfo != NULL);
+
+ if (IpInfo->RefCnt > 1) {
+ //
+ // This IP instance is shared, don't reconfigure it until it has only one
+ // consumer. Currently, only the tcp children cloned from their passive parent
+ // will share the same IP. So this cases only happens while IpConfigData is NULL,
+ // let the last consumer clean the IP instance.
+ //
+ return EFI_SUCCESS;
+ }
+
+ IpVersion = IpInfo->IpVersion;
+ ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6));
+
+ Ip = IpInfo->Ip;
+
+ if (IpInfo->IpVersion == IP_VERSION_4) {
+ Status = Ip.Ip4->Configure (Ip.Ip4, IpConfigData);
+ } else {
+ Status = Ip.Ip6->Configure (Ip.Ip6, IpConfigData);
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (IpConfigData != NULL) {
+ if (IpInfo->IpVersion == IP_VERSION_4) {
+
+ if (((EFI_IP4_CONFIG_DATA *) IpConfigData)->UseDefaultAddress) {
+ Status = Ip.Ip4->GetModeData (
+ Ip.Ip4,
+ &Ip4ModeData,
+ NULL,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ Ip.Ip4->Configure (Ip.Ip4, NULL);
+ return Status;
+ }
+
+ IP4_COPY_ADDRESS (&((EFI_IP4_CONFIG_DATA*) IpConfigData)->StationAddress, &Ip4ModeData.ConfigData.StationAddress);
+ IP4_COPY_ADDRESS (&((EFI_IP4_CONFIG_DATA*) IpConfigData)->SubnetMask, &Ip4ModeData.ConfigData.SubnetMask);
+ }
+
+ CopyMem (
+ &IpInfo->Addr.Addr,
+ &((EFI_IP4_CONFIG_DATA *) IpConfigData)->StationAddress,
+ sizeof (IP4_ADDR)
+ );
+ CopyMem (
+ &IpInfo->PreMask.SubnetMask,
+ &((EFI_IP4_CONFIG_DATA *) IpConfigData)->SubnetMask,
+ sizeof (IP4_ADDR)
+ );
+
+ Status = Ip.Ip4->Receive (
+ Ip.Ip4,
+ &IpInfo->DummyRcvToken.Ip4Token
+ );
+ if (EFI_ERROR (Status)) {
+ Ip.Ip4->Configure (Ip.Ip4, NULL);
+ }
+ } else {
+ Status = Ip.Ip6->GetModeData (
+ Ip.Ip6,
+ &Ip6ModeData,
+ NULL,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ Ip.Ip6->Configure (Ip.Ip6, NULL);
+ return Status;
+ }
+
+ if (Ip6ModeData.IsConfigured) {
+ CopyMem (
+ &((EFI_IP6_CONFIG_DATA *) IpConfigData)->StationAddress,
+ &Ip6ModeData.ConfigData.StationAddress,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+
+ if (Ip6ModeData.AddressList != NULL) {
+ FreePool (Ip6ModeData.AddressList);
+ }
+
+ if (Ip6ModeData.GroupTable != NULL) {
+ FreePool (Ip6ModeData.GroupTable);
+ }
+
+ if (Ip6ModeData.RouteTable != NULL) {
+ FreePool (Ip6ModeData.RouteTable);
+ }
+
+ if (Ip6ModeData.NeighborCache != NULL) {
+ FreePool (Ip6ModeData.NeighborCache);
+ }
+
+ if (Ip6ModeData.PrefixTable != NULL) {
+ FreePool (Ip6ModeData.PrefixTable);
+ }
+
+ if (Ip6ModeData.IcmpTypeList != NULL) {
+ FreePool (Ip6ModeData.IcmpTypeList);
+ }
+
+ } else {
+ Status = EFI_NO_MAPPING;
+ return Status;
+ }
+
+ CopyMem (
+ &IpInfo->Addr,
+ &Ip6ModeData.ConfigData.StationAddress,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+
+ Status = Ip.Ip6->Receive (
+ Ip.Ip6,
+ &IpInfo->DummyRcvToken.Ip6Token
+ );
+ if (EFI_ERROR (Status)) {
+ Ip.Ip6->Configure (Ip.Ip6, NULL);
+ }
+ }
+ } else {
+ //
+ // The IP instance is reset, set the stored Addr and SubnetMask to zero.
+ //
+ ZeroMem (&IpInfo->Addr, sizeof (IpInfo->Addr));
+ ZeroMem (&IpInfo->PreMask, sizeof (IpInfo->PreMask));
+ }
+
+ return Status;
+}
+
+
+/**
+ Destroy an IP instance maintained in IpIo->IpList for
+ sending purpose.
+
+ If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().
+
+ This function pairs with IpIoAddIp(). The IpInfo is previously created by
+ IpIoAddIp(). The IP_IO_IP_INFO::RefCnt is decremented and the IP instance
+ will be dstroyed if the RefCnt is zero.
+
+ @param[in] IpIo Pointer to the IP_IO instance.
+ @param[in] IpInfo Pointer to the IpInfo to be removed.
+
+**/
+VOID
+EFIAPI
+IpIoRemoveIp (
+ IN IP_IO *IpIo,
+ IN IP_IO_IP_INFO *IpInfo
+ )
+{
+
+ UINT8 IpVersion;
+
+ if (IpIo == NULL || IpInfo == NULL) {
+ return;
+ }
+
+ ASSERT (IpInfo->RefCnt > 0);
+
+ NET_PUT_REF (IpInfo);
+
+ if (IpInfo->RefCnt > 0) {
+
+ return;
+ }
+
+ IpVersion = IpIo->IpVersion;
+
+ ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6));
+
+ RemoveEntryList (&IpInfo->Entry);
+
+ if (IpVersion == IP_VERSION_4){
+ IpInfo->Ip.Ip4->Configure (
+ IpInfo->Ip.Ip4,
+ NULL
+ );
+ IpIoCloseProtocolDestroyIpChild (
+ IpIo->Controller,
+ IpIo->Image,
+ IpInfo->ChildHandle,
+ IP_VERSION_4
+ );
+
+ gBS->CloseEvent (IpInfo->DummyRcvToken.Ip4Token.Event);
+
+ } else {
+
+ IpInfo->Ip.Ip6->Configure (
+ IpInfo->Ip.Ip6,
+ NULL
+ );
+
+ IpIoCloseProtocolDestroyIpChild (
+ IpIo->Controller,
+ IpIo->Image,
+ IpInfo->ChildHandle,
+ IP_VERSION_6
+ );
+
+ gBS->CloseEvent (IpInfo->DummyRcvToken.Ip6Token.Event);
+ }
+
+ FreePool (IpInfo);
+}
+
+
+/**
+ Find the first IP protocol maintained in IpIo whose local
+ address is the same as Src.
+
+ This function is called when the caller needs the IpIo to send data to the
+ specified Src. The IpIo was added previously by IpIoAddIp().
+
+ @param[in, out] IpIo Pointer to the pointer of the IP_IO instance.
+ @param[in] IpVersion The version of the IP protocol to use, either
+ IPv4 or IPv6.
+ @param[in] Src The local IP address.
+
+ @return Pointer to the IP protocol can be used for sending purpose and its local
+ address is the same with Src. NULL if failed.
+
+**/
+IP_IO_IP_INFO *
+EFIAPI
+IpIoFindSender (
+ IN OUT IP_IO **IpIo,
+ IN UINT8 IpVersion,
+ IN EFI_IP_ADDRESS *Src
+ )
+{
+ LIST_ENTRY *IpIoEntry;
+ IP_IO *IpIoPtr;
+ LIST_ENTRY *IpInfoEntry;
+ IP_IO_IP_INFO *IpInfo;
+
+ if (IpIo == NULL || Src == NULL) {
+ return NULL;
+ }
+
+ if ((IpVersion != IP_VERSION_4) && (IpVersion != IP_VERSION_6)) {
+ return NULL;
+ }
+
+ NET_LIST_FOR_EACH (IpIoEntry, &mActiveIpIoList) {
+ IpIoPtr = NET_LIST_USER_STRUCT (IpIoEntry, IP_IO, Entry);
+
+ if (((*IpIo != NULL) && (*IpIo != IpIoPtr)) || (IpIoPtr->IpVersion != IpVersion)) {
+ continue;
+ }
+
+ NET_LIST_FOR_EACH (IpInfoEntry, &IpIoPtr->IpList) {
+ IpInfo = NET_LIST_USER_STRUCT (IpInfoEntry, IP_IO_IP_INFO, Entry);
+ if (IpInfo->IpVersion == IP_VERSION_4){
+
+ if (EFI_IP4_EQUAL (&IpInfo->Addr.v4, &Src->v4)) {
+ *IpIo = IpIoPtr;
+ return IpInfo;
+ }
+
+ } else {
+
+ if (EFI_IP6_EQUAL (&IpInfo->Addr.v6, &Src->v6)) {
+ *IpIo = IpIoPtr;
+ return IpInfo;
+ }
+ }
+ }
+ }
+
+ //
+ // No match.
+ //
+ return NULL;
+}
+
+
+/**
+ Get the ICMP error map information.
+
+ The ErrorStatus will be returned. The IsHard and Notify are optional. If they
+ are not NULL, this routine will fill them.
+
+ @param[in] IcmpError IcmpError Type.
+ @param[in] IpVersion The version of the IP protocol to use,
+ either IPv4 or IPv6.
+ @param[out] IsHard If TRUE, indicates that it is a hard error.
+ @param[out] Notify If TRUE, SockError needs to be notified.
+
+ @retval EFI_UNSUPPORTED Unrecognizable ICMP error code.
+ @return ICMP Error Status, such as EFI_NETWORK_UNREACHABLE.
+
+**/
+EFI_STATUS
+EFIAPI
+IpIoGetIcmpErrStatus (
+ IN UINT8 IcmpError,
+ IN UINT8 IpVersion,
+ OUT BOOLEAN *IsHard OPTIONAL,
+ OUT BOOLEAN *Notify OPTIONAL
+ )
+{
+ if (IpVersion == IP_VERSION_4 ) {
+ ASSERT (IcmpError <= ICMP_ERR_PARAMPROB);
+
+ if (IsHard != NULL) {
+ *IsHard = mIcmpErrMap[IcmpError].IsHard;
+ }
+
+ if (Notify != NULL) {
+ *Notify = mIcmpErrMap[IcmpError].Notify;
+ }
+
+ switch (IcmpError) {
+ case ICMP_ERR_UNREACH_NET:
+ return EFI_NETWORK_UNREACHABLE;
+
+ case ICMP_ERR_TIMXCEED_INTRANS:
+ case ICMP_ERR_TIMXCEED_REASS:
+ case ICMP_ERR_UNREACH_HOST:
+ return EFI_HOST_UNREACHABLE;
+
+ case ICMP_ERR_UNREACH_PROTOCOL:
+ return EFI_PROTOCOL_UNREACHABLE;
+
+ case ICMP_ERR_UNREACH_PORT:
+ return EFI_PORT_UNREACHABLE;
+
+ case ICMP_ERR_MSGSIZE:
+ case ICMP_ERR_UNREACH_SRCFAIL:
+ case ICMP_ERR_QUENCH:
+ case ICMP_ERR_PARAMPROB:
+ return EFI_ICMP_ERROR;
+
+ default:
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+ }
+
+ } else if (IpVersion == IP_VERSION_6) {
+
+ ASSERT (IcmpError <= ICMP6_ERR_PARAMPROB_IPV6OPTION);
+
+ if (IsHard != NULL) {
+ *IsHard = mIcmp6ErrMap[IcmpError].IsHard;
+ }
+
+ if (Notify != NULL) {
+ *Notify = mIcmp6ErrMap[IcmpError].Notify;
+ }
+
+ switch (IcmpError) {
+ case ICMP6_ERR_UNREACH_NET:
+ return EFI_NETWORK_UNREACHABLE;
+
+ case ICMP6_ERR_UNREACH_HOST:
+ case ICMP6_ERR_TIMXCEED_HOPLIMIT:
+ case ICMP6_ERR_TIMXCEED_REASS:
+ return EFI_HOST_UNREACHABLE;
+
+ case ICMP6_ERR_UNREACH_PROTOCOL:
+ return EFI_PROTOCOL_UNREACHABLE;
+
+ case ICMP6_ERR_UNREACH_PORT:
+ return EFI_PORT_UNREACHABLE;
+
+ case ICMP6_ERR_PACKAGE_TOOBIG:
+ case ICMP6_ERR_PARAMPROB_HEADER:
+ case ICMP6_ERR_PARAMPROB_NEXHEADER:
+ case ICMP6_ERR_PARAMPROB_IPV6OPTION:
+ return EFI_ICMP_ERROR;
+
+ default:
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+ }
+
+ } else {
+ //
+ // Should never be here
+ //
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+ }
+}
+
+
+/**
+ Refresh the remote peer's Neighbor Cache entries.
+
+ This function is called when the caller needs the IpIo to refresh the existing
+ IPv6 neighbor cache entries since the neighbor is considered reachable by the
+ node has recently received a confirmation that packets sent recently to the
+ neighbor were received by its IP layer.
+
+ @param[in] IpIo Pointer to an IP_IO instance
+ @param[in] Neighbor The IP address of the neighbor
+ @param[in] Timeout Time in 100-ns units that this entry will
+ remain in the neighbor cache. A value of
+ zero means that the entry is permanent.
+ A value of non-zero means that the entry is
+ dynamic and will be deleted after Timeout.
+
+ @retval EFI_SUCCESS The operation is completed successfully.
+ @retval EFI_NOT_STARTED The IpIo is not configured.
+ @retval EFI_INVALID_PARAMETER Neighbor Address is invalid.
+ @retval EFI_NOT_FOUND The neighbor cache entry is not in the
+ neighbor table.
+ @retval EFI_UNSUPPORTED IP version is IPv4, which doesn't support neighbor cache refresh.
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limit.
+
+**/
+EFI_STATUS
+EFIAPI
+IpIoRefreshNeighbor (
+ IN IP_IO *IpIo,
+ IN EFI_IP_ADDRESS *Neighbor,
+ IN UINT32 Timeout
+ )
+{
+ EFI_IP6_PROTOCOL *Ip;
+
+ if (!IpIo->IsConfigured) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (IpIo->IpVersion != IP_VERSION_6) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Ip = IpIo->Ip.Ip6;
+
+ return Ip->Neighbors (Ip, FALSE, &Neighbor->v6, NULL, Timeout, TRUE);
+}
+
diff --git a/NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.inf b/NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.inf
new file mode 100644
index 0000000000..a789d80de6
--- /dev/null
+++ b/NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.inf
@@ -0,0 +1,46 @@
+## @file
+# This library instance provides IP services upon EFI IPv4/IPv6 Protocols.
+#
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxeIpIoLib
+ MODULE_UNI_FILE = DxeIpIoLib.uni
+ FILE_GUID = A302F877-8625-425c-B1EC-7487B62C4FDA
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = IpIoLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ DxeIpIoLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ BaseMemoryLib
+ DpcLib
+
+[Protocols]
+ gEfiIp4ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiIp4ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiIp6ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiIp6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+
diff --git a/NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.uni b/NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.uni
new file mode 100644
index 0000000000..4af043cfe9
--- /dev/null
+++ b/NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.uni
@@ -0,0 +1,16 @@
+// /** @file
+// This library instance provides IP services upon EFI IPv4/IPv6 Protocols.
+//
+// This library instance provides IP services upon EFI IPv4/IPv6 Protocols.
+//
+// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "EFI IPv4/IPv6 IP Services Library"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This library instance provides IP services upon EFI IPv4/IPv6 Protocols."
+
diff --git a/NetworkPkg/Library/DxeNetLib/DxeNetLib.c b/NetworkPkg/Library/DxeNetLib/DxeNetLib.c
new file mode 100644
index 0000000000..8e2f720666
--- /dev/null
+++ b/NetworkPkg/Library/DxeNetLib/DxeNetLib.c
@@ -0,0 +1,3394 @@
+/** @file
+ Network library.
+
+Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <IndustryStandard/SmBios.h>
+
+#include <Protocol/DriverBinding.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/AdapterInformation.h>
+#include <Protocol/ManagedNetwork.h>
+#include <Protocol/Ip4Config2.h>
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+
+#include <Guid/SmBios.h>
+
+#include <Library/NetLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiLib.h>
+
+#define NIC_ITEM_CONFIG_SIZE (sizeof (NIC_IP4_CONFIG_INFO) + sizeof (EFI_IP4_ROUTE_TABLE) * MAX_IP4_CONFIG_IN_VARIABLE)
+#define DEFAULT_ZERO_START ((UINTN) ~0)
+
+//
+// All the supported IP4 maskes in host byte order.
+//
+GLOBAL_REMOVE_IF_UNREFERENCED IP4_ADDR gIp4AllMasks[IP4_MASK_NUM] = {
+ 0x00000000,
+ 0x80000000,
+ 0xC0000000,
+ 0xE0000000,
+ 0xF0000000,
+ 0xF8000000,
+ 0xFC000000,
+ 0xFE000000,
+
+ 0xFF000000,
+ 0xFF800000,
+ 0xFFC00000,
+ 0xFFE00000,
+ 0xFFF00000,
+ 0xFFF80000,
+ 0xFFFC0000,
+ 0xFFFE0000,
+
+ 0xFFFF0000,
+ 0xFFFF8000,
+ 0xFFFFC000,
+ 0xFFFFE000,
+ 0xFFFFF000,
+ 0xFFFFF800,
+ 0xFFFFFC00,
+ 0xFFFFFE00,
+
+ 0xFFFFFF00,
+ 0xFFFFFF80,
+ 0xFFFFFFC0,
+ 0xFFFFFFE0,
+ 0xFFFFFFF0,
+ 0xFFFFFFF8,
+ 0xFFFFFFFC,
+ 0xFFFFFFFE,
+ 0xFFFFFFFF,
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_IPv4_ADDRESS mZeroIp4Addr = {{0, 0, 0, 0}};
+
+//
+// Any error level digitally larger than mNetDebugLevelMax
+// will be silently discarded.
+//
+GLOBAL_REMOVE_IF_UNREFERENCED UINTN mNetDebugLevelMax = NETDEBUG_LEVEL_ERROR;
+GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mSyslogPacketSeq = 0xDEADBEEF;
+
+//
+// You can change mSyslogDstMac mSyslogDstIp and mSyslogSrcIp
+// here to direct the syslog packets to the syslog deamon. The
+// default is broadcast to both the ethernet and IP.
+//
+GLOBAL_REMOVE_IF_UNREFERENCED UINT8 mSyslogDstMac[NET_ETHER_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mSyslogDstIp = 0xffffffff;
+GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mSyslogSrcIp = 0;
+
+GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 *mMonthName[] = {
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec"
+};
+
+//
+// VLAN device path node template
+//
+GLOBAL_REMOVE_IF_UNREFERENCED VLAN_DEVICE_PATH mNetVlanDevicePathTemplate = {
+ {
+ MESSAGING_DEVICE_PATH,
+ MSG_VLAN_DP,
+ {
+ (UINT8) (sizeof (VLAN_DEVICE_PATH)),
+ (UINT8) ((sizeof (VLAN_DEVICE_PATH)) >> 8)
+ }
+ },
+ 0
+};
+
+/**
+ Locate the handles that support SNP, then open one of them
+ to send the syslog packets. The caller isn't required to close
+ the SNP after use because the SNP is opened by HandleProtocol.
+
+ @return The point to SNP if one is properly openned. Otherwise NULL
+
+**/
+EFI_SIMPLE_NETWORK_PROTOCOL *
+SyslogLocateSnp (
+ VOID
+ )
+{
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_STATUS Status;
+ EFI_HANDLE *Handles;
+ UINTN HandleCount;
+ UINTN Index;
+
+ //
+ // Locate the handles which has SNP installed.
+ //
+ Handles = NULL;
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiSimpleNetworkProtocolGuid,
+ NULL,
+ &HandleCount,
+ &Handles
+ );
+
+ if (EFI_ERROR (Status) || (HandleCount == 0)) {
+ return NULL;
+ }
+
+ //
+ // Try to open one of the ethernet SNP protocol to send packet
+ //
+ Snp = NULL;
+
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->HandleProtocol (
+ Handles[Index],
+ &gEfiSimpleNetworkProtocolGuid,
+ (VOID **) &Snp
+ );
+
+ if ((Status == EFI_SUCCESS) && (Snp != NULL) &&
+ (Snp->Mode->IfType == NET_IFTYPE_ETHERNET) &&
+ (Snp->Mode->MaxPacketSize >= NET_SYSLOG_PACKET_LEN)) {
+
+ break;
+ }
+
+ Snp = NULL;
+ }
+
+ FreePool (Handles);
+ return Snp;
+}
+
+/**
+ Transmit a syslog packet synchronously through SNP. The Packet
+ already has the ethernet header prepended. This function should
+ fill in the source MAC because it will try to locate a SNP each
+ time it is called to avoid the problem if SNP is unloaded.
+ This code snip is copied from MNP.
+ If Packet is NULL, then ASSERT().
+
+ @param[in] Packet The Syslog packet
+ @param[in] Length The length of the packet
+
+ @retval EFI_DEVICE_ERROR Failed to locate a usable SNP protocol
+ @retval EFI_TIMEOUT Timeout happened to send the packet.
+ @retval EFI_SUCCESS Packet is sent.
+
+**/
+EFI_STATUS
+SyslogSendPacket (
+ IN CHAR8 *Packet,
+ IN UINT32 Length
+ )
+{
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ ETHER_HEAD *Ether;
+ EFI_STATUS Status;
+ EFI_EVENT TimeoutEvent;
+ UINT8 *TxBuf;
+
+ ASSERT (Packet != NULL);
+
+ Snp = SyslogLocateSnp ();
+
+ if (Snp == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Ether = (ETHER_HEAD *) Packet;
+ CopyMem (Ether->SrcMac, Snp->Mode->CurrentAddress.Addr, NET_ETHER_ADDR_LEN);
+
+ //
+ // Start the timeout event.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_NOTIFY,
+ NULL,
+ NULL,
+ &TimeoutEvent
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ for (;;) {
+ //
+ // Transmit the packet through SNP.
+ //
+ Status = Snp->Transmit (Snp, 0, Length, Packet, NULL, NULL, NULL);
+
+ if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {
+ Status = EFI_DEVICE_ERROR;
+ break;
+ }
+
+ //
+ // If Status is EFI_SUCCESS, the packet is put in the transmit queue.
+ // if Status is EFI_NOT_READY, the transmit engine of the network
+ // interface is busy. Both need to sync SNP.
+ //
+ TxBuf = NULL;
+
+ do {
+ //
+ // Get the recycled transmit buffer status.
+ //
+ Snp->GetStatus (Snp, NULL, (VOID **) &TxBuf);
+
+ if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ Status = EFI_TIMEOUT;
+ break;
+ }
+
+ } while (TxBuf == NULL);
+
+ if ((Status == EFI_SUCCESS) || (Status == EFI_TIMEOUT)) {
+ break;
+ }
+
+ //
+ // Status is EFI_NOT_READY. Restart the timer event and
+ // call Snp->Transmit again.
+ //
+ gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT);
+ }
+
+ gBS->SetTimer (TimeoutEvent, TimerCancel, 0);
+
+ON_EXIT:
+ gBS->CloseEvent (TimeoutEvent);
+ return Status;
+}
+
+/**
+ Build a syslog packet, including the Ethernet/Ip/Udp headers
+ and user's message.
+
+ @param[in] Level Syslog severity level
+ @param[in] Module The module that generates the log
+ @param[in] File The file that contains the current log
+ @param[in] Line The line of code in the File that contains the current log
+ @param[in] Message The log message
+ @param[in] BufLen The lenght of the Buf
+ @param[out] Buf The buffer to put the packet data
+
+ @return The length of the syslog packet built, 0 represents no packet is built.
+
+**/
+UINT32
+SyslogBuildPacket (
+ IN UINT32 Level,
+ IN UINT8 *Module,
+ IN UINT8 *File,
+ IN UINT32 Line,
+ IN UINT8 *Message,
+ IN UINT32 BufLen,
+ OUT CHAR8 *Buf
+ )
+{
+ EFI_STATUS Status;
+ ETHER_HEAD *Ether;
+ IP4_HEAD *Ip4;
+ EFI_UDP_HEADER *Udp4;
+ EFI_TIME Time;
+ UINT32 Pri;
+ UINT32 Len;
+
+ //
+ // Fill in the Ethernet header. Leave alone the source MAC.
+ // SyslogSendPacket will fill in the address for us.
+ //
+ Ether = (ETHER_HEAD *) Buf;
+ CopyMem (Ether->DstMac, mSyslogDstMac, NET_ETHER_ADDR_LEN);
+ ZeroMem (Ether->SrcMac, NET_ETHER_ADDR_LEN);
+
+ Ether->EtherType = HTONS (0x0800); // IPv4 protocol
+
+ Buf += sizeof (ETHER_HEAD);
+ BufLen -= sizeof (ETHER_HEAD);
+
+ //
+ // Fill in the IP header
+ //
+ Ip4 = (IP4_HEAD *) Buf;
+ Ip4->HeadLen = 5;
+ Ip4->Ver = 4;
+ Ip4->Tos = 0;
+ Ip4->TotalLen = 0;
+ Ip4->Id = (UINT16) mSyslogPacketSeq;
+ Ip4->Fragment = 0;
+ Ip4->Ttl = 16;
+ Ip4->Protocol = 0x11;
+ Ip4->Checksum = 0;
+ Ip4->Src = mSyslogSrcIp;
+ Ip4->Dst = mSyslogDstIp;
+
+ Buf += sizeof (IP4_HEAD);
+ BufLen -= sizeof (IP4_HEAD);
+
+ //
+ // Fill in the UDP header, Udp checksum is optional. Leave it zero.
+ //
+ Udp4 = (EFI_UDP_HEADER *) Buf;
+ Udp4->SrcPort = HTONS (514);
+ Udp4->DstPort = HTONS (514);
+ Udp4->Length = 0;
+ Udp4->Checksum = 0;
+
+ Buf += sizeof (EFI_UDP_HEADER);
+ BufLen -= sizeof (EFI_UDP_HEADER);
+
+ //
+ // Build the syslog message body with <PRI> Timestamp machine module Message
+ //
+ Pri = ((NET_SYSLOG_FACILITY & 31) << 3) | (Level & 7);
+ Status = gRT->GetTime (&Time, NULL);
+ if (EFI_ERROR (Status)) {
+ return 0;
+ }
+
+ //
+ // Use %a to format the ASCII strings, %s to format UNICODE strings
+ //
+ Len = 0;
+ Len += (UINT32) AsciiSPrint (
+ Buf,
+ BufLen,
+ "<%d> %a %d %d:%d:%d ",
+ Pri,
+ mMonthName [Time.Month-1],
+ Time.Day,
+ Time.Hour,
+ Time.Minute,
+ Time.Second
+ );
+
+ Len += (UINT32) AsciiSPrint (
+ Buf + Len,
+ BufLen - Len,
+ "Tiano %a: %a (Line: %d File: %a)",
+ Module,
+ Message,
+ Line,
+ File
+ );
+ Len ++;
+
+ //
+ // OK, patch the IP length/checksum and UDP length fields.
+ //
+ Len += sizeof (EFI_UDP_HEADER);
+ Udp4->Length = HTONS ((UINT16) Len);
+
+ Len += sizeof (IP4_HEAD);
+ Ip4->TotalLen = HTONS ((UINT16) Len);
+ Ip4->Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Ip4, sizeof (IP4_HEAD)));
+
+ return Len + sizeof (ETHER_HEAD);
+}
+
+/**
+ Allocate a buffer, then format the message to it. This is a
+ help function for the NET_DEBUG_XXX macros. The PrintArg of
+ these macros treats the variable length print parameters as a
+ single parameter, and pass it to the NetDebugASPrint. For
+ example, NET_DEBUG_TRACE ("Tcp", ("State transit to %a\n", Name))
+ if extracted to:
+
+ NetDebugOutput (
+ NETDEBUG_LEVEL_TRACE,
+ "Tcp",
+ __FILE__,
+ __LINE__,
+ NetDebugASPrint ("State transit to %a\n", Name)
+ )
+
+ If Format is NULL, then ASSERT().
+
+ @param Format The ASCII format string.
+ @param ... The variable length parameter whose format is determined
+ by the Format string.
+
+ @return The buffer containing the formatted message,
+ or NULL if failed to allocate memory.
+
+**/
+CHAR8 *
+EFIAPI
+NetDebugASPrint (
+ IN CHAR8 *Format,
+ ...
+ )
+{
+ VA_LIST Marker;
+ CHAR8 *Buf;
+
+ ASSERT (Format != NULL);
+
+ Buf = (CHAR8 *) AllocatePool (NET_DEBUG_MSG_LEN);
+
+ if (Buf == NULL) {
+ return NULL;
+ }
+
+ VA_START (Marker, Format);
+ AsciiVSPrint (Buf, NET_DEBUG_MSG_LEN, Format, Marker);
+ VA_END (Marker);
+
+ return Buf;
+}
+
+/**
+ Builds an UDP4 syslog packet and send it using SNP.
+
+ This function will locate a instance of SNP then send the message through it.
+ Because it isn't open the SNP BY_DRIVER, apply caution when using it.
+
+ @param Level The severity level of the message.
+ @param Module The Moudle that generates the log.
+ @param File The file that contains the log.
+ @param Line The exact line that contains the log.
+ @param Message The user message to log.
+
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet.
+ @retval EFI_DEVICE_ERROR Device error occurs.
+ @retval EFI_SUCCESS The log is discard because that it is more verbose
+ than the mNetDebugLevelMax. Or, it has been sent out.
+**/
+EFI_STATUS
+EFIAPI
+NetDebugOutput (
+ IN UINT32 Level,
+ IN UINT8 *Module,
+ IN UINT8 *File,
+ IN UINT32 Line,
+ IN UINT8 *Message
+ )
+{
+ CHAR8 *Packet;
+ UINT32 Len;
+ EFI_STATUS Status;
+
+ //
+ // Check whether the message should be sent out
+ //
+ if (Message == NULL || File == NULL || Module == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Level > mNetDebugLevelMax) {
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ //
+ // Allocate a maxium of 1024 bytes, the caller should ensure
+ // that the message plus the ethernet/ip/udp header is shorter
+ // than this
+ //
+ Packet = (CHAR8 *) AllocatePool (NET_SYSLOG_PACKET_LEN);
+
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Build the message: Ethernet header + IP header + Udp Header + user data
+ //
+ Len = SyslogBuildPacket (
+ Level,
+ Module,
+ File,
+ Line,
+ Message,
+ NET_SYSLOG_PACKET_LEN,
+ Packet
+ );
+ if (Len == 0) {
+ Status = EFI_DEVICE_ERROR;
+ } else {
+ mSyslogPacketSeq++;
+ Status = SyslogSendPacket (Packet, Len);
+ }
+
+ FreePool (Packet);
+
+ON_EXIT:
+ FreePool (Message);
+ return Status;
+}
+/**
+ Return the length of the mask.
+
+ Return the length of the mask, the correct value is from 0 to 32.
+ If the mask is invalid, return the invalid length 33, which is IP4_MASK_NUM.
+ NetMask is in the host byte order.
+
+ @param[in] NetMask The netmask to get the length from.
+
+ @return The length of the netmask, IP4_MASK_NUM if the mask is invalid.
+
+**/
+INTN
+EFIAPI
+NetGetMaskLength (
+ IN IP4_ADDR NetMask
+ )
+{
+ INTN Index;
+
+ for (Index = 0; Index <= IP4_MASK_MAX; Index++) {
+ if (NetMask == gIp4AllMasks[Index]) {
+ break;
+ }
+ }
+
+ return Index;
+}
+
+
+
+/**
+ Return the class of the IP address, such as class A, B, C.
+ Addr is in host byte order.
+
+ [ATTENTION]
+ Classful addressing (IP class A/B/C) has been deprecated according to RFC4632.
+ Caller of this function could only check the returned value against
+ IP4_ADDR_CLASSD (multicast) or IP4_ADDR_CLASSE (reserved) now.
+
+ The address of class A starts with 0.
+ If the address belong to class A, return IP4_ADDR_CLASSA.
+ The address of class B starts with 10.
+ If the address belong to class B, return IP4_ADDR_CLASSB.
+ The address of class C starts with 110.
+ If the address belong to class C, return IP4_ADDR_CLASSC.
+ The address of class D starts with 1110.
+ If the address belong to class D, return IP4_ADDR_CLASSD.
+ The address of class E starts with 1111.
+ If the address belong to class E, return IP4_ADDR_CLASSE.
+
+
+ @param[in] Addr The address to get the class from.
+
+ @return IP address class, such as IP4_ADDR_CLASSA.
+
+**/
+INTN
+EFIAPI
+NetGetIpClass (
+ IN IP4_ADDR Addr
+ )
+{
+ UINT8 ByteOne;
+
+ ByteOne = (UINT8) (Addr >> 24);
+
+ if ((ByteOne & 0x80) == 0) {
+ return IP4_ADDR_CLASSA;
+
+ } else if ((ByteOne & 0xC0) == 0x80) {
+ return IP4_ADDR_CLASSB;
+
+ } else if ((ByteOne & 0xE0) == 0xC0) {
+ return IP4_ADDR_CLASSC;
+
+ } else if ((ByteOne & 0xF0) == 0xE0) {
+ return IP4_ADDR_CLASSD;
+
+ } else {
+ return IP4_ADDR_CLASSE;
+
+ }
+}
+
+
+/**
+ Check whether the IP is a valid unicast address according to
+ the netmask.
+
+ ASSERT if NetMask is zero.
+
+ If all bits of the host address of IP are 0 or 1, IP is also not a valid unicast address,
+ except when the originator is one of the endpoints of a point-to-point link with a 31-bit
+ mask (RFC3021), or a 32bit NetMask (all 0xFF) is used for special network environment (e.g.
+ PPP link).
+
+ @param[in] Ip The IP to check against.
+ @param[in] NetMask The mask of the IP.
+
+ @return TRUE if IP is a valid unicast address on the network, otherwise FALSE.
+
+**/
+BOOLEAN
+EFIAPI
+NetIp4IsUnicast (
+ IN IP4_ADDR Ip,
+ IN IP4_ADDR NetMask
+ )
+{
+ INTN MaskLength;
+
+ ASSERT (NetMask != 0);
+
+ if (Ip == 0 || IP4_IS_LOCAL_BROADCAST (Ip)) {
+ return FALSE;
+ }
+
+ MaskLength = NetGetMaskLength (NetMask);
+ ASSERT ((MaskLength >= 0) && (MaskLength <= IP4_MASK_NUM));
+ if (MaskLength < 31) {
+ if (((Ip &~NetMask) == ~NetMask) || ((Ip &~NetMask) == 0)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ Check whether the incoming IPv6 address is a valid unicast address.
+
+ ASSERT if Ip6 is NULL.
+
+ If the address is a multicast address has binary 0xFF at the start, it is not
+ a valid unicast address. If the address is unspecified ::, it is not a valid
+ unicast address to be assigned to any node. If the address is loopback address
+ ::1, it is also not a valid unicast address to be assigned to any physical
+ interface.
+
+ @param[in] Ip6 The IPv6 address to check against.
+
+ @return TRUE if Ip6 is a valid unicast address on the network, otherwise FALSE.
+
+**/
+BOOLEAN
+EFIAPI
+NetIp6IsValidUnicast (
+ IN EFI_IPv6_ADDRESS *Ip6
+ )
+{
+ UINT8 Byte;
+ UINT8 Index;
+
+ ASSERT (Ip6 != NULL);
+
+ if (Ip6->Addr[0] == 0xFF) {
+ return FALSE;
+ }
+
+ for (Index = 0; Index < 15; Index++) {
+ if (Ip6->Addr[Index] != 0) {
+ return TRUE;
+ }
+ }
+
+ Byte = Ip6->Addr[Index];
+
+ if (Byte == 0x0 || Byte == 0x1) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Check whether the incoming Ipv6 address is the unspecified address or not.
+
+ ASSERT if Ip6 is NULL.
+
+ @param[in] Ip6 - Ip6 address, in network order.
+
+ @retval TRUE - Yes, unspecified
+ @retval FALSE - No
+
+**/
+BOOLEAN
+EFIAPI
+NetIp6IsUnspecifiedAddr (
+ IN EFI_IPv6_ADDRESS *Ip6
+ )
+{
+ UINT8 Index;
+
+ ASSERT (Ip6 != NULL);
+
+ for (Index = 0; Index < 16; Index++) {
+ if (Ip6->Addr[Index] != 0) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ Check whether the incoming Ipv6 address is a link-local address.
+
+ ASSERT if Ip6 is NULL.
+
+ @param[in] Ip6 - Ip6 address, in network order.
+
+ @retval TRUE - Yes, link-local address
+ @retval FALSE - No
+
+**/
+BOOLEAN
+EFIAPI
+NetIp6IsLinkLocalAddr (
+ IN EFI_IPv6_ADDRESS *Ip6
+ )
+{
+ UINT8 Index;
+
+ ASSERT (Ip6 != NULL);
+
+ if (Ip6->Addr[0] != 0xFE) {
+ return FALSE;
+ }
+
+ if (Ip6->Addr[1] != 0x80) {
+ return FALSE;
+ }
+
+ for (Index = 2; Index < 8; Index++) {
+ if (Ip6->Addr[Index] != 0) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ Check whether the Ipv6 address1 and address2 are on the connected network.
+
+ ASSERT if Ip1 or Ip2 is NULL.
+ ASSERT if PrefixLength exceeds or equals to IP6_PREFIX_MAX.
+
+ @param[in] Ip1 - Ip6 address1, in network order.
+ @param[in] Ip2 - Ip6 address2, in network order.
+ @param[in] PrefixLength - The prefix length of the checking net.
+
+ @retval TRUE - Yes, connected.
+ @retval FALSE - No.
+
+**/
+BOOLEAN
+EFIAPI
+NetIp6IsNetEqual (
+ EFI_IPv6_ADDRESS *Ip1,
+ EFI_IPv6_ADDRESS *Ip2,
+ UINT8 PrefixLength
+ )
+{
+ UINT8 Byte;
+ UINT8 Bit;
+ UINT8 Mask;
+
+ ASSERT ((Ip1 != NULL) && (Ip2 != NULL) && (PrefixLength < IP6_PREFIX_MAX));
+
+ if (PrefixLength == 0) {
+ return TRUE;
+ }
+
+ Byte = (UINT8) (PrefixLength / 8);
+ Bit = (UINT8) (PrefixLength % 8);
+
+ if (CompareMem (Ip1, Ip2, Byte) != 0) {
+ return FALSE;
+ }
+
+ if (Bit > 0) {
+ Mask = (UINT8) (0xFF << (8 - Bit));
+
+ ASSERT (Byte < 16);
+ if (Byte >= 16) {
+ return FALSE;
+ }
+ if ((Ip1->Addr[Byte] & Mask) != (Ip2->Addr[Byte] & Mask)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Switches the endianess of an IPv6 address
+
+ ASSERT if Ip6 is NULL.
+
+ This function swaps the bytes in a 128-bit IPv6 address to switch the value
+ from little endian to big endian or vice versa. The byte swapped value is
+ returned.
+
+ @param Ip6 Points to an IPv6 address
+
+ @return The byte swapped IPv6 address.
+
+**/
+EFI_IPv6_ADDRESS *
+EFIAPI
+Ip6Swap128 (
+ EFI_IPv6_ADDRESS *Ip6
+ )
+{
+ UINT64 High;
+ UINT64 Low;
+
+ ASSERT (Ip6 != NULL);
+
+ CopyMem (&High, Ip6, sizeof (UINT64));
+ CopyMem (&Low, &Ip6->Addr[8], sizeof (UINT64));
+
+ High = SwapBytes64 (High);
+ Low = SwapBytes64 (Low);
+
+ CopyMem (Ip6, &Low, sizeof (UINT64));
+ CopyMem (&Ip6->Addr[8], &High, sizeof (UINT64));
+
+ return Ip6;
+}
+
+/**
+ Initialize a random seed using current time and monotonic count.
+
+ Get current time and monotonic count first. Then initialize a random seed
+ based on some basic mathematics operation on the hour, day, minute, second,
+ nanosecond and year of the current time and the monotonic count value.
+
+ @return The random seed initialized with current time.
+
+**/
+UINT32
+EFIAPI
+NetRandomInitSeed (
+ VOID
+ )
+{
+ EFI_TIME Time;
+ UINT32 Seed;
+ UINT64 MonotonicCount;
+
+ gRT->GetTime (&Time, NULL);
+ Seed = (Time.Hour << 24 | Time.Day << 16 | Time.Minute << 8 | Time.Second);
+ Seed ^= Time.Nanosecond;
+ Seed ^= Time.Year << 7;
+
+ gBS->GetNextMonotonicCount (&MonotonicCount);
+ Seed += (UINT32) MonotonicCount;
+
+ return Seed;
+}
+
+
+/**
+ Extract a UINT32 from a byte stream.
+
+ ASSERT if Buf is NULL.
+
+ Copy a UINT32 from a byte stream, then converts it from Network
+ byte order to host byte order. Use this function to avoid alignment error.
+
+ @param[in] Buf The buffer to extract the UINT32.
+
+ @return The UINT32 extracted.
+
+**/
+UINT32
+EFIAPI
+NetGetUint32 (
+ IN UINT8 *Buf
+ )
+{
+ UINT32 Value;
+
+ ASSERT (Buf != NULL);
+
+ CopyMem (&Value, Buf, sizeof (UINT32));
+ return NTOHL (Value);
+}
+
+
+/**
+ Put a UINT32 to the byte stream in network byte order.
+
+ ASSERT if Buf is NULL.
+
+ Converts a UINT32 from host byte order to network byte order. Then copy it to the
+ byte stream.
+
+ @param[in, out] Buf The buffer to put the UINT32.
+ @param[in] Data The data to be converted and put into the byte stream.
+
+**/
+VOID
+EFIAPI
+NetPutUint32 (
+ IN OUT UINT8 *Buf,
+ IN UINT32 Data
+ )
+{
+ ASSERT (Buf != NULL);
+
+ Data = HTONL (Data);
+ CopyMem (Buf, &Data, sizeof (UINT32));
+}
+
+
+/**
+ Remove the first node entry on the list, and return the removed node entry.
+
+ Removes the first node Entry from a doubly linked list. It is up to the caller of
+ this function to release the memory used by the first node if that is required. On
+ exit, the removed node is returned.
+
+ If Head is NULL, then ASSERT().
+ If Head was not initialized, then ASSERT().
+ If PcdMaximumLinkedListLength is not zero, and the number of nodes in the
+ linked list including the head node is greater than or equal to PcdMaximumLinkedListLength,
+ then ASSERT().
+
+ @param[in, out] Head The list header.
+
+ @return The first node entry that is removed from the list, NULL if the list is empty.
+
+**/
+LIST_ENTRY *
+EFIAPI
+NetListRemoveHead (
+ IN OUT LIST_ENTRY *Head
+ )
+{
+ LIST_ENTRY *First;
+
+ ASSERT (Head != NULL);
+
+ if (IsListEmpty (Head)) {
+ return NULL;
+ }
+
+ First = Head->ForwardLink;
+ Head->ForwardLink = First->ForwardLink;
+ First->ForwardLink->BackLink = Head;
+
+ DEBUG_CODE (
+ First->ForwardLink = (LIST_ENTRY *) NULL;
+ First->BackLink = (LIST_ENTRY *) NULL;
+ );
+
+ return First;
+}
+
+
+/**
+ Remove the last node entry on the list and and return the removed node entry.
+
+ Removes the last node entry from a doubly linked list. It is up to the caller of
+ this function to release the memory used by the first node if that is required. On
+ exit, the removed node is returned.
+
+ If Head is NULL, then ASSERT().
+ If Head was not initialized, then ASSERT().
+ If PcdMaximumLinkedListLength is not zero, and the number of nodes in the
+ linked list including the head node is greater than or equal to PcdMaximumLinkedListLength,
+ then ASSERT().
+
+ @param[in, out] Head The list head.
+
+ @return The last node entry that is removed from the list, NULL if the list is empty.
+
+**/
+LIST_ENTRY *
+EFIAPI
+NetListRemoveTail (
+ IN OUT LIST_ENTRY *Head
+ )
+{
+ LIST_ENTRY *Last;
+
+ ASSERT (Head != NULL);
+
+ if (IsListEmpty (Head)) {
+ return NULL;
+ }
+
+ Last = Head->BackLink;
+ Head->BackLink = Last->BackLink;
+ Last->BackLink->ForwardLink = Head;
+
+ DEBUG_CODE (
+ Last->ForwardLink = (LIST_ENTRY *) NULL;
+ Last->BackLink = (LIST_ENTRY *) NULL;
+ );
+
+ return Last;
+}
+
+
+/**
+ Insert a new node entry after a designated node entry of a doubly linked list.
+
+ ASSERT if PrevEntry or NewEntry is NULL.
+
+ Inserts a new node entry donated by NewEntry after the node entry donated by PrevEntry
+ of the doubly linked list.
+
+ @param[in, out] PrevEntry The previous entry to insert after.
+ @param[in, out] NewEntry The new entry to insert.
+
+**/
+VOID
+EFIAPI
+NetListInsertAfter (
+ IN OUT LIST_ENTRY *PrevEntry,
+ IN OUT LIST_ENTRY *NewEntry
+ )
+{
+ ASSERT (PrevEntry != NULL && NewEntry != NULL);
+
+ NewEntry->BackLink = PrevEntry;
+ NewEntry->ForwardLink = PrevEntry->ForwardLink;
+ PrevEntry->ForwardLink->BackLink = NewEntry;
+ PrevEntry->ForwardLink = NewEntry;
+}
+
+
+/**
+ Insert a new node entry before a designated node entry of a doubly linked list.
+
+ ASSERT if PostEntry or NewEntry is NULL.
+
+ Inserts a new node entry donated by NewEntry after the node entry donated by PostEntry
+ of the doubly linked list.
+
+ @param[in, out] PostEntry The entry to insert before.
+ @param[in, out] NewEntry The new entry to insert.
+
+**/
+VOID
+EFIAPI
+NetListInsertBefore (
+ IN OUT LIST_ENTRY *PostEntry,
+ IN OUT LIST_ENTRY *NewEntry
+ )
+{
+ ASSERT (PostEntry != NULL && NewEntry != NULL);
+
+ NewEntry->ForwardLink = PostEntry;
+ NewEntry->BackLink = PostEntry->BackLink;
+ PostEntry->BackLink->ForwardLink = NewEntry;
+ PostEntry->BackLink = NewEntry;
+}
+
+/**
+ Safe destroy nodes in a linked list, and return the length of the list after all possible operations finished.
+
+ Destroy network child instance list by list traversals is not safe due to graph dependencies between nodes.
+ This function performs a safe traversal to destroy these nodes by checking to see if the node being destroyed
+ has been removed from the list or not.
+ If it has been removed, then restart the traversal from the head.
+ If it hasn't been removed, then continue with the next node directly.
+ This function will end the iterate and return the CallBack's last return value if error happens,
+ or retrun EFI_SUCCESS if 2 complete passes are made with no changes in the number of children in the list.
+
+ @param[in] List The head of the list.
+ @param[in] CallBack Pointer to the callback function to destroy one node in the list.
+ @param[in] Context Pointer to the callback function's context: corresponds to the
+ parameter Context in NET_DESTROY_LINK_LIST_CALLBACK.
+ @param[out] ListLength The length of the link list if the function returns successfully.
+
+ @retval EFI_SUCCESS Two complete passes are made with no changes in the number of children.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+ @retval Others Return the CallBack's last return value.
+
+**/
+EFI_STATUS
+EFIAPI
+NetDestroyLinkList (
+ IN LIST_ENTRY *List,
+ IN NET_DESTROY_LINK_LIST_CALLBACK CallBack,
+ IN VOID *Context, OPTIONAL
+ OUT UINTN *ListLength OPTIONAL
+ )
+{
+ UINTN PreviousLength;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Ptr;
+ UINTN Length;
+ EFI_STATUS Status;
+
+ if (List == NULL || CallBack == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Length = 0;
+ do {
+ PreviousLength = Length;
+ Entry = GetFirstNode (List);
+ while (!IsNull (List, Entry)) {
+ Status = CallBack (Entry, Context);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Walk through the list to see whether the Entry has been removed or not.
+ // If the Entry still exists, just try to destroy the next one.
+ // If not, go back to the start point to iterate the list again.
+ //
+ for (Ptr = List->ForwardLink; Ptr != List; Ptr = Ptr->ForwardLink) {
+ if (Ptr == Entry) {
+ break;
+ }
+ }
+ if (Ptr == Entry) {
+ Entry = GetNextNode (List, Entry);
+ } else {
+ Entry = GetFirstNode (List);
+ }
+ }
+ for (Length = 0, Ptr = List->ForwardLink; Ptr != List; Length++, Ptr = Ptr->ForwardLink);
+ } while (Length != PreviousLength);
+
+ if (ListLength != NULL) {
+ *ListLength = Length;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ This function checks the input Handle to see if it's one of these handles in ChildHandleBuffer.
+
+ @param[in] Handle Handle to be checked.
+ @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval TRUE Found the input Handle in ChildHandleBuffer.
+ @retval FALSE Can't find the input Handle in ChildHandleBuffer.
+
+**/
+BOOLEAN
+EFIAPI
+NetIsInHandleBuffer (
+ IN EFI_HANDLE Handle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ UINTN Index;
+
+ if (NumberOfChildren == 0 || ChildHandleBuffer == NULL) {
+ return FALSE;
+ }
+
+ for (Index = 0; Index < NumberOfChildren; Index++) {
+ if (Handle == ChildHandleBuffer[Index]) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Initialize the netmap. Netmap is a reposity to keep the <Key, Value> pairs.
+
+ Initialize the forward and backward links of two head nodes donated by Map->Used
+ and Map->Recycled of two doubly linked lists.
+ Initializes the count of the <Key, Value> pairs in the netmap to zero.
+
+ If Map is NULL, then ASSERT().
+ If the address of Map->Used is NULL, then ASSERT().
+ If the address of Map->Recycled is NULl, then ASSERT().
+
+ @param[in, out] Map The netmap to initialize.
+
+**/
+VOID
+EFIAPI
+NetMapInit (
+ IN OUT NET_MAP *Map
+ )
+{
+ ASSERT (Map != NULL);
+
+ InitializeListHead (&Map->Used);
+ InitializeListHead (&Map->Recycled);
+ Map->Count = 0;
+}
+
+
+/**
+ To clean up the netmap, that is, release allocated memories.
+
+ Removes all nodes of the Used doubly linked list and free memory of all related netmap items.
+ Removes all nodes of the Recycled doubly linked list and free memory of all related netmap items.
+ The number of the <Key, Value> pairs in the netmap is set to be zero.
+
+ If Map is NULL, then ASSERT().
+
+ @param[in, out] Map The netmap to clean up.
+
+**/
+VOID
+EFIAPI
+NetMapClean (
+ IN OUT NET_MAP *Map
+ )
+{
+ NET_MAP_ITEM *Item;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+
+ ASSERT (Map != NULL);
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Map->Used) {
+ Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);
+
+ RemoveEntryList (&Item->Link);
+ Map->Count--;
+
+ gBS->FreePool (Item);
+ }
+
+ ASSERT ((Map->Count == 0) && IsListEmpty (&Map->Used));
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Map->Recycled) {
+ Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);
+
+ RemoveEntryList (&Item->Link);
+ gBS->FreePool (Item);
+ }
+
+ ASSERT (IsListEmpty (&Map->Recycled));
+}
+
+
+/**
+ Test whether the netmap is empty and return true if it is.
+
+ If the number of the <Key, Value> pairs in the netmap is zero, return TRUE.
+
+ If Map is NULL, then ASSERT().
+
+ @param[in] Map The net map to test.
+
+ @return TRUE if the netmap is empty, otherwise FALSE.
+
+**/
+BOOLEAN
+EFIAPI
+NetMapIsEmpty (
+ IN NET_MAP *Map
+ )
+{
+ ASSERT (Map != NULL);
+ return (BOOLEAN) (Map->Count == 0);
+}
+
+
+/**
+ Return the number of the <Key, Value> pairs in the netmap.
+
+ If Map is NULL, then ASSERT().
+
+ @param[in] Map The netmap to get the entry number.
+
+ @return The entry number in the netmap.
+
+**/
+UINTN
+EFIAPI
+NetMapGetCount (
+ IN NET_MAP *Map
+ )
+{
+ ASSERT (Map != NULL);
+ return Map->Count;
+}
+
+
+/**
+ Return one allocated item.
+
+ If the Recycled doubly linked list of the netmap is empty, it will try to allocate
+ a batch of items if there are enough resources and add corresponding nodes to the begining
+ of the Recycled doubly linked list of the netmap. Otherwise, it will directly remove
+ the fist node entry of the Recycled doubly linked list and return the corresponding item.
+
+ If Map is NULL, then ASSERT().
+
+ @param[in, out] Map The netmap to allocate item for.
+
+ @return The allocated item. If NULL, the
+ allocation failed due to resource limit.
+
+**/
+NET_MAP_ITEM *
+NetMapAllocItem (
+ IN OUT NET_MAP *Map
+ )
+{
+ NET_MAP_ITEM *Item;
+ LIST_ENTRY *Head;
+ UINTN Index;
+
+ ASSERT (Map != NULL);
+
+ Head = &Map->Recycled;
+
+ if (IsListEmpty (Head)) {
+ for (Index = 0; Index < NET_MAP_INCREAMENT; Index++) {
+ Item = AllocatePool (sizeof (NET_MAP_ITEM));
+
+ if (Item == NULL) {
+ if (Index == 0) {
+ return NULL;
+ }
+
+ break;
+ }
+
+ InsertHeadList (Head, &Item->Link);
+ }
+ }
+
+ Item = NET_LIST_HEAD (Head, NET_MAP_ITEM, Link);
+ NetListRemoveHead (Head);
+
+ return Item;
+}
+
+
+/**
+ Allocate an item to save the <Key, Value> pair to the head of the netmap.
+
+ Allocate an item to save the <Key, Value> pair and add corresponding node entry
+ to the beginning of the Used doubly linked list. The number of the <Key, Value>
+ pairs in the netmap increase by 1.
+
+ If Map is NULL, then ASSERT().
+ If Key is NULL, then ASSERT().
+
+ @param[in, out] Map The netmap to insert into.
+ @param[in] Key The user's key.
+ @param[in] Value The user's value for the key.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the item.
+ @retval EFI_SUCCESS The item is inserted to the head.
+
+**/
+EFI_STATUS
+EFIAPI
+NetMapInsertHead (
+ IN OUT NET_MAP *Map,
+ IN VOID *Key,
+ IN VOID *Value OPTIONAL
+ )
+{
+ NET_MAP_ITEM *Item;
+
+ ASSERT (Map != NULL && Key != NULL);
+
+ Item = NetMapAllocItem (Map);
+
+ if (Item == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Item->Key = Key;
+ Item->Value = Value;
+ InsertHeadList (&Map->Used, &Item->Link);
+
+ Map->Count++;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Allocate an item to save the <Key, Value> pair to the tail of the netmap.
+
+ Allocate an item to save the <Key, Value> pair and add corresponding node entry
+ to the tail of the Used doubly linked list. The number of the <Key, Value>
+ pairs in the netmap increase by 1.
+
+ If Map is NULL, then ASSERT().
+ If Key is NULL, then ASSERT().
+
+ @param[in, out] Map The netmap to insert into.
+ @param[in] Key The user's key.
+ @param[in] Value The user's value for the key.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the item.
+ @retval EFI_SUCCESS The item is inserted to the tail.
+
+**/
+EFI_STATUS
+EFIAPI
+NetMapInsertTail (
+ IN OUT NET_MAP *Map,
+ IN VOID *Key,
+ IN VOID *Value OPTIONAL
+ )
+{
+ NET_MAP_ITEM *Item;
+
+ ASSERT (Map != NULL && Key != NULL);
+
+ Item = NetMapAllocItem (Map);
+
+ if (Item == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Item->Key = Key;
+ Item->Value = Value;
+ InsertTailList (&Map->Used, &Item->Link);
+
+ Map->Count++;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Check whether the item is in the Map and return TRUE if it is.
+
+ If Map is NULL, then ASSERT().
+ If Item is NULL, then ASSERT().
+
+ @param[in] Map The netmap to search within.
+ @param[in] Item The item to search.
+
+ @return TRUE if the item is in the netmap, otherwise FALSE.
+
+**/
+BOOLEAN
+NetItemInMap (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item
+ )
+{
+ LIST_ENTRY *ListEntry;
+
+ ASSERT (Map != NULL && Item != NULL);
+
+ NET_LIST_FOR_EACH (ListEntry, &Map->Used) {
+ if (ListEntry == &Item->Link) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Find the key in the netmap and returns the point to the item contains the Key.
+
+ Iterate the Used doubly linked list of the netmap to get every item. Compare the key of every
+ item with the key to search. It returns the point to the item contains the Key if found.
+
+ If Map is NULL, then ASSERT().
+ If Key is NULL, then ASSERT().
+
+ @param[in] Map The netmap to search within.
+ @param[in] Key The key to search.
+
+ @return The point to the item contains the Key, or NULL if Key isn't in the map.
+
+**/
+NET_MAP_ITEM *
+EFIAPI
+NetMapFindKey (
+ IN NET_MAP *Map,
+ IN VOID *Key
+ )
+{
+ LIST_ENTRY *Entry;
+ NET_MAP_ITEM *Item;
+
+ ASSERT (Map != NULL && Key != NULL);
+
+ NET_LIST_FOR_EACH (Entry, &Map->Used) {
+ Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);
+
+ if (Item->Key == Key) {
+ return Item;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Remove the node entry of the item from the netmap and return the key of the removed item.
+
+ Remove the node entry of the item from the Used doubly linked list of the netmap.
+ The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node
+ entry of the item to the Recycled doubly linked list of the netmap. If Value is not NULL,
+ Value will point to the value of the item. It returns the key of the removed item.
+
+ If Map is NULL, then ASSERT().
+ If Item is NULL, then ASSERT().
+ if item in not in the netmap, then ASSERT().
+
+ @param[in, out] Map The netmap to remove the item from.
+ @param[in, out] Item The item to remove.
+ @param[out] Value The variable to receive the value if not NULL.
+
+ @return The key of the removed item.
+
+**/
+VOID *
+EFIAPI
+NetMapRemoveItem (
+ IN OUT NET_MAP *Map,
+ IN OUT NET_MAP_ITEM *Item,
+ OUT VOID **Value OPTIONAL
+ )
+{
+ ASSERT ((Map != NULL) && (Item != NULL));
+ ASSERT (NetItemInMap (Map, Item));
+
+ RemoveEntryList (&Item->Link);
+ Map->Count--;
+ InsertHeadList (&Map->Recycled, &Item->Link);
+
+ if (Value != NULL) {
+ *Value = Item->Value;
+ }
+
+ return Item->Key;
+}
+
+
+/**
+ Remove the first node entry on the netmap and return the key of the removed item.
+
+ Remove the first node entry from the Used doubly linked list of the netmap.
+ The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node
+ entry to the Recycled doubly linked list of the netmap. If parameter Value is not NULL,
+ parameter Value will point to the value of the item. It returns the key of the removed item.
+
+ If Map is NULL, then ASSERT().
+ If the Used doubly linked list is empty, then ASSERT().
+
+ @param[in, out] Map The netmap to remove the head from.
+ @param[out] Value The variable to receive the value if not NULL.
+
+ @return The key of the item removed.
+
+**/
+VOID *
+EFIAPI
+NetMapRemoveHead (
+ IN OUT NET_MAP *Map,
+ OUT VOID **Value OPTIONAL
+ )
+{
+ NET_MAP_ITEM *Item;
+
+ //
+ // Often, it indicates a programming error to remove
+ // the first entry in an empty list
+ //
+ ASSERT (Map && !IsListEmpty (&Map->Used));
+
+ Item = NET_LIST_HEAD (&Map->Used, NET_MAP_ITEM, Link);
+ RemoveEntryList (&Item->Link);
+ Map->Count--;
+ InsertHeadList (&Map->Recycled, &Item->Link);
+
+ if (Value != NULL) {
+ *Value = Item->Value;
+ }
+
+ return Item->Key;
+}
+
+
+/**
+ Remove the last node entry on the netmap and return the key of the removed item.
+
+ Remove the last node entry from the Used doubly linked list of the netmap.
+ The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node
+ entry to the Recycled doubly linked list of the netmap. If parameter Value is not NULL,
+ parameter Value will point to the value of the item. It returns the key of the removed item.
+
+ If Map is NULL, then ASSERT().
+ If the Used doubly linked list is empty, then ASSERT().
+
+ @param[in, out] Map The netmap to remove the tail from.
+ @param[out] Value The variable to receive the value if not NULL.
+
+ @return The key of the item removed.
+
+**/
+VOID *
+EFIAPI
+NetMapRemoveTail (
+ IN OUT NET_MAP *Map,
+ OUT VOID **Value OPTIONAL
+ )
+{
+ NET_MAP_ITEM *Item;
+
+ //
+ // Often, it indicates a programming error to remove
+ // the last entry in an empty list
+ //
+ ASSERT (Map && !IsListEmpty (&Map->Used));
+
+ Item = NET_LIST_TAIL (&Map->Used, NET_MAP_ITEM, Link);
+ RemoveEntryList (&Item->Link);
+ Map->Count--;
+ InsertHeadList (&Map->Recycled, &Item->Link);
+
+ if (Value != NULL) {
+ *Value = Item->Value;
+ }
+
+ return Item->Key;
+}
+
+
+/**
+ Iterate through the netmap and call CallBack for each item.
+
+ It will continue the traverse if CallBack returns EFI_SUCCESS, otherwise, break
+ from the loop. It returns the CallBack's last return value. This function is
+ delete safe for the current item.
+
+ If Map is NULL, then ASSERT().
+ If CallBack is NULL, then ASSERT().
+
+ @param[in] Map The Map to iterate through.
+ @param[in] CallBack The callback function to call for each item.
+ @param[in] Arg The opaque parameter to the callback.
+
+ @retval EFI_SUCCESS There is no item in the netmap or CallBack for each item
+ return EFI_SUCCESS.
+ @retval Others It returns the CallBack's last return value.
+
+**/
+EFI_STATUS
+EFIAPI
+NetMapIterate (
+ IN NET_MAP *Map,
+ IN NET_MAP_CALLBACK CallBack,
+ IN VOID *Arg OPTIONAL
+ )
+{
+
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ LIST_ENTRY *Head;
+ NET_MAP_ITEM *Item;
+ EFI_STATUS Result;
+
+ ASSERT ((Map != NULL) && (CallBack != NULL));
+
+ Head = &Map->Used;
+
+ if (IsListEmpty (Head)) {
+ return EFI_SUCCESS;
+ }
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {
+ Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);
+ Result = CallBack (Map, Item, Arg);
+
+ if (EFI_ERROR (Result)) {
+ return Result;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This is the default unload handle for all the network drivers.
+
+ Disconnect the driver specified by ImageHandle from all the devices in the handle database.
+ Uninstall all the protocols installed in the driver entry point.
+
+ @param[in] ImageHandle The drivers' driver image.
+
+ @retval EFI_SUCCESS The image is unloaded.
+ @retval Others Failed to unload the image.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibDefaultUnload (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *DeviceHandleBuffer;
+ UINTN DeviceHandleCount;
+ UINTN Index;
+ UINTN Index2;
+ EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;
+ EFI_COMPONENT_NAME_PROTOCOL *ComponentName;
+ EFI_COMPONENT_NAME2_PROTOCOL *ComponentName2;
+
+ //
+ // Get the list of all the handles in the handle database.
+ // If there is an error getting the list, then the unload
+ // operation fails.
+ //
+ Status = gBS->LocateHandleBuffer (
+ AllHandles,
+ NULL,
+ NULL,
+ &DeviceHandleCount,
+ &DeviceHandleBuffer
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ for (Index = 0; Index < DeviceHandleCount; Index++) {
+ Status = gBS->HandleProtocol (
+ DeviceHandleBuffer[Index],
+ &gEfiDriverBindingProtocolGuid,
+ (VOID **) &DriverBinding
+ );
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ if (DriverBinding->ImageHandle != ImageHandle) {
+ continue;
+ }
+
+ //
+ // Disconnect the driver specified by ImageHandle from all
+ // the devices in the handle database.
+ //
+ for (Index2 = 0; Index2 < DeviceHandleCount; Index2++) {
+ Status = gBS->DisconnectController (
+ DeviceHandleBuffer[Index2],
+ DriverBinding->DriverBindingHandle,
+ NULL
+ );
+ }
+
+ //
+ // Uninstall all the protocols installed in the driver entry point
+ //
+ gBS->UninstallProtocolInterface (
+ DriverBinding->DriverBindingHandle,
+ &gEfiDriverBindingProtocolGuid,
+ DriverBinding
+ );
+
+ Status = gBS->HandleProtocol (
+ DeviceHandleBuffer[Index],
+ &gEfiComponentNameProtocolGuid,
+ (VOID **) &ComponentName
+ );
+ if (!EFI_ERROR (Status)) {
+ gBS->UninstallProtocolInterface (
+ DriverBinding->DriverBindingHandle,
+ &gEfiComponentNameProtocolGuid,
+ ComponentName
+ );
+ }
+
+ Status = gBS->HandleProtocol (
+ DeviceHandleBuffer[Index],
+ &gEfiComponentName2ProtocolGuid,
+ (VOID **) &ComponentName2
+ );
+ if (!EFI_ERROR (Status)) {
+ gBS->UninstallProtocolInterface (
+ DriverBinding->DriverBindingHandle,
+ &gEfiComponentName2ProtocolGuid,
+ ComponentName2
+ );
+ }
+ }
+
+ //
+ // Free the buffer containing the list of handles from the handle database
+ //
+ if (DeviceHandleBuffer != NULL) {
+ gBS->FreePool (DeviceHandleBuffer);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Create a child of the service that is identified by ServiceBindingGuid.
+
+ Get the ServiceBinding Protocol first, then use it to create a child.
+
+ If ServiceBindingGuid is NULL, then ASSERT().
+ If ChildHandle is NULL, then ASSERT().
+
+ @param[in] Controller The controller which has the service installed.
+ @param[in] Image The image handle used to open service.
+ @param[in] ServiceBindingGuid The service's Guid.
+ @param[in, out] ChildHandle The handle to receive the create child.
+
+ @retval EFI_SUCCESS The child is successfully created.
+ @retval Others Failed to create the child.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibCreateServiceChild (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE Image,
+ IN EFI_GUID *ServiceBindingGuid,
+ IN OUT EFI_HANDLE *ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_SERVICE_BINDING_PROTOCOL *Service;
+
+
+ ASSERT ((ServiceBindingGuid != NULL) && (ChildHandle != NULL));
+
+ //
+ // Get the ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ ServiceBindingGuid,
+ (VOID **) &Service,
+ Image,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Create a child
+ //
+ Status = Service->CreateChild (Service, ChildHandle);
+ return Status;
+}
+
+
+/**
+ Destroy a child of the service that is identified by ServiceBindingGuid.
+
+ Get the ServiceBinding Protocol first, then use it to destroy a child.
+
+ If ServiceBindingGuid is NULL, then ASSERT().
+
+ @param[in] Controller The controller which has the service installed.
+ @param[in] Image The image handle used to open service.
+ @param[in] ServiceBindingGuid The service's Guid.
+ @param[in] ChildHandle The child to destroy.
+
+ @retval EFI_SUCCESS The child is successfully destroyed.
+ @retval Others Failed to destroy the child.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibDestroyServiceChild (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE Image,
+ IN EFI_GUID *ServiceBindingGuid,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_SERVICE_BINDING_PROTOCOL *Service;
+
+ ASSERT (ServiceBindingGuid != NULL);
+
+ //
+ // Get the ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ ServiceBindingGuid,
+ (VOID **) &Service,
+ Image,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // destroy the child
+ //
+ Status = Service->DestroyChild (Service, ChildHandle);
+ return Status;
+}
+
+/**
+ Get handle with Simple Network Protocol installed on it.
+
+ There should be MNP Service Binding Protocol installed on the input ServiceHandle.
+ If Simple Network Protocol is already installed on the ServiceHandle, the
+ ServiceHandle will be returned. If SNP is not installed on the ServiceHandle,
+ try to find its parent handle with SNP installed.
+
+ @param[in] ServiceHandle The handle where network service binding protocols are
+ installed on.
+ @param[out] Snp The pointer to store the address of the SNP instance.
+ This is an optional parameter that may be NULL.
+
+ @return The SNP handle, or NULL if not found.
+
+**/
+EFI_HANDLE
+EFIAPI
+NetLibGetSnpHandle (
+ IN EFI_HANDLE ServiceHandle,
+ OUT EFI_SIMPLE_NETWORK_PROTOCOL **Snp OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *SnpInstance;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_HANDLE SnpHandle;
+
+ //
+ // Try to open SNP from ServiceHandle
+ //
+ SnpInstance = NULL;
+ Status = gBS->HandleProtocol (ServiceHandle, &gEfiSimpleNetworkProtocolGuid, (VOID **) &SnpInstance);
+ if (!EFI_ERROR (Status)) {
+ if (Snp != NULL) {
+ *Snp = SnpInstance;
+ }
+ return ServiceHandle;
+ }
+
+ //
+ // Failed to open SNP, try to get SNP handle by LocateDevicePath()
+ //
+ DevicePath = DevicePathFromHandle (ServiceHandle);
+ if (DevicePath == NULL) {
+ return NULL;
+ }
+
+ SnpHandle = NULL;
+ Status = gBS->LocateDevicePath (&gEfiSimpleNetworkProtocolGuid, &DevicePath, &SnpHandle);
+ if (EFI_ERROR (Status)) {
+ //
+ // Failed to find SNP handle
+ //
+ return NULL;
+ }
+
+ Status = gBS->HandleProtocol (SnpHandle, &gEfiSimpleNetworkProtocolGuid, (VOID **) &SnpInstance);
+ if (!EFI_ERROR (Status)) {
+ if (Snp != NULL) {
+ *Snp = SnpInstance;
+ }
+ return SnpHandle;
+ }
+
+ return NULL;
+}
+
+/**
+ Retrieve VLAN ID of a VLAN device handle.
+
+ Search VLAN device path node in Device Path of specified ServiceHandle and
+ return its VLAN ID. If no VLAN device path node found, then this ServiceHandle
+ is not a VLAN device handle, and 0 will be returned.
+
+ @param[in] ServiceHandle The handle where network service binding protocols are
+ installed on.
+
+ @return VLAN ID of the device handle, or 0 if not a VLAN device.
+
+**/
+UINT16
+EFIAPI
+NetLibGetVlanId (
+ IN EFI_HANDLE ServiceHandle
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *Node;
+
+ DevicePath = DevicePathFromHandle (ServiceHandle);
+ if (DevicePath == NULL) {
+ return 0;
+ }
+
+ Node = DevicePath;
+ while (!IsDevicePathEnd (Node)) {
+ if (Node->Type == MESSAGING_DEVICE_PATH && Node->SubType == MSG_VLAN_DP) {
+ return ((VLAN_DEVICE_PATH *) Node)->VlanId;
+ }
+ Node = NextDevicePathNode (Node);
+ }
+
+ return 0;
+}
+
+/**
+ Find VLAN device handle with specified VLAN ID.
+
+ The VLAN child device handle is created by VLAN Config Protocol on ControllerHandle.
+ This function will append VLAN device path node to the parent device path,
+ and then use LocateDevicePath() to find the correct VLAN device handle.
+
+ @param[in] ControllerHandle The handle where network service binding protocols are
+ installed on.
+ @param[in] VlanId The configured VLAN ID for the VLAN device.
+
+ @return The VLAN device handle, or NULL if not found.
+
+**/
+EFI_HANDLE
+EFIAPI
+NetLibGetVlanHandle (
+ IN EFI_HANDLE ControllerHandle,
+ IN UINT16 VlanId
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *VlanDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ VLAN_DEVICE_PATH VlanNode;
+ EFI_HANDLE Handle;
+
+ ParentDevicePath = DevicePathFromHandle (ControllerHandle);
+ if (ParentDevicePath == NULL) {
+ return NULL;
+ }
+
+ //
+ // Construct VLAN device path
+ //
+ CopyMem (&VlanNode, &mNetVlanDevicePathTemplate, sizeof (VLAN_DEVICE_PATH));
+ VlanNode.VlanId = VlanId;
+ VlanDevicePath = AppendDevicePathNode (
+ ParentDevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *) &VlanNode
+ );
+ if (VlanDevicePath == NULL) {
+ return NULL;
+ }
+
+ //
+ // Find VLAN device handle
+ //
+ Handle = NULL;
+ DevicePath = VlanDevicePath;
+ gBS->LocateDevicePath (
+ &gEfiDevicePathProtocolGuid,
+ &DevicePath,
+ &Handle
+ );
+ if (!IsDevicePathEnd (DevicePath)) {
+ //
+ // Device path is not exactly match
+ //
+ Handle = NULL;
+ }
+
+ FreePool (VlanDevicePath);
+ return Handle;
+}
+
+/**
+ Get MAC address associated with the network service handle.
+
+ If MacAddress is NULL, then ASSERT().
+ If AddressSize is NULL, then ASSERT().
+
+ There should be MNP Service Binding Protocol installed on the input ServiceHandle.
+ If SNP is installed on the ServiceHandle or its parent handle, MAC address will
+ be retrieved from SNP. If no SNP found, try to get SNP mode data use MNP.
+
+ @param[in] ServiceHandle The handle where network service binding protocols are
+ installed on.
+ @param[out] MacAddress The pointer to store the returned MAC address.
+ @param[out] AddressSize The length of returned MAC address.
+
+ @retval EFI_SUCCESS MAC address is returned successfully.
+ @retval Others Failed to get SNP mode data.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibGetMacAddress (
+ IN EFI_HANDLE ServiceHandle,
+ OUT EFI_MAC_ADDRESS *MacAddress,
+ OUT UINTN *AddressSize
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ EFI_SIMPLE_NETWORK_MODE SnpModeData;
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;
+ EFI_SERVICE_BINDING_PROTOCOL *MnpSb;
+ EFI_HANDLE *SnpHandle;
+ EFI_HANDLE MnpChildHandle;
+
+ ASSERT (MacAddress != NULL);
+ ASSERT (AddressSize != NULL);
+
+ //
+ // Try to get SNP handle
+ //
+ Snp = NULL;
+ SnpHandle = NetLibGetSnpHandle (ServiceHandle, &Snp);
+ if (SnpHandle != NULL) {
+ //
+ // SNP found, use it directly
+ //
+ SnpMode = Snp->Mode;
+ } else {
+ //
+ // Failed to get SNP handle, try to get MAC address from MNP
+ //
+ MnpChildHandle = NULL;
+ Status = gBS->HandleProtocol (
+ ServiceHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ (VOID **) &MnpSb
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Create a MNP child
+ //
+ Status = MnpSb->CreateChild (MnpSb, &MnpChildHandle);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Open MNP protocol
+ //
+ Status = gBS->HandleProtocol (
+ MnpChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ (VOID **) &Mnp
+ );
+ if (EFI_ERROR (Status)) {
+ MnpSb->DestroyChild (MnpSb, MnpChildHandle);
+ return Status;
+ }
+
+ //
+ // Try to get SNP mode from MNP
+ //
+ Status = Mnp->GetModeData (Mnp, NULL, &SnpModeData);
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_STARTED)) {
+ MnpSb->DestroyChild (MnpSb, MnpChildHandle);
+ return Status;
+ }
+ SnpMode = &SnpModeData;
+
+ //
+ // Destroy the MNP child
+ //
+ MnpSb->DestroyChild (MnpSb, MnpChildHandle);
+ }
+
+ *AddressSize = SnpMode->HwAddressSize;
+ CopyMem (MacAddress->Addr, SnpMode->CurrentAddress.Addr, SnpMode->HwAddressSize);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Convert MAC address of the NIC associated with specified Service Binding Handle
+ to a unicode string. Callers are responsible for freeing the string storage.
+
+ If MacString is NULL, then ASSERT().
+
+ Locate simple network protocol associated with the Service Binding Handle and
+ get the mac address from SNP. Then convert the mac address into a unicode
+ string. It takes 2 unicode characters to represent a 1 byte binary buffer.
+ Plus one unicode character for the null-terminator.
+
+ @param[in] ServiceHandle The handle where network service binding protocol is
+ installed on.
+ @param[in] ImageHandle The image handle used to act as the agent handle to
+ get the simple network protocol. This parameter is
+ optional and may be NULL.
+ @param[out] MacString The pointer to store the address of the string
+ representation of the mac address.
+
+ @retval EFI_SUCCESS Convert the mac address a unicode string successfully.
+ @retval EFI_OUT_OF_RESOURCES There are not enough memory resource.
+ @retval Others Failed to open the simple network protocol.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibGetMacString (
+ IN EFI_HANDLE ServiceHandle,
+ IN EFI_HANDLE ImageHandle, OPTIONAL
+ OUT CHAR16 **MacString
+ )
+{
+ EFI_STATUS Status;
+ EFI_MAC_ADDRESS MacAddress;
+ UINT8 *HwAddress;
+ UINTN HwAddressSize;
+ UINT16 VlanId;
+ CHAR16 *String;
+ UINTN Index;
+ UINTN BufferSize;
+
+ ASSERT (MacString != NULL);
+
+ //
+ // Get MAC address of the network device
+ //
+ Status = NetLibGetMacAddress (ServiceHandle, &MacAddress, &HwAddressSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // It takes 2 unicode characters to represent a 1 byte binary buffer.
+ // If VLAN is configured, it will need extra 5 characters like "\0005".
+ // Plus one unicode character for the null-terminator.
+ //
+ BufferSize = (2 * HwAddressSize + 5 + 1) * sizeof (CHAR16);
+ String = AllocateZeroPool (BufferSize);
+ if (String == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ *MacString = String;
+
+ //
+ // Convert the MAC address into a unicode string.
+ //
+ HwAddress = &MacAddress.Addr[0];
+ for (Index = 0; Index < HwAddressSize; Index++) {
+ UnicodeValueToStringS (
+ String,
+ BufferSize - ((UINTN)String - (UINTN)*MacString),
+ PREFIX_ZERO | RADIX_HEX,
+ *(HwAddress++),
+ 2
+ );
+ String += StrnLenS (String, (BufferSize - ((UINTN)String - (UINTN)*MacString)) / sizeof (CHAR16));
+ }
+
+ //
+ // Append VLAN ID if any
+ //
+ VlanId = NetLibGetVlanId (ServiceHandle);
+ if (VlanId != 0) {
+ *String++ = L'\\';
+ UnicodeValueToStringS (
+ String,
+ BufferSize - ((UINTN)String - (UINTN)*MacString),
+ PREFIX_ZERO | RADIX_HEX,
+ VlanId,
+ 4
+ );
+ String += StrnLenS (String, (BufferSize - ((UINTN)String - (UINTN)*MacString)) / sizeof (CHAR16));
+ }
+
+ //
+ // Null terminate the Unicode string
+ //
+ *String = L'\0';
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Detect media status for specified network device.
+
+ If MediaPresent is NULL, then ASSERT().
+
+ The underlying UNDI driver may or may not support reporting media status from
+ GET_STATUS command (PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED). This routine
+ will try to invoke Snp->GetStatus() to get the media status: if media already
+ present, it return directly; if media not present, it will stop SNP and then
+ restart SNP to get the latest media status, this give chance to get the correct
+ media status for old UNDI driver which doesn't support reporting media status
+ from GET_STATUS command.
+ Note: there will be two limitations for current algorithm:
+ 1) for UNDI with this capability, in case of cable is not attached, there will
+ be an redundant Stop/Start() process;
+ 2) for UNDI without this capability, in case that network cable is attached when
+ Snp->Initialize() is invoked while network cable is unattached later,
+ NetLibDetectMedia() will report MediaPresent as TRUE, causing upper layer
+ apps to wait for timeout time.
+
+ @param[in] ServiceHandle The handle where network service binding protocols are
+ installed on.
+ @param[out] MediaPresent The pointer to store the media status.
+
+ @retval EFI_SUCCESS Media detection success.
+ @retval EFI_INVALID_PARAMETER ServiceHandle is not valid network device handle.
+ @retval EFI_UNSUPPORTED Network device does not support media detection.
+ @retval EFI_DEVICE_ERROR SNP is in unknown state.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibDetectMedia (
+ IN EFI_HANDLE ServiceHandle,
+ OUT BOOLEAN *MediaPresent
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE SnpHandle;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ UINT32 InterruptStatus;
+ UINT32 OldState;
+ EFI_MAC_ADDRESS *MCastFilter;
+ UINT32 MCastFilterCount;
+ UINT32 EnableFilterBits;
+ UINT32 DisableFilterBits;
+ BOOLEAN ResetMCastFilters;
+
+ ASSERT (MediaPresent != NULL);
+
+ //
+ // Get SNP handle
+ //
+ Snp = NULL;
+ SnpHandle = NetLibGetSnpHandle (ServiceHandle, &Snp);
+ if (SnpHandle == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check whether SNP support media detection
+ //
+ if (!Snp->Mode->MediaPresentSupported) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data
+ //
+ Status = Snp->GetStatus (Snp, &InterruptStatus, NULL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Snp->Mode->MediaPresent) {
+ //
+ // Media is present, return directly
+ //
+ *MediaPresent = TRUE;
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Till now, GetStatus() report no media; while, in case UNDI not support
+ // reporting media status from GetStatus(), this media status may be incorrect.
+ // So, we will stop SNP and then restart it to get the correct media status.
+ //
+ OldState = Snp->Mode->State;
+ if (OldState >= EfiSimpleNetworkMaxState) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ MCastFilter = NULL;
+
+ if (OldState == EfiSimpleNetworkInitialized) {
+ //
+ // SNP is already in use, need Shutdown/Stop and then Start/Initialize
+ //
+
+ //
+ // Backup current SNP receive filter settings
+ //
+ EnableFilterBits = Snp->Mode->ReceiveFilterSetting;
+ DisableFilterBits = Snp->Mode->ReceiveFilterMask ^ EnableFilterBits;
+
+ ResetMCastFilters = TRUE;
+ MCastFilterCount = Snp->Mode->MCastFilterCount;
+ if (MCastFilterCount != 0) {
+ MCastFilter = AllocateCopyPool (
+ MCastFilterCount * sizeof (EFI_MAC_ADDRESS),
+ Snp->Mode->MCastFilter
+ );
+ ASSERT (MCastFilter != NULL);
+ if (MCastFilter == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ ResetMCastFilters = FALSE;
+ }
+
+ //
+ // Shutdown/Stop the simple network
+ //
+ Status = Snp->Shutdown (Snp);
+ if (!EFI_ERROR (Status)) {
+ Status = Snp->Stop (Snp);
+ }
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Start/Initialize the simple network
+ //
+ Status = Snp->Start (Snp);
+ if (!EFI_ERROR (Status)) {
+ Status = Snp->Initialize (Snp, 0, 0);
+ }
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Here we get the correct media status
+ //
+ *MediaPresent = Snp->Mode->MediaPresent;
+
+ //
+ // Restore SNP receive filter settings
+ //
+ Status = Snp->ReceiveFilters (
+ Snp,
+ EnableFilterBits,
+ DisableFilterBits,
+ ResetMCastFilters,
+ MCastFilterCount,
+ MCastFilter
+ );
+
+ if (MCastFilter != NULL) {
+ FreePool (MCastFilter);
+ }
+
+ return Status;
+ }
+
+ //
+ // SNP is not in use, it's in state of EfiSimpleNetworkStopped or EfiSimpleNetworkStarted
+ //
+ if (OldState == EfiSimpleNetworkStopped) {
+ //
+ // SNP not start yet, start it
+ //
+ Status = Snp->Start (Snp);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+ }
+
+ //
+ // Initialize the simple network
+ //
+ Status = Snp->Initialize (Snp, 0, 0);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ //
+ // Here we get the correct media status
+ //
+ *MediaPresent = Snp->Mode->MediaPresent;
+
+ //
+ // Shut down the simple network
+ //
+ Snp->Shutdown (Snp);
+
+Exit:
+ if (OldState == EfiSimpleNetworkStopped) {
+ //
+ // Original SNP sate is Stopped, restore to original state
+ //
+ Snp->Stop (Snp);
+ }
+
+ if (MCastFilter != NULL) {
+ FreePool (MCastFilter);
+ }
+
+ return Status;
+}
+
+/**
+
+ Detect media state for a network device. This routine will wait for a period of time at
+ a specified checking interval when a certain network is under connecting until connection
+ process finishs or timeout. If Aip protocol is supported by low layer drivers, three kinds
+ of media states can be detected: EFI_SUCCESS, EFI_NOT_READY and EFI_NO_MEDIA, represents
+ connected state, connecting state and no media state respectively. When function detects
+ the current state is EFI_NOT_READY, it will loop to wait for next time's check until state
+ turns to be EFI_SUCCESS or EFI_NO_MEDIA. If Aip protocol is not supported, function will
+ call NetLibDetectMedia() and return state directly.
+
+ @param[in] ServiceHandle The handle where network service binding protocols are
+ installed on.
+ @param[in] Timeout The maximum number of 100ns units to wait when network
+ is connecting. Zero value means detect once and return
+ immediately.
+ @param[out] MediaState The pointer to the detected media state.
+
+ @retval EFI_SUCCESS Media detection success.
+ @retval EFI_INVALID_PARAMETER ServiceHandle is not a valid network device handle or
+ MediaState pointer is NULL.
+ @retval EFI_DEVICE_ERROR A device error occurred.
+ @retval EFI_TIMEOUT Network is connecting but timeout.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibDetectMediaWaitTimeout (
+ IN EFI_HANDLE ServiceHandle,
+ IN UINT64 Timeout,
+ OUT EFI_STATUS *MediaState
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE SnpHandle;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_ADAPTER_INFORMATION_PROTOCOL *Aip;
+ EFI_ADAPTER_INFO_MEDIA_STATE *MediaInfo;
+ BOOLEAN MediaPresent;
+ UINTN DataSize;
+ EFI_STATUS TimerStatus;
+ EFI_EVENT Timer;
+ UINT64 TimeRemained;
+
+ if (MediaState == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ *MediaState = EFI_SUCCESS;
+ MediaInfo = NULL;
+
+ //
+ // Get SNP handle
+ //
+ Snp = NULL;
+ SnpHandle = NetLibGetSnpHandle (ServiceHandle, &Snp);
+ if (SnpHandle == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = gBS->HandleProtocol (
+ SnpHandle,
+ &gEfiAdapterInformationProtocolGuid,
+ (VOID *) &Aip
+ );
+ if (EFI_ERROR (Status)) {
+
+ MediaPresent = TRUE;
+ Status = NetLibDetectMedia (ServiceHandle, &MediaPresent);
+ if (!EFI_ERROR (Status)) {
+ if (MediaPresent) {
+ *MediaState = EFI_SUCCESS;
+ } else {
+ *MediaState = EFI_NO_MEDIA;
+ }
+ }
+
+ //
+ // NetLibDetectMedia doesn't support EFI_NOT_READY status, return now!
+ //
+ return Status;
+ }
+
+ Status = Aip->GetInformation (
+ Aip,
+ &gEfiAdapterInfoMediaStateGuid,
+ (VOID **) &MediaInfo,
+ &DataSize
+ );
+ if (!EFI_ERROR (Status)) {
+
+ *MediaState = MediaInfo->MediaState;
+ FreePool (MediaInfo);
+ if (*MediaState != EFI_NOT_READY || Timeout < MEDIA_STATE_DETECT_TIME_INTERVAL) {
+
+ return EFI_SUCCESS;
+ }
+ } else {
+
+ if (MediaInfo != NULL) {
+ FreePool (MediaInfo);
+ }
+
+ if (Status == EFI_UNSUPPORTED) {
+
+ //
+ // If gEfiAdapterInfoMediaStateGuid is not supported, call NetLibDetectMedia to get media state!
+ //
+ MediaPresent = TRUE;
+ Status = NetLibDetectMedia (ServiceHandle, &MediaPresent);
+ if (!EFI_ERROR (Status)) {
+ if (MediaPresent) {
+ *MediaState = EFI_SUCCESS;
+ } else {
+ *MediaState = EFI_NO_MEDIA;
+ }
+ }
+ return Status;
+ }
+
+ return Status;
+ }
+
+ //
+ // Loop to check media state
+ //
+
+ Timer = NULL;
+ TimeRemained = Timeout;
+ Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ do {
+ Status = gBS->SetTimer (
+ Timer,
+ TimerRelative,
+ MEDIA_STATE_DETECT_TIME_INTERVAL
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent(Timer);
+ return EFI_DEVICE_ERROR;
+ }
+
+ do {
+ TimerStatus = gBS->CheckEvent (Timer);
+ if (!EFI_ERROR (TimerStatus)) {
+
+ TimeRemained -= MEDIA_STATE_DETECT_TIME_INTERVAL;
+ Status = Aip->GetInformation (
+ Aip,
+ &gEfiAdapterInfoMediaStateGuid,
+ (VOID **) &MediaInfo,
+ &DataSize
+ );
+ if (!EFI_ERROR (Status)) {
+
+ *MediaState = MediaInfo->MediaState;
+ FreePool (MediaInfo);
+ } else {
+
+ if (MediaInfo != NULL) {
+ FreePool (MediaInfo);
+ }
+ gBS->CloseEvent(Timer);
+ return Status;
+ }
+ }
+ } while (TimerStatus == EFI_NOT_READY);
+ } while (*MediaState == EFI_NOT_READY && TimeRemained >= MEDIA_STATE_DETECT_TIME_INTERVAL);
+
+ gBS->CloseEvent(Timer);
+ if (*MediaState == EFI_NOT_READY && TimeRemained < MEDIA_STATE_DETECT_TIME_INTERVAL) {
+ return EFI_TIMEOUT;
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+/**
+ Check the default address used by the IPv4 driver is static or dynamic (acquired
+ from DHCP).
+
+ If the controller handle does not have the EFI_IP4_CONFIG2_PROTOCOL installed, the
+ default address is static. If failed to get the policy from Ip4 Config2 Protocol,
+ the default address is static. Otherwise, get the result from Ip4 Config2 Protocol.
+
+ @param[in] Controller The controller handle which has the EFI_IP4_CONFIG2_PROTOCOL
+ relative with the default address to judge.
+
+ @retval TRUE If the default address is static.
+ @retval FALSE If the default address is acquired from DHCP.
+
+**/
+BOOLEAN
+NetLibDefaultAddressIsStatic (
+ IN EFI_HANDLE Controller
+ )
+{
+ EFI_STATUS Status;
+ EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2;
+ UINTN DataSize;
+ EFI_IP4_CONFIG2_POLICY Policy;
+ BOOLEAN IsStatic;
+
+ Ip4Config2 = NULL;
+
+ DataSize = sizeof (EFI_IP4_CONFIG2_POLICY);
+
+ IsStatic = TRUE;
+
+ //
+ // Get Ip4Config2 policy.
+ //
+ Status = gBS->HandleProtocol (Controller, &gEfiIp4Config2ProtocolGuid, (VOID **) &Ip4Config2);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypePolicy, &DataSize, &Policy);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ IsStatic = (BOOLEAN) (Policy == Ip4Config2PolicyStatic);
+
+ON_EXIT:
+
+ return IsStatic;
+}
+
+/**
+ Create an IPv4 device path node.
+
+ If Node is NULL, then ASSERT().
+
+ The header type of IPv4 device path node is MESSAGING_DEVICE_PATH.
+ The header subtype of IPv4 device path node is MSG_IPv4_DP.
+ Get other info from parameters to make up the whole IPv4 device path node.
+
+ @param[in, out] Node Pointer to the IPv4 device path node.
+ @param[in] Controller The controller handle.
+ @param[in] LocalIp The local IPv4 address.
+ @param[in] LocalPort The local port.
+ @param[in] RemoteIp The remote IPv4 address.
+ @param[in] RemotePort The remote port.
+ @param[in] Protocol The protocol type in the IP header.
+ @param[in] UseDefaultAddress Whether this instance is using default address or not.
+
+**/
+VOID
+EFIAPI
+NetLibCreateIPv4DPathNode (
+ IN OUT IPv4_DEVICE_PATH *Node,
+ IN EFI_HANDLE Controller,
+ IN IP4_ADDR LocalIp,
+ IN UINT16 LocalPort,
+ IN IP4_ADDR RemoteIp,
+ IN UINT16 RemotePort,
+ IN UINT16 Protocol,
+ IN BOOLEAN UseDefaultAddress
+ )
+{
+ ASSERT (Node != NULL);
+
+ Node->Header.Type = MESSAGING_DEVICE_PATH;
+ Node->Header.SubType = MSG_IPv4_DP;
+ SetDevicePathNodeLength (&Node->Header, sizeof (IPv4_DEVICE_PATH));
+
+ CopyMem (&Node->LocalIpAddress, &LocalIp, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Node->RemoteIpAddress, &RemoteIp, sizeof (EFI_IPv4_ADDRESS));
+
+ Node->LocalPort = LocalPort;
+ Node->RemotePort = RemotePort;
+
+ Node->Protocol = Protocol;
+
+ if (!UseDefaultAddress) {
+ Node->StaticIpAddress = TRUE;
+ } else {
+ Node->StaticIpAddress = NetLibDefaultAddressIsStatic (Controller);
+ }
+
+ //
+ // Set the Gateway IP address to default value 0:0:0:0.
+ // Set the Subnet mask to default value 255:255:255:0.
+ //
+ ZeroMem (&Node->GatewayIpAddress, sizeof (EFI_IPv4_ADDRESS));
+ SetMem (&Node->SubnetMask, sizeof (EFI_IPv4_ADDRESS), 0xff);
+ Node->SubnetMask.Addr[3] = 0;
+}
+
+/**
+ Create an IPv6 device path node.
+
+ If Node is NULL, then ASSERT().
+ If LocalIp is NULL, then ASSERT().
+ If RemoteIp is NULL, then ASSERT().
+
+ The header type of IPv6 device path node is MESSAGING_DEVICE_PATH.
+ The header subtype of IPv6 device path node is MSG_IPv6_DP.
+ Get other info from parameters to make up the whole IPv6 device path node.
+
+ @param[in, out] Node Pointer to the IPv6 device path node.
+ @param[in] Controller The controller handle.
+ @param[in] LocalIp The local IPv6 address.
+ @param[in] LocalPort The local port.
+ @param[in] RemoteIp The remote IPv6 address.
+ @param[in] RemotePort The remote port.
+ @param[in] Protocol The protocol type in the IP header.
+
+**/
+VOID
+EFIAPI
+NetLibCreateIPv6DPathNode (
+ IN OUT IPv6_DEVICE_PATH *Node,
+ IN EFI_HANDLE Controller,
+ IN EFI_IPv6_ADDRESS *LocalIp,
+ IN UINT16 LocalPort,
+ IN EFI_IPv6_ADDRESS *RemoteIp,
+ IN UINT16 RemotePort,
+ IN UINT16 Protocol
+ )
+{
+ ASSERT (Node != NULL && LocalIp != NULL && RemoteIp != NULL);
+
+ Node->Header.Type = MESSAGING_DEVICE_PATH;
+ Node->Header.SubType = MSG_IPv6_DP;
+ SetDevicePathNodeLength (&Node->Header, sizeof (IPv6_DEVICE_PATH));
+
+ CopyMem (&Node->LocalIpAddress, LocalIp, sizeof (EFI_IPv6_ADDRESS));
+ CopyMem (&Node->RemoteIpAddress, RemoteIp, sizeof (EFI_IPv6_ADDRESS));
+
+ Node->LocalPort = LocalPort;
+ Node->RemotePort = RemotePort;
+
+ Node->Protocol = Protocol;
+
+ //
+ // Set default value to IPAddressOrigin, PrefixLength.
+ // Set the Gateway IP address to unspecified address.
+ //
+ Node->IpAddressOrigin = 0;
+ Node->PrefixLength = IP6_PREFIX_LENGTH;
+ ZeroMem (&Node->GatewayIpAddress, sizeof (EFI_IPv6_ADDRESS));
+}
+
+/**
+ Find the UNDI/SNP handle from controller and protocol GUID.
+
+ If ProtocolGuid is NULL, then ASSERT().
+
+ For example, IP will open a MNP child to transmit/receive
+ packets, when MNP is stopped, IP should also be stopped. IP
+ needs to find its own private data which is related the IP's
+ service binding instance that is install on UNDI/SNP handle.
+ Now, the controller is either a MNP or ARP child handle. But
+ IP opens these handle BY_DRIVER, use that info, we can get the
+ UNDI/SNP handle.
+
+ @param[in] Controller Then protocol handle to check.
+ @param[in] ProtocolGuid The protocol that is related with the handle.
+
+ @return The UNDI/SNP handle or NULL for errors.
+
+**/
+EFI_HANDLE
+EFIAPI
+NetLibGetNicHandle (
+ IN EFI_HANDLE Controller,
+ IN EFI_GUID *ProtocolGuid
+ )
+{
+ EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenBuffer;
+ EFI_HANDLE Handle;
+ EFI_STATUS Status;
+ UINTN OpenCount;
+ UINTN Index;
+
+ ASSERT (ProtocolGuid != NULL);
+
+ Status = gBS->OpenProtocolInformation (
+ Controller,
+ ProtocolGuid,
+ &OpenBuffer,
+ &OpenCount
+ );
+
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ Handle = NULL;
+
+ for (Index = 0; Index < OpenCount; Index++) {
+ if ((OpenBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) != 0) {
+ Handle = OpenBuffer[Index].ControllerHandle;
+ break;
+ }
+ }
+
+ gBS->FreePool (OpenBuffer);
+ return Handle;
+}
+
+/**
+ Convert one Null-terminated ASCII string (decimal dotted) to EFI_IPv4_ADDRESS.
+
+ @param[in] String The pointer to the Ascii string.
+ @param[out] Ip4Address The pointer to the converted IPv4 address.
+
+ @retval EFI_SUCCESS Convert to IPv4 address successfully.
+ @retval EFI_INVALID_PARAMETER The string is mal-formated or Ip4Address is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibAsciiStrToIp4 (
+ IN CONST CHAR8 *String,
+ OUT EFI_IPv4_ADDRESS *Ip4Address
+ )
+{
+ RETURN_STATUS Status;
+ CHAR8 *EndPointer;
+
+ Status = AsciiStrToIpv4Address (String, &EndPointer, Ip4Address, NULL);
+ if (RETURN_ERROR (Status) || (*EndPointer != '\0')) {
+ return EFI_INVALID_PARAMETER;
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+
+/**
+ Convert one Null-terminated ASCII string to EFI_IPv6_ADDRESS. The format of the
+ string is defined in RFC 4291 - Text Representation of Addresses.
+
+ @param[in] String The pointer to the Ascii string.
+ @param[out] Ip6Address The pointer to the converted IPv6 address.
+
+ @retval EFI_SUCCESS Convert to IPv6 address successfully.
+ @retval EFI_INVALID_PARAMETER The string is mal-formated or Ip6Address is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibAsciiStrToIp6 (
+ IN CONST CHAR8 *String,
+ OUT EFI_IPv6_ADDRESS *Ip6Address
+ )
+{
+ RETURN_STATUS Status;
+ CHAR8 *EndPointer;
+
+ Status = AsciiStrToIpv6Address (String, &EndPointer, Ip6Address, NULL);
+ if (RETURN_ERROR (Status) || (*EndPointer != '\0')) {
+ return EFI_INVALID_PARAMETER;
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+
+/**
+ Convert one Null-terminated Unicode string (decimal dotted) to EFI_IPv4_ADDRESS.
+
+ @param[in] String The pointer to the Ascii string.
+ @param[out] Ip4Address The pointer to the converted IPv4 address.
+
+ @retval EFI_SUCCESS Convert to IPv4 address successfully.
+ @retval EFI_INVALID_PARAMETER The string is mal-formated or Ip4Address is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibStrToIp4 (
+ IN CONST CHAR16 *String,
+ OUT EFI_IPv4_ADDRESS *Ip4Address
+ )
+{
+ RETURN_STATUS Status;
+ CHAR16 *EndPointer;
+
+ Status = StrToIpv4Address (String, &EndPointer, Ip4Address, NULL);
+ if (RETURN_ERROR (Status) || (*EndPointer != L'\0')) {
+ return EFI_INVALID_PARAMETER;
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+
+/**
+ Convert one Null-terminated Unicode string to EFI_IPv6_ADDRESS. The format of
+ the string is defined in RFC 4291 - Text Representation of Addresses.
+
+ @param[in] String The pointer to the Ascii string.
+ @param[out] Ip6Address The pointer to the converted IPv6 address.
+
+ @retval EFI_SUCCESS Convert to IPv6 address successfully.
+ @retval EFI_INVALID_PARAMETER The string is mal-formated or Ip6Address is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibStrToIp6 (
+ IN CONST CHAR16 *String,
+ OUT EFI_IPv6_ADDRESS *Ip6Address
+ )
+{
+ RETURN_STATUS Status;
+ CHAR16 *EndPointer;
+
+ Status = StrToIpv6Address (String, &EndPointer, Ip6Address, NULL);
+ if (RETURN_ERROR (Status) || (*EndPointer != L'\0')) {
+ return EFI_INVALID_PARAMETER;
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+/**
+ Convert one Null-terminated Unicode string to EFI_IPv6_ADDRESS and prefix length.
+ The format of the string is defined in RFC 4291 - Text Representation of Addresses
+ Prefixes: ipv6-address/prefix-length.
+
+ @param[in] String The pointer to the Ascii string.
+ @param[out] Ip6Address The pointer to the converted IPv6 address.
+ @param[out] PrefixLength The pointer to the converted prefix length.
+
+ @retval EFI_SUCCESS Convert to IPv6 address successfully.
+ @retval EFI_INVALID_PARAMETER The string is mal-formated or Ip6Address is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibStrToIp6andPrefix (
+ IN CONST CHAR16 *String,
+ OUT EFI_IPv6_ADDRESS *Ip6Address,
+ OUT UINT8 *PrefixLength
+ )
+{
+ RETURN_STATUS Status;
+ CHAR16 *EndPointer;
+
+ Status = StrToIpv6Address (String, &EndPointer, Ip6Address, PrefixLength);
+ if (RETURN_ERROR (Status) || (*EndPointer != L'\0')) {
+ return EFI_INVALID_PARAMETER;
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+/**
+
+ Convert one EFI_IPv6_ADDRESS to Null-terminated Unicode string.
+ The text representation of address is defined in RFC 4291.
+
+ @param[in] Ip6Address The pointer to the IPv6 address.
+ @param[out] String The buffer to return the converted string.
+ @param[in] StringSize The length in bytes of the input String.
+
+ @retval EFI_SUCCESS Convert to string successfully.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+ @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small for the result. BufferSize has been
+ updated with the size needed to complete the request.
+**/
+EFI_STATUS
+EFIAPI
+NetLibIp6ToStr (
+ IN EFI_IPv6_ADDRESS *Ip6Address,
+ OUT CHAR16 *String,
+ IN UINTN StringSize
+ )
+{
+ UINT16 Ip6Addr[8];
+ UINTN Index;
+ UINTN LongestZerosStart;
+ UINTN LongestZerosLength;
+ UINTN CurrentZerosStart;
+ UINTN CurrentZerosLength;
+ CHAR16 Buffer[sizeof"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"];
+ CHAR16 *Ptr;
+
+ if (Ip6Address == NULL || String == NULL || StringSize == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Convert the UINT8 array to an UINT16 array for easy handling.
+ //
+ ZeroMem (Ip6Addr, sizeof (Ip6Addr));
+ for (Index = 0; Index < 16; Index++) {
+ Ip6Addr[Index / 2] |= (Ip6Address->Addr[Index] << ((1 - (Index % 2)) << 3));
+ }
+
+ //
+ // Find the longest zeros and mark it.
+ //
+ CurrentZerosStart = DEFAULT_ZERO_START;
+ CurrentZerosLength = 0;
+ LongestZerosStart = DEFAULT_ZERO_START;
+ LongestZerosLength = 0;
+ for (Index = 0; Index < 8; Index++) {
+ if (Ip6Addr[Index] == 0) {
+ if (CurrentZerosStart == DEFAULT_ZERO_START) {
+ CurrentZerosStart = Index;
+ CurrentZerosLength = 1;
+ } else {
+ CurrentZerosLength++;
+ }
+ } else {
+ if (CurrentZerosStart != DEFAULT_ZERO_START) {
+ if (CurrentZerosLength > 2 && (LongestZerosStart == (DEFAULT_ZERO_START) || CurrentZerosLength > LongestZerosLength)) {
+ LongestZerosStart = CurrentZerosStart;
+ LongestZerosLength = CurrentZerosLength;
+ }
+ CurrentZerosStart = DEFAULT_ZERO_START;
+ CurrentZerosLength = 0;
+ }
+ }
+ }
+
+ if (CurrentZerosStart != DEFAULT_ZERO_START && CurrentZerosLength > 2) {
+ if (LongestZerosStart == DEFAULT_ZERO_START || LongestZerosLength < CurrentZerosLength) {
+ LongestZerosStart = CurrentZerosStart;
+ LongestZerosLength = CurrentZerosLength;
+ }
+ }
+
+ Ptr = Buffer;
+ for (Index = 0; Index < 8; Index++) {
+ if (LongestZerosStart != DEFAULT_ZERO_START && Index >= LongestZerosStart && Index < LongestZerosStart + LongestZerosLength) {
+ if (Index == LongestZerosStart) {
+ *Ptr++ = L':';
+ }
+ continue;
+ }
+ if (Index != 0) {
+ *Ptr++ = L':';
+ }
+ Ptr += UnicodeSPrint(Ptr, 10, L"%x", Ip6Addr[Index]);
+ }
+
+ if (LongestZerosStart != DEFAULT_ZERO_START && LongestZerosStart + LongestZerosLength == 8) {
+ *Ptr++ = L':';
+ }
+ *Ptr = L'\0';
+
+ if ((UINTN)Ptr - (UINTN)Buffer > StringSize) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ StrCpyS (String, StringSize / sizeof (CHAR16), Buffer);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function obtains the system guid from the smbios table.
+
+ If SystemGuid is NULL, then ASSERT().
+
+ @param[out] SystemGuid The pointer of the returned system guid.
+
+ @retval EFI_SUCCESS Successfully obtained the system guid.
+ @retval EFI_NOT_FOUND Did not find the SMBIOS table.
+
+**/
+EFI_STATUS
+EFIAPI
+NetLibGetSystemGuid (
+ OUT EFI_GUID *SystemGuid
+ )
+{
+ EFI_STATUS Status;
+ SMBIOS_TABLE_ENTRY_POINT *SmbiosTable;
+ SMBIOS_TABLE_3_0_ENTRY_POINT *Smbios30Table;
+ SMBIOS_STRUCTURE_POINTER Smbios;
+ SMBIOS_STRUCTURE_POINTER SmbiosEnd;
+ CHAR8 *String;
+
+ ASSERT (SystemGuid != NULL);
+
+ SmbiosTable = NULL;
+ Status = EfiGetSystemConfigurationTable (&gEfiSmbios3TableGuid, (VOID **) &Smbios30Table);
+ if (!(EFI_ERROR (Status) || Smbios30Table == NULL)) {
+ Smbios.Hdr = (SMBIOS_STRUCTURE *) (UINTN) Smbios30Table->TableAddress;
+ SmbiosEnd.Raw = (UINT8 *) (UINTN) (Smbios30Table->TableAddress + Smbios30Table->TableMaximumSize);
+ } else {
+ Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SmbiosTable);
+ if (EFI_ERROR (Status) || SmbiosTable == NULL) {
+ return EFI_NOT_FOUND;
+ }
+ Smbios.Hdr = (SMBIOS_STRUCTURE *) (UINTN) SmbiosTable->TableAddress;
+ SmbiosEnd.Raw = (UINT8 *) ((UINTN) SmbiosTable->TableAddress + SmbiosTable->TableLength);
+ }
+
+ do {
+ if (Smbios.Hdr->Type == 1) {
+ if (Smbios.Hdr->Length < 0x19) {
+ //
+ // Older version did not support UUID.
+ //
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // SMBIOS tables are byte packed so we need to do a byte copy to
+ // prevend alignment faults on Itanium-based platform.
+ //
+ CopyMem (SystemGuid, &Smbios.Type1->Uuid, sizeof (EFI_GUID));
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Go to the next SMBIOS structure. Each SMBIOS structure may include 2 parts:
+ // 1. Formatted section; 2. Unformatted string section. So, 2 steps are needed
+ // to skip one SMBIOS structure.
+ //
+
+ //
+ // Step 1: Skip over formatted section.
+ //
+ String = (CHAR8 *) (Smbios.Raw + Smbios.Hdr->Length);
+
+ //
+ // Step 2: Skip over unformated string section.
+ //
+ do {
+ //
+ // Each string is terminated with a NULL(00h) BYTE and the sets of strings
+ // is terminated with an additional NULL(00h) BYTE.
+ //
+ for ( ; *String != 0; String++) {
+ }
+
+ if (*(UINT8*)++String == 0) {
+ //
+ // Pointer to the next SMBIOS structure.
+ //
+ Smbios.Raw = (UINT8 *)++String;
+ break;
+ }
+ } while (TRUE);
+ } while (Smbios.Raw < SmbiosEnd.Raw);
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Create Dns QName according the queried domain name.
+
+ If DomainName is NULL, then ASSERT().
+
+ QName is a domain name represented as a sequence of labels,
+ where each label consists of a length octet followed by that
+ number of octets. The QName terminates with the zero
+ length octet for the null label of the root. Caller should
+ take responsibility to free the buffer in returned pointer.
+
+ @param DomainName The pointer to the queried domain name string.
+
+ @retval NULL Failed to fill QName.
+ @return QName filled successfully.
+
+**/
+CHAR8 *
+EFIAPI
+NetLibCreateDnsQName (
+ IN CHAR16 *DomainName
+ )
+{
+ CHAR8 *QueryName;
+ UINTN QueryNameSize;
+ CHAR8 *Header;
+ CHAR8 *Tail;
+ UINTN Len;
+ UINTN Index;
+
+ ASSERT (DomainName != NULL);
+
+ QueryName = NULL;
+ QueryNameSize = 0;
+ Header = NULL;
+ Tail = NULL;
+
+ //
+ // One byte for first label length, one byte for terminated length zero.
+ //
+ QueryNameSize = StrLen (DomainName) + 2;
+
+ if (QueryNameSize > DNS_MAX_NAME_SIZE) {
+ return NULL;
+ }
+
+ QueryName = AllocateZeroPool (QueryNameSize);
+ if (QueryName == NULL) {
+ return NULL;
+ }
+
+ Header = QueryName;
+ Tail = Header + 1;
+ Len = 0;
+ for (Index = 0; DomainName[Index] != 0; Index++) {
+ *Tail = (CHAR8) DomainName[Index];
+ if (*Tail == '.') {
+ *Header = (CHAR8) Len;
+ Header = Tail;
+ Tail ++;
+ Len = 0;
+ } else {
+ Tail++;
+ Len++;
+ }
+ }
+ *Header = (CHAR8) Len;
+ *Tail = 0;
+
+ return QueryName;
+}
diff --git a/NetworkPkg/Library/DxeNetLib/DxeNetLib.inf b/NetworkPkg/Library/DxeNetLib/DxeNetLib.inf
new file mode 100644
index 0000000000..35a8247517
--- /dev/null
+++ b/NetworkPkg/Library/DxeNetLib/DxeNetLib.inf
@@ -0,0 +1,61 @@
+## @file
+# This library instance provides the basic network services.
+#
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+# (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxeNetLib
+ MODULE_UNI_FILE = DxeNetLib.uni
+ FILE_GUID = db6dcef3-9f4e-4340-9351-fc35aa8a5888
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = NetLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ DxeNetLib.c
+ NetBuffer.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ BaseMemoryLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ UefiLib
+ MemoryAllocationLib
+ DevicePathLib
+ PrintLib
+
+
+[Guids]
+ gEfiSmbiosTableGuid ## SOMETIMES_CONSUMES ## SystemTable
+ gEfiSmbios3TableGuid ## SOMETIMES_CONSUMES ## SystemTable
+ gEfiAdapterInfoMediaStateGuid ## SOMETIMES_CONSUMES
+
+
+[Protocols]
+ gEfiSimpleNetworkProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiManagedNetworkProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiManagedNetworkServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiIp4Config2ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiComponentNameProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiComponentName2ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiAdapterInformationProtocolGuid ## SOMETIMES_CONSUMES
diff --git a/NetworkPkg/Library/DxeNetLib/DxeNetLib.uni b/NetworkPkg/Library/DxeNetLib/DxeNetLib.uni
new file mode 100644
index 0000000000..0c9f1fddde
--- /dev/null
+++ b/NetworkPkg/Library/DxeNetLib/DxeNetLib.uni
@@ -0,0 +1,16 @@
+// /** @file
+// This library instance provides the basic network services.
+//
+// This library instance provides the basic network services.
+//
+// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Provides the basic network services"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This library instance provides the basic network services."
+
diff --git a/NetworkPkg/Library/DxeNetLib/NetBuffer.c b/NetworkPkg/Library/DxeNetLib/NetBuffer.c
new file mode 100644
index 0000000000..2408e9a104
--- /dev/null
+++ b/NetworkPkg/Library/DxeNetLib/NetBuffer.c
@@ -0,0 +1,1890 @@
+/** @file
+ Network library functions providing net buffer operation support.
+
+Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <Library/NetLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+
+/**
+ Allocate and build up the sketch for a NET_BUF.
+
+ The net buffer allocated has the BlockOpNum's NET_BLOCK_OP, and its associated
+ NET_VECTOR has the BlockNum's NET_BLOCK. But all the NET_BLOCK_OP and
+ NET_BLOCK remain un-initialized.
+
+ @param[in] BlockNum The number of NET_BLOCK in the vector of net buffer
+ @param[in] BlockOpNum The number of NET_BLOCK_OP in the net buffer
+
+ @return Pointer to the allocated NET_BUF, or NULL if the
+ allocation failed due to resource limit.
+
+**/
+NET_BUF *
+NetbufAllocStruct (
+ IN UINT32 BlockNum,
+ IN UINT32 BlockOpNum
+ )
+{
+ NET_BUF *Nbuf;
+ NET_VECTOR *Vector;
+
+ ASSERT (BlockOpNum >= 1);
+
+ //
+ // Allocate three memory blocks.
+ //
+ Nbuf = AllocateZeroPool (NET_BUF_SIZE (BlockOpNum));
+
+ if (Nbuf == NULL) {
+ return NULL;
+ }
+
+ Nbuf->Signature = NET_BUF_SIGNATURE;
+ Nbuf->RefCnt = 1;
+ Nbuf->BlockOpNum = BlockOpNum;
+ InitializeListHead (&Nbuf->List);
+
+ if (BlockNum != 0) {
+ Vector = AllocateZeroPool (NET_VECTOR_SIZE (BlockNum));
+
+ if (Vector == NULL) {
+ goto FreeNbuf;
+ }
+
+ Vector->Signature = NET_VECTOR_SIGNATURE;
+ Vector->RefCnt = 1;
+ Vector->BlockNum = BlockNum;
+ Nbuf->Vector = Vector;
+ }
+
+ return Nbuf;
+
+FreeNbuf:
+
+ FreePool (Nbuf);
+ return NULL;
+}
+
+
+/**
+ Allocate a single block NET_BUF. Upon allocation, all the
+ free space is in the tail room.
+
+ @param[in] Len The length of the block.
+
+ @return Pointer to the allocated NET_BUF, or NULL if the
+ allocation failed due to resource limit.
+
+**/
+NET_BUF *
+EFIAPI
+NetbufAlloc (
+ IN UINT32 Len
+ )
+{
+ NET_BUF *Nbuf;
+ NET_VECTOR *Vector;
+ UINT8 *Bulk;
+
+ ASSERT (Len > 0);
+
+ Nbuf = NetbufAllocStruct (1, 1);
+
+ if (Nbuf == NULL) {
+ return NULL;
+ }
+
+ Bulk = AllocatePool (Len);
+
+ if (Bulk == NULL) {
+ goto FreeNBuf;
+ }
+
+ Vector = Nbuf->Vector;
+ Vector->Len = Len;
+
+ Vector->Block[0].Bulk = Bulk;
+ Vector->Block[0].Len = Len;
+
+ Nbuf->BlockOp[0].BlockHead = Bulk;
+ Nbuf->BlockOp[0].BlockTail = Bulk + Len;
+
+ Nbuf->BlockOp[0].Head = Bulk;
+ Nbuf->BlockOp[0].Tail = Bulk;
+ Nbuf->BlockOp[0].Size = 0;
+
+ return Nbuf;
+
+FreeNBuf:
+ FreePool (Nbuf);
+ return NULL;
+}
+
+/**
+ Free the net vector.
+
+ Decrease the reference count of the net vector by one. The real resource free
+ operation isn't performed until the reference count of the net vector is
+ decreased to 0.
+
+ @param[in] Vector Pointer to the NET_VECTOR to be freed.
+
+**/
+VOID
+NetbufFreeVector (
+ IN NET_VECTOR *Vector
+ )
+{
+ UINT32 Index;
+
+ ASSERT (Vector != NULL);
+ NET_CHECK_SIGNATURE (Vector, NET_VECTOR_SIGNATURE);
+ ASSERT (Vector->RefCnt > 0);
+
+ Vector->RefCnt--;
+
+ if (Vector->RefCnt > 0) {
+ return;
+ }
+
+ if (Vector->Free != NULL) {
+ //
+ // Call external free function to free the vector if it
+ // isn't NULL. If NET_VECTOR_OWN_FIRST is set, release the
+ // first block since it is allocated by us
+ //
+ if ((Vector->Flag & NET_VECTOR_OWN_FIRST) != 0) {
+ gBS->FreePool (Vector->Block[0].Bulk);
+ }
+
+ Vector->Free (Vector->Arg);
+
+ } else {
+ //
+ // Free each memory block associated with the Vector
+ //
+ for (Index = 0; Index < Vector->BlockNum; Index++) {
+ gBS->FreePool (Vector->Block[Index].Bulk);
+ }
+ }
+
+ FreePool (Vector);
+}
+
+
+/**
+ Free the net buffer and its associated NET_VECTOR.
+
+ Decrease the reference count of the net buffer by one. Free the associated net
+ vector and itself if the reference count of the net buffer is decreased to 0.
+ The net vector free operation just decrease the reference count of the net
+ vector by one and do the real resource free operation when the reference count
+ of the net vector is 0.
+
+ @param[in] Nbuf Pointer to the NET_BUF to be freed.
+
+**/
+VOID
+EFIAPI
+NetbufFree (
+ IN NET_BUF *Nbuf
+ )
+{
+ ASSERT (Nbuf != NULL);
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ ASSERT (Nbuf->RefCnt > 0);
+
+ Nbuf->RefCnt--;
+
+ if (Nbuf->RefCnt == 0) {
+ //
+ // Update Vector only when NBuf is to be released. That is,
+ // all the sharing of Nbuf increse Vector's RefCnt by one
+ //
+ NetbufFreeVector (Nbuf->Vector);
+ FreePool (Nbuf);
+ }
+}
+
+
+/**
+ Create a copy of the net buffer that shares the associated net vector.
+
+ The reference count of the newly created net buffer is set to 1. The reference
+ count of the associated net vector is increased by one.
+
+ @param[in] Nbuf Pointer to the net buffer to be cloned.
+
+ @return Pointer to the cloned net buffer, or NULL if the
+ allocation failed due to resource limit.
+
+**/
+NET_BUF *
+EFIAPI
+NetbufClone (
+ IN NET_BUF *Nbuf
+ )
+{
+ NET_BUF *Clone;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ Clone = AllocatePool (NET_BUF_SIZE (Nbuf->BlockOpNum));
+
+ if (Clone == NULL) {
+ return NULL;
+ }
+
+ Clone->Signature = NET_BUF_SIGNATURE;
+ Clone->RefCnt = 1;
+ InitializeListHead (&Clone->List);
+
+ Clone->Ip = Nbuf->Ip;
+ Clone->Tcp = Nbuf->Tcp;
+
+ CopyMem (Clone->ProtoData, Nbuf->ProtoData, NET_PROTO_DATA);
+
+ NET_GET_REF (Nbuf->Vector);
+
+ Clone->Vector = Nbuf->Vector;
+ Clone->BlockOpNum = Nbuf->BlockOpNum;
+ Clone->TotalSize = Nbuf->TotalSize;
+ CopyMem (Clone->BlockOp, Nbuf->BlockOp, sizeof (NET_BLOCK_OP) * Nbuf->BlockOpNum);
+
+ return Clone;
+}
+
+
+/**
+ Create a duplicated copy of the net buffer with data copied and HeadSpace
+ bytes of head space reserved.
+
+ The duplicated net buffer will allocate its own memory to hold the data of the
+ source net buffer.
+
+ @param[in] Nbuf Pointer to the net buffer to be duplicated from.
+ @param[in, out] Duplicate Pointer to the net buffer to duplicate to, if
+ NULL a new net buffer is allocated.
+ @param[in] HeadSpace Length of the head space to reserve.
+
+ @return Pointer to the duplicated net buffer, or NULL if
+ the allocation failed due to resource limit.
+
+**/
+NET_BUF *
+EFIAPI
+NetbufDuplicate (
+ IN NET_BUF *Nbuf,
+ IN OUT NET_BUF *Duplicate OPTIONAL,
+ IN UINT32 HeadSpace
+ )
+{
+ UINT8 *Dst;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ if (Duplicate == NULL) {
+ Duplicate = NetbufAlloc (Nbuf->TotalSize + HeadSpace);
+ }
+
+ if (Duplicate == NULL) {
+ return NULL;
+ }
+
+ //
+ // Don't set the IP and TCP head point, since it is most
+ // like that they are pointing to the memory of Nbuf.
+ //
+ CopyMem (Duplicate->ProtoData, Nbuf->ProtoData, NET_PROTO_DATA);
+ NetbufReserve (Duplicate, HeadSpace);
+
+ Dst = NetbufAllocSpace (Duplicate, Nbuf->TotalSize, NET_BUF_TAIL);
+ NetbufCopy (Nbuf, 0, Nbuf->TotalSize, Dst);
+
+ return Duplicate;
+}
+
+
+/**
+ Free a list of net buffers.
+
+ @param[in, out] Head Pointer to the head of linked net buffers.
+
+**/
+VOID
+EFIAPI
+NetbufFreeList (
+ IN OUT LIST_ENTRY *Head
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ NET_BUF *Nbuf;
+
+ Entry = Head->ForwardLink;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ RemoveEntryList (Entry);
+ NetbufFree (Nbuf);
+ }
+
+ ASSERT (IsListEmpty (Head));
+}
+
+
+/**
+ Get the index of NET_BLOCK_OP that contains the byte at Offset in the net
+ buffer.
+
+ This can be used to, for example, retrieve the IP header in the packet. It
+ also can be used to get the fragment that contains the byte which is used
+ mainly by the library implementation itself.
+
+ @param[in] Nbuf Pointer to the net buffer.
+ @param[in] Offset The offset of the byte.
+ @param[out] Index Index of the NET_BLOCK_OP that contains the byte at
+ Offset.
+
+ @return Pointer to the Offset'th byte of data in the net buffer, or NULL
+ if there is no such data in the net buffer.
+
+**/
+UINT8 *
+EFIAPI
+NetbufGetByte (
+ IN NET_BUF *Nbuf,
+ IN UINT32 Offset,
+ OUT UINT32 *Index OPTIONAL
+ )
+{
+ NET_BLOCK_OP *BlockOp;
+ UINT32 Loop;
+ UINT32 Len;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ if (Offset >= Nbuf->TotalSize) {
+ return NULL;
+ }
+
+ BlockOp = Nbuf->BlockOp;
+ Len = 0;
+
+ for (Loop = 0; Loop < Nbuf->BlockOpNum; Loop++) {
+
+ if (Len + BlockOp[Loop].Size <= Offset) {
+ Len += BlockOp[Loop].Size;
+ continue;
+ }
+
+ if (Index != NULL) {
+ *Index = Loop;
+ }
+
+ return BlockOp[Loop].Head + (Offset - Len);
+ }
+
+ return NULL;
+}
+
+
+
+/**
+ Set the NET_BLOCK and corresponding NET_BLOCK_OP in the net buffer and
+ corresponding net vector according to the bulk pointer and bulk length.
+
+ All the pointers in the Index'th NET_BLOCK and NET_BLOCK_OP are set to the
+ bulk's head and tail respectively. So, this function alone can't be used by
+ NetbufAlloc.
+
+ @param[in, out] Nbuf Pointer to the net buffer.
+ @param[in] Bulk Pointer to the data.
+ @param[in] Len Length of the bulk data.
+ @param[in] Index The data block index in the net buffer the bulk
+ data should belong to.
+
+**/
+VOID
+NetbufSetBlock (
+ IN OUT NET_BUF *Nbuf,
+ IN UINT8 *Bulk,
+ IN UINT32 Len,
+ IN UINT32 Index
+ )
+{
+ NET_BLOCK_OP *BlockOp;
+ NET_BLOCK *Block;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ NET_CHECK_SIGNATURE (Nbuf->Vector, NET_VECTOR_SIGNATURE);
+ ASSERT (Index < Nbuf->BlockOpNum);
+
+ Block = &(Nbuf->Vector->Block[Index]);
+ BlockOp = &(Nbuf->BlockOp[Index]);
+ Block->Len = Len;
+ Block->Bulk = Bulk;
+ BlockOp->BlockHead = Bulk;
+ BlockOp->BlockTail = Bulk + Len;
+ BlockOp->Head = Bulk;
+ BlockOp->Tail = Bulk + Len;
+ BlockOp->Size = Len;
+}
+
+
+
+/**
+ Set the NET_BLOCK_OP in the net buffer. The corresponding NET_BLOCK
+ structure is left untouched.
+
+ Some times, there is no 1:1 relationship between NET_BLOCK and NET_BLOCK_OP.
+ For example, that in NetbufGetFragment.
+
+ @param[in, out] Nbuf Pointer to the net buffer.
+ @param[in] Bulk Pointer to the data.
+ @param[in] Len Length of the bulk data.
+ @param[in] Index The data block index in the net buffer the bulk
+ data should belong to.
+
+**/
+VOID
+NetbufSetBlockOp (
+ IN OUT NET_BUF *Nbuf,
+ IN UINT8 *Bulk,
+ IN UINT32 Len,
+ IN UINT32 Index
+ )
+{
+ NET_BLOCK_OP *BlockOp;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ ASSERT (Index < Nbuf->BlockOpNum);
+
+ BlockOp = &(Nbuf->BlockOp[Index]);
+ BlockOp->BlockHead = Bulk;
+ BlockOp->BlockTail = Bulk + Len;
+ BlockOp->Head = Bulk;
+ BlockOp->Tail = Bulk + Len;
+ BlockOp->Size = Len;
+}
+
+
+/**
+ Helper function for NetbufGetFragment. NetbufGetFragment may allocate the
+ first block to reserve HeadSpace bytes header space. So it needs to create a
+ new net vector for the first block and can avoid copy for the remaining data
+ by sharing the old net vector.
+
+ @param[in] Arg Point to the old NET_VECTOR.
+
+**/
+VOID
+EFIAPI
+NetbufGetFragmentFree (
+ IN VOID *Arg
+ )
+{
+ NET_VECTOR *Vector;
+
+ Vector = (NET_VECTOR *)Arg;
+ NetbufFreeVector (Vector);
+}
+
+
+/**
+ Create a NET_BUF structure which contains Len byte data of Nbuf starting from
+ Offset.
+
+ A new NET_BUF structure will be created but the associated data in NET_VECTOR
+ is shared. This function exists to do IP packet fragmentation.
+
+ @param[in] Nbuf Pointer to the net buffer to be extracted.
+ @param[in] Offset Starting point of the data to be included in the new
+ net buffer.
+ @param[in] Len Bytes of data to be included in the new net buffer.
+ @param[in] HeadSpace Bytes of head space to reserve for protocol header.
+
+ @return Pointer to the cloned net buffer, or NULL if the
+ allocation failed due to resource limit.
+
+**/
+NET_BUF *
+EFIAPI
+NetbufGetFragment (
+ IN NET_BUF *Nbuf,
+ IN UINT32 Offset,
+ IN UINT32 Len,
+ IN UINT32 HeadSpace
+ )
+{
+ NET_BUF *Child;
+ NET_VECTOR *Vector;
+ NET_BLOCK_OP *BlockOp;
+ UINT32 CurBlockOp;
+ UINT32 BlockOpNum;
+ UINT8 *FirstBulk;
+ UINT32 Index;
+ UINT32 First;
+ UINT32 Last;
+ UINT32 FirstSkip;
+ UINT32 FirstLen;
+ UINT32 LastLen;
+ UINT32 Cur;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ if ((Len == 0) || (Offset + Len > Nbuf->TotalSize)) {
+ return NULL;
+ }
+
+ //
+ // First find the first and last BlockOp that contains
+ // the valid data, and compute the offset of the first
+ // BlockOp and length of the last BlockOp
+ //
+ BlockOp = Nbuf->BlockOp;
+ Cur = 0;
+
+ for (Index = 0; Index < Nbuf->BlockOpNum; Index++) {
+ if (Offset < Cur + BlockOp[Index].Size) {
+ break;
+ }
+
+ Cur += BlockOp[Index].Size;
+ }
+
+ //
+ // First is the index of the first BlockOp, FirstSkip is
+ // the offset of the first byte in the first BlockOp.
+ //
+ First = Index;
+ FirstSkip = Offset - Cur;
+ FirstLen = BlockOp[Index].Size - FirstSkip;
+
+ Last = 0;
+ LastLen = 0;
+
+ if (Len > FirstLen) {
+ Cur += BlockOp[Index].Size;
+ Index++;
+
+ for (; Index < Nbuf->BlockOpNum; Index++) {
+ if (Offset + Len <= Cur + BlockOp[Index].Size) {
+ Last = Index;
+ LastLen = Offset + Len - Cur;
+ break;
+ }
+
+ Cur += BlockOp[Index].Size;
+ }
+
+ } else {
+ Last = First;
+ LastLen = Len;
+ FirstLen = Len;
+ }
+
+ ASSERT (Last >= First);
+ BlockOpNum = Last - First + 1;
+ CurBlockOp = 0;
+
+ if (HeadSpace != 0) {
+ //
+ // Allocate an extra block to accomdate the head space.
+ //
+ BlockOpNum++;
+
+ Child = NetbufAllocStruct (1, BlockOpNum);
+
+ if (Child == NULL) {
+ return NULL;
+ }
+
+ FirstBulk = AllocatePool (HeadSpace);
+
+ if (FirstBulk == NULL) {
+ goto FreeChild;
+ }
+
+ Vector = Child->Vector;
+ Vector->Free = NetbufGetFragmentFree;
+ Vector->Arg = Nbuf->Vector;
+ Vector->Flag = NET_VECTOR_OWN_FIRST;
+ Vector->Len = HeadSpace;
+
+ //
+ // Reserve the head space in the first block
+ //
+ NetbufSetBlock (Child, FirstBulk, HeadSpace, 0);
+ Child->BlockOp[0].Head += HeadSpace;
+ Child->BlockOp[0].Size = 0;
+ CurBlockOp++;
+
+ } else {
+ Child = NetbufAllocStruct (0, BlockOpNum);
+
+ if (Child == NULL) {
+ return NULL;
+ }
+
+ Child->Vector = Nbuf->Vector;
+ }
+
+ NET_GET_REF (Nbuf->Vector);
+ Child->TotalSize = Len;
+
+ //
+ // Set all the BlockOp up, the first and last one are special
+ // and need special process.
+ //
+ NetbufSetBlockOp (
+ Child,
+ Nbuf->BlockOp[First].Head + FirstSkip,
+ FirstLen,
+ CurBlockOp++
+ );
+
+ for (Index = First + 1; Index < Last; Index++) {
+ NetbufSetBlockOp (
+ Child,
+ BlockOp[Index].Head,
+ BlockOp[Index].Size,
+ CurBlockOp++
+ );
+ }
+
+ if (First != Last) {
+ NetbufSetBlockOp (
+ Child,
+ BlockOp[Last].Head,
+ LastLen,
+ CurBlockOp
+ );
+ }
+
+ CopyMem (Child->ProtoData, Nbuf->ProtoData, NET_PROTO_DATA);
+ return Child;
+
+FreeChild:
+
+ FreePool (Child);
+ return NULL;
+}
+
+
+
+/**
+ Build a NET_BUF from external blocks.
+
+ A new NET_BUF structure will be created from external blocks. Additional block
+ of memory will be allocated to hold reserved HeadSpace bytes of header room
+ and existing HeadLen bytes of header but the external blocks are shared by the
+ net buffer to avoid data copying.
+
+ @param[in] ExtFragment Pointer to the data block.
+ @param[in] ExtNum The number of the data blocks.
+ @param[in] HeadSpace The head space to be reserved.
+ @param[in] HeadLen The length of the protocol header, This function
+ will pull that number of data into a linear block.
+ @param[in] ExtFree Pointer to the caller provided free function.
+ @param[in] Arg The argument passed to ExtFree when ExtFree is
+ called.
+
+ @return Pointer to the net buffer built from the data blocks,
+ or NULL if the allocation failed due to resource
+ limit.
+
+**/
+NET_BUF *
+EFIAPI
+NetbufFromExt (
+ IN NET_FRAGMENT *ExtFragment,
+ IN UINT32 ExtNum,
+ IN UINT32 HeadSpace,
+ IN UINT32 HeadLen,
+ IN NET_VECTOR_EXT_FREE ExtFree,
+ IN VOID *Arg OPTIONAL
+ )
+{
+ NET_BUF *Nbuf;
+ NET_VECTOR *Vector;
+ NET_FRAGMENT SavedFragment;
+ UINT32 SavedIndex;
+ UINT32 TotalLen;
+ UINT32 BlockNum;
+ UINT8 *FirstBlock;
+ UINT32 FirstBlockLen;
+ UINT8 *Header;
+ UINT32 CurBlock;
+ UINT32 Index;
+ UINT32 Len;
+ UINT32 Copied;
+
+ ASSERT ((ExtFragment != NULL) && (ExtNum > 0) && (ExtFree != NULL));
+
+ SavedFragment.Bulk = NULL;
+ SavedFragment.Len = 0;
+
+ FirstBlockLen = 0;
+ FirstBlock = NULL;
+ BlockNum = ExtNum;
+ Index = 0;
+ TotalLen = 0;
+ SavedIndex = 0;
+ Len = 0;
+ Copied = 0;
+
+ //
+ // No need to consolidate the header if the first block is
+ // longer than the header length or there is only one block.
+ //
+ if ((ExtFragment[0].Len >= HeadLen) || (ExtNum == 1)) {
+ HeadLen = 0;
+ }
+
+ //
+ // Allocate an extra block if we need to:
+ // 1. Allocate some header space
+ // 2. aggreate the packet header
+ //
+ if ((HeadSpace != 0) || (HeadLen != 0)) {
+ FirstBlockLen = HeadLen + HeadSpace;
+ FirstBlock = AllocatePool (FirstBlockLen);
+
+ if (FirstBlock == NULL) {
+ return NULL;
+ }
+
+ BlockNum++;
+ }
+
+ //
+ // Copy the header to the first block, reduce the NET_BLOCK
+ // to allocate by one for each block that is completely covered
+ // by the first bulk.
+ //
+ if (HeadLen != 0) {
+ Len = HeadLen;
+ Header = FirstBlock + HeadSpace;
+
+ for (Index = 0; Index < ExtNum; Index++) {
+ if (Len >= ExtFragment[Index].Len) {
+ CopyMem (Header, ExtFragment[Index].Bulk, ExtFragment[Index].Len);
+
+ Copied += ExtFragment[Index].Len;
+ Len -= ExtFragment[Index].Len;
+ Header += ExtFragment[Index].Len;
+ TotalLen += ExtFragment[Index].Len;
+ BlockNum--;
+
+ if (Len == 0) {
+ //
+ // Increament the index number to point to the next
+ // non-empty fragment.
+ //
+ Index++;
+ break;
+ }
+
+ } else {
+ CopyMem (Header, ExtFragment[Index].Bulk, Len);
+
+ Copied += Len;
+ TotalLen += Len;
+
+ //
+ // Adjust the block structure to exclude the data copied,
+ // So, the left-over block can be processed as other blocks.
+ // But it must be recovered later. (SavedIndex > 0) always
+ // holds since we don't aggreate the header if the first block
+ // is bigger enough that the header is continuous
+ //
+ SavedIndex = Index;
+ SavedFragment = ExtFragment[Index];
+ ExtFragment[Index].Bulk += Len;
+ ExtFragment[Index].Len -= Len;
+ break;
+ }
+ }
+ }
+
+ Nbuf = NetbufAllocStruct (BlockNum, BlockNum);
+
+ if (Nbuf == NULL) {
+ goto FreeFirstBlock;
+ }
+
+ Vector = Nbuf->Vector;
+ Vector->Free = ExtFree;
+ Vector->Arg = Arg;
+ Vector->Flag = ((FirstBlockLen != 0) ? NET_VECTOR_OWN_FIRST : 0);
+
+ //
+ // Set the first block up which may contain
+ // some head space and aggregated header
+ //
+ CurBlock = 0;
+
+ if (FirstBlockLen != 0) {
+ NetbufSetBlock (Nbuf, FirstBlock, HeadSpace + Copied, 0);
+ Nbuf->BlockOp[0].Head += HeadSpace;
+ Nbuf->BlockOp[0].Size = Copied;
+
+ CurBlock++;
+ }
+
+ for (; Index < ExtNum; Index++) {
+ NetbufSetBlock (Nbuf, ExtFragment[Index].Bulk, ExtFragment[Index].Len, CurBlock);
+ TotalLen += ExtFragment[Index].Len;
+ CurBlock++;
+ }
+
+ Vector->Len = TotalLen + HeadSpace;
+ Nbuf->TotalSize = TotalLen;
+
+ if (SavedIndex != 0) {
+ ExtFragment[SavedIndex] = SavedFragment;
+ }
+
+ return Nbuf;
+
+FreeFirstBlock:
+ if (FirstBlock != NULL) {
+ FreePool (FirstBlock);
+ }
+ return NULL;
+}
+
+
+/**
+ Build a fragment table to contain the fragments in the net buffer. This is the
+ opposite operation of the NetbufFromExt.
+
+ @param[in] Nbuf Point to the net buffer.
+ @param[in, out] ExtFragment Pointer to the data block.
+ @param[in, out] ExtNum The number of the data blocks.
+
+ @retval EFI_BUFFER_TOO_SMALL The number of non-empty block is bigger than
+ ExtNum.
+ @retval EFI_SUCCESS Fragment table is built successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+NetbufBuildExt (
+ IN NET_BUF *Nbuf,
+ IN OUT NET_FRAGMENT *ExtFragment,
+ IN OUT UINT32 *ExtNum
+ )
+{
+ UINT32 Index;
+ UINT32 Current;
+
+ Current = 0;
+
+ for (Index = 0; (Index < Nbuf->BlockOpNum); Index++) {
+ if (Nbuf->BlockOp[Index].Size == 0) {
+ continue;
+ }
+
+ if (Current < *ExtNum) {
+ ExtFragment[Current].Bulk = Nbuf->BlockOp[Index].Head;
+ ExtFragment[Current].Len = Nbuf->BlockOp[Index].Size;
+ Current++;
+ } else {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ }
+
+ *ExtNum = Current;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Build a net buffer from a list of net buffers.
+
+ All the fragments will be collected from the list of NEW_BUF and then a new
+ net buffer will be created through NetbufFromExt.
+
+ @param[in] BufList A List of the net buffer.
+ @param[in] HeadSpace The head space to be reserved.
+ @param[in] HeaderLen The length of the protocol header, This function
+ will pull that number of data into a linear block.
+ @param[in] ExtFree Pointer to the caller provided free function.
+ @param[in] Arg The argument passed to ExtFree when ExtFree is called.
+
+ @return Pointer to the net buffer built from the list of net
+ buffers.
+
+**/
+NET_BUF *
+EFIAPI
+NetbufFromBufList (
+ IN LIST_ENTRY *BufList,
+ IN UINT32 HeadSpace,
+ IN UINT32 HeaderLen,
+ IN NET_VECTOR_EXT_FREE ExtFree,
+ IN VOID *Arg OPTIONAL
+ )
+{
+ NET_FRAGMENT *Fragment;
+ UINT32 FragmentNum;
+ LIST_ENTRY *Entry;
+ NET_BUF *Nbuf;
+ UINT32 Index;
+ UINT32 Current;
+
+ //
+ //Compute how many blocks are there
+ //
+ FragmentNum = 0;
+
+ NET_LIST_FOR_EACH (Entry, BufList) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ FragmentNum += Nbuf->BlockOpNum;
+ }
+
+ //
+ //Allocate and copy block points
+ //
+ Fragment = AllocatePool (sizeof (NET_FRAGMENT) * FragmentNum);
+
+ if (Fragment == NULL) {
+ return NULL;
+ }
+
+ Current = 0;
+
+ NET_LIST_FOR_EACH (Entry, BufList) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ for (Index = 0; Index < Nbuf->BlockOpNum; Index++) {
+ if (Nbuf->BlockOp[Index].Size != 0) {
+ Fragment[Current].Bulk = Nbuf->BlockOp[Index].Head;
+ Fragment[Current].Len = Nbuf->BlockOp[Index].Size;
+ Current++;
+ }
+ }
+ }
+
+ Nbuf = NetbufFromExt (Fragment, Current, HeadSpace, HeaderLen, ExtFree, Arg);
+ FreePool (Fragment);
+
+ return Nbuf;
+}
+
+
+/**
+ Reserve some space in the header room of the net buffer.
+
+ Upon allocation, all the space are in the tail room of the buffer. Call this
+ function to move some space to the header room. This function is quite limited
+ in that it can only reserve space from the first block of an empty NET_BUF not
+ built from the external. But it should be enough for the network stack.
+
+ @param[in, out] Nbuf Pointer to the net buffer.
+ @param[in] Len The length of buffer to be reserved from the header.
+
+**/
+VOID
+EFIAPI
+NetbufReserve (
+ IN OUT NET_BUF *Nbuf,
+ IN UINT32 Len
+ )
+{
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ NET_CHECK_SIGNATURE (Nbuf->Vector, NET_VECTOR_SIGNATURE);
+
+ ASSERT ((Nbuf->BlockOpNum == 1) && (Nbuf->TotalSize == 0));
+ ASSERT ((Nbuf->Vector->Free == NULL) && (Nbuf->Vector->Len >= Len));
+
+ Nbuf->BlockOp[0].Head += Len;
+ Nbuf->BlockOp[0].Tail += Len;
+
+ ASSERT (Nbuf->BlockOp[0].Tail <= Nbuf->BlockOp[0].BlockTail);
+}
+
+
+/**
+ Allocate Len bytes of space from the header or tail of the buffer.
+
+ @param[in, out] Nbuf Pointer to the net buffer.
+ @param[in] Len The length of the buffer to be allocated.
+ @param[in] FromHead The flag to indicate whether reserve the data
+ from head (TRUE) or tail (FALSE).
+
+ @return Pointer to the first byte of the allocated buffer,
+ or NULL if there is no sufficient space.
+
+**/
+UINT8*
+EFIAPI
+NetbufAllocSpace (
+ IN OUT NET_BUF *Nbuf,
+ IN UINT32 Len,
+ IN BOOLEAN FromHead
+ )
+{
+ NET_BLOCK_OP *BlockOp;
+ UINT32 Index;
+ UINT8 *SavedTail;
+
+ Index = 0;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ NET_CHECK_SIGNATURE (Nbuf->Vector, NET_VECTOR_SIGNATURE);
+
+ ASSERT (Len > 0);
+
+ if (FromHead) {
+ //
+ // Allocate some space from head. If the buffer is empty,
+ // allocate from the first block. If it isn't, allocate
+ // from the first non-empty block, or the block before that.
+ //
+ if (Nbuf->TotalSize == 0) {
+ Index = 0;
+ } else {
+ NetbufGetByte (Nbuf, 0, &Index);
+
+ if ((NET_HEADSPACE(&(Nbuf->BlockOp[Index])) < Len) && (Index > 0)) {
+ Index--;
+ }
+ }
+
+ BlockOp = &(Nbuf->BlockOp[Index]);
+
+ if (NET_HEADSPACE (BlockOp) < Len) {
+ return NULL;
+ }
+
+ BlockOp->Head -= Len;
+ BlockOp->Size += Len;
+ Nbuf->TotalSize += Len;
+
+ return BlockOp->Head;
+
+ } else {
+ //
+ // Allocate some space from the tail. If the buffer is empty,
+ // allocate from the first block. If it isn't, allocate
+ // from the last non-empty block, or the block after that.
+ //
+ if (Nbuf->TotalSize == 0) {
+ Index = 0;
+ } else {
+ NetbufGetByte (Nbuf, Nbuf->TotalSize - 1, &Index);
+
+ if ((NET_TAILSPACE(&(Nbuf->BlockOp[Index])) < Len) &&
+ (Index < Nbuf->BlockOpNum - 1)) {
+
+ Index++;
+ }
+ }
+
+ BlockOp = &(Nbuf->BlockOp[Index]);
+
+ if (NET_TAILSPACE (BlockOp) < Len) {
+ return NULL;
+ }
+
+ SavedTail = BlockOp->Tail;
+
+ BlockOp->Tail += Len;
+ BlockOp->Size += Len;
+ Nbuf->TotalSize += Len;
+
+ return SavedTail;
+ }
+}
+
+
+/**
+ Trim a single NET_BLOCK by Len bytes from the header or tail.
+
+ @param[in, out] BlockOp Pointer to the NET_BLOCK.
+ @param[in] Len The length of the data to be trimmed.
+ @param[in] FromHead The flag to indicate whether trim data from head
+ (TRUE) or tail (FALSE).
+
+**/
+VOID
+NetblockTrim (
+ IN OUT NET_BLOCK_OP *BlockOp,
+ IN UINT32 Len,
+ IN BOOLEAN FromHead
+ )
+{
+ ASSERT ((BlockOp != NULL) && (BlockOp->Size >= Len));
+
+ BlockOp->Size -= Len;
+
+ if (FromHead) {
+ BlockOp->Head += Len;
+ } else {
+ BlockOp->Tail -= Len;
+ }
+}
+
+
+/**
+ Trim Len bytes from the header or tail of the net buffer.
+
+ @param[in, out] Nbuf Pointer to the net buffer.
+ @param[in] Len The length of the data to be trimmed.
+ @param[in] FromHead The flag to indicate whether trim data from head
+ (TRUE) or tail (FALSE).
+
+ @return Length of the actually trimmed data, which is possible to be less
+ than Len because the TotalSize of Nbuf is less than Len.
+
+**/
+UINT32
+EFIAPI
+NetbufTrim (
+ IN OUT NET_BUF *Nbuf,
+ IN UINT32 Len,
+ IN BOOLEAN FromHead
+ )
+{
+ NET_BLOCK_OP *BlockOp;
+ UINT32 Index;
+ UINT32 Trimmed;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ if (Len == 0 || Nbuf->TotalSize == 0) {
+ return 0;
+ }
+
+ if (Len > Nbuf->TotalSize) {
+ Len = Nbuf->TotalSize;
+ }
+
+ //
+ // If FromTail is true, iterate backward. That
+ // is, init Index to NBuf->BlockNum - 1, and
+ // decrease it by 1 during each loop. Otherwise,
+ // iterate forward. That is, init Index to 0, and
+ // increase it by 1 during each loop.
+ //
+ Trimmed = 0;
+ Nbuf->TotalSize -= Len;
+
+ Index = (FromHead ? 0 : Nbuf->BlockOpNum - 1);
+ BlockOp = Nbuf->BlockOp;
+
+ for (;;) {
+ if (BlockOp[Index].Size == 0) {
+ Index += (FromHead ? 1 : -1);
+ continue;
+ }
+
+ if (Len > BlockOp[Index].Size) {
+ Len -= BlockOp[Index].Size;
+ Trimmed += BlockOp[Index].Size;
+ NetblockTrim (&BlockOp[Index], BlockOp[Index].Size, FromHead);
+ } else {
+ Trimmed += Len;
+ NetblockTrim (&BlockOp[Index], Len, FromHead);
+ break;
+ }
+
+ Index += (FromHead ? 1 : -1);
+ }
+
+ return Trimmed;
+}
+
+
+/**
+ Copy Len bytes of data from the specific offset of the net buffer to the
+ destination memory.
+
+ The Len bytes of data may cross the several fragments of the net buffer.
+
+ @param[in] Nbuf Pointer to the net buffer.
+ @param[in] Offset The sequence number of the first byte to copy.
+ @param[in] Len Length of the data to copy.
+ @param[in] Dest The destination of the data to copy to.
+
+ @return The length of the actual copied data, or 0 if the offset
+ specified exceeds the total size of net buffer.
+
+**/
+UINT32
+EFIAPI
+NetbufCopy (
+ IN NET_BUF *Nbuf,
+ IN UINT32 Offset,
+ IN UINT32 Len,
+ IN UINT8 *Dest
+ )
+{
+ NET_BLOCK_OP *BlockOp;
+ UINT32 Skip;
+ UINT32 Left;
+ UINT32 Copied;
+ UINT32 Index;
+ UINT32 Cur;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ ASSERT (Dest);
+
+ if ((Len == 0) || (Nbuf->TotalSize <= Offset)) {
+ return 0;
+ }
+
+ if (Nbuf->TotalSize - Offset < Len) {
+ Len = Nbuf->TotalSize - Offset;
+ }
+
+ BlockOp = Nbuf->BlockOp;
+
+ //
+ // Skip to the offset. Don't make "Offset-By-One" error here.
+ // Cur + BLOCK.SIZE is the first sequence number of next block.
+ // So, (Offset < Cur + BLOCK.SIZE) means that the first byte
+ // is in the current block. if (Offset == Cur + BLOCK.SIZE), the
+ // first byte is the next block's first byte.
+ //
+ Cur = 0;
+
+ for (Index = 0; Index < Nbuf->BlockOpNum; Index++) {
+ if (BlockOp[Index].Size == 0) {
+ continue;
+ }
+
+ if (Offset < Cur + BlockOp[Index].Size) {
+ break;
+ }
+
+ Cur += BlockOp[Index].Size;
+ }
+
+ //
+ // Cur is the sequence number of the first byte in the block
+ // Offset - Cur is the number of bytes before first byte to
+ // to copy in the current block.
+ //
+ Skip = Offset - Cur;
+ Left = BlockOp[Index].Size - Skip;
+
+ if (Len <= Left) {
+ CopyMem (Dest, BlockOp[Index].Head + Skip, Len);
+ return Len;
+ }
+
+ CopyMem (Dest, BlockOp[Index].Head + Skip, Left);
+
+ Dest += Left;
+ Len -= Left;
+ Copied = Left;
+
+ Index++;
+
+ for (; Index < Nbuf->BlockOpNum; Index++) {
+ if (Len > BlockOp[Index].Size) {
+ Len -= BlockOp[Index].Size;
+ Copied += BlockOp[Index].Size;
+
+ CopyMem (Dest, BlockOp[Index].Head, BlockOp[Index].Size);
+ Dest += BlockOp[Index].Size;
+ } else {
+ Copied += Len;
+ CopyMem (Dest, BlockOp[Index].Head, Len);
+ break;
+ }
+ }
+
+ return Copied;
+}
+
+
+/**
+ Initiate the net buffer queue.
+
+ @param[in, out] NbufQue Pointer to the net buffer queue to be initialized.
+
+**/
+VOID
+EFIAPI
+NetbufQueInit (
+ IN OUT NET_BUF_QUEUE *NbufQue
+ )
+{
+ NbufQue->Signature = NET_QUE_SIGNATURE;
+ NbufQue->RefCnt = 1;
+ InitializeListHead (&NbufQue->List);
+
+ InitializeListHead (&NbufQue->BufList);
+ NbufQue->BufSize = 0;
+ NbufQue->BufNum = 0;
+}
+
+
+/**
+ Allocate and initialize a net buffer queue.
+
+ @return Pointer to the allocated net buffer queue, or NULL if the
+ allocation failed due to resource limit.
+
+**/
+NET_BUF_QUEUE *
+EFIAPI
+NetbufQueAlloc (
+ VOID
+ )
+{
+ NET_BUF_QUEUE *NbufQue;
+
+ NbufQue = AllocatePool (sizeof (NET_BUF_QUEUE));
+ if (NbufQue == NULL) {
+ return NULL;
+ }
+
+ NetbufQueInit (NbufQue);
+
+ return NbufQue;
+}
+
+
+/**
+ Free a net buffer queue.
+
+ Decrease the reference count of the net buffer queue by one. The real resource
+ free operation isn't performed until the reference count of the net buffer
+ queue is decreased to 0.
+
+ @param[in] NbufQue Pointer to the net buffer queue to be freed.
+
+**/
+VOID
+EFIAPI
+NetbufQueFree (
+ IN NET_BUF_QUEUE *NbufQue
+ )
+{
+ ASSERT (NbufQue != NULL);
+ NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
+
+ NbufQue->RefCnt--;
+
+ if (NbufQue->RefCnt == 0) {
+ NetbufQueFlush (NbufQue);
+ FreePool (NbufQue);
+ }
+}
+
+
+/**
+ Append a net buffer to the net buffer queue.
+
+ @param[in, out] NbufQue Pointer to the net buffer queue.
+ @param[in, out] Nbuf Pointer to the net buffer to be appended.
+
+**/
+VOID
+EFIAPI
+NetbufQueAppend (
+ IN OUT NET_BUF_QUEUE *NbufQue,
+ IN OUT NET_BUF *Nbuf
+ )
+{
+ NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ InsertTailList (&NbufQue->BufList, &Nbuf->List);
+
+ NbufQue->BufSize += Nbuf->TotalSize;
+ NbufQue->BufNum++;
+}
+
+
+/**
+ Remove a net buffer from the head in the specific queue and return it.
+
+ @param[in, out] NbufQue Pointer to the net buffer queue.
+
+ @return Pointer to the net buffer removed from the specific queue,
+ or NULL if there is no net buffer in the specific queue.
+
+**/
+NET_BUF *
+EFIAPI
+NetbufQueRemove (
+ IN OUT NET_BUF_QUEUE *NbufQue
+ )
+{
+ NET_BUF *First;
+
+ NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
+
+ if (NbufQue->BufNum == 0) {
+ return NULL;
+ }
+
+ First = NET_LIST_USER_STRUCT (NbufQue->BufList.ForwardLink, NET_BUF, List);
+
+ NetListRemoveHead (&NbufQue->BufList);
+
+ NbufQue->BufSize -= First->TotalSize;
+ NbufQue->BufNum--;
+ return First;
+}
+
+
+/**
+ Copy Len bytes of data from the net buffer queue at the specific offset to the
+ destination memory.
+
+ The copying operation is the same as NetbufCopy but applies to the net buffer
+ queue instead of the net buffer.
+
+ @param[in] NbufQue Pointer to the net buffer queue.
+ @param[in] Offset The sequence number of the first byte to copy.
+ @param[in] Len Length of the data to copy.
+ @param[out] Dest The destination of the data to copy to.
+
+ @return The length of the actual copied data, or 0 if the offset
+ specified exceeds the total size of net buffer queue.
+
+**/
+UINT32
+EFIAPI
+NetbufQueCopy (
+ IN NET_BUF_QUEUE *NbufQue,
+ IN UINT32 Offset,
+ IN UINT32 Len,
+ OUT UINT8 *Dest
+ )
+{
+ LIST_ENTRY *Entry;
+ NET_BUF *Nbuf;
+ UINT32 Skip;
+ UINT32 Left;
+ UINT32 Cur;
+ UINT32 Copied;
+
+ NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
+ ASSERT (Dest != NULL);
+
+ if ((Len == 0) || (NbufQue->BufSize <= Offset)) {
+ return 0;
+ }
+
+ if (NbufQue->BufSize - Offset < Len) {
+ Len = NbufQue->BufSize - Offset;
+ }
+
+ //
+ // skip to the Offset
+ //
+ Cur = 0;
+ Nbuf = NULL;
+
+ NET_LIST_FOR_EACH (Entry, &NbufQue->BufList) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ if (Offset < Cur + Nbuf->TotalSize) {
+ break;
+ }
+
+ Cur += Nbuf->TotalSize;
+ }
+
+ ASSERT (Nbuf != NULL);
+
+ //
+ // Copy the data in the first buffer.
+ //
+ Skip = Offset - Cur;
+ Left = Nbuf->TotalSize - Skip;
+
+ if (Len < Left) {
+ return NetbufCopy (Nbuf, Skip, Len, Dest);
+ }
+
+ NetbufCopy (Nbuf, Skip, Left, Dest);
+ Dest += Left;
+ Len -= Left;
+ Copied = Left;
+
+ //
+ // Iterate over the others
+ //
+ Entry = Entry->ForwardLink;
+
+ while ((Len > 0) && (Entry != &NbufQue->BufList)) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ if (Len > Nbuf->TotalSize) {
+ Len -= Nbuf->TotalSize;
+ Copied += Nbuf->TotalSize;
+
+ NetbufCopy (Nbuf, 0, Nbuf->TotalSize, Dest);
+ Dest += Nbuf->TotalSize;
+
+ } else {
+ NetbufCopy (Nbuf, 0, Len, Dest);
+ Copied += Len;
+ break;
+ }
+
+ Entry = Entry->ForwardLink;
+ }
+
+ return Copied;
+}
+
+
+/**
+ Trim Len bytes of data from the buffer queue and free any net buffer
+ that is completely trimmed.
+
+ The trimming operation is the same as NetbufTrim but applies to the net buffer
+ queue instead of the net buffer.
+
+ @param[in, out] NbufQue Pointer to the net buffer queue.
+ @param[in] Len Length of the data to trim.
+
+ @return The actual length of the data trimmed.
+
+**/
+UINT32
+EFIAPI
+NetbufQueTrim (
+ IN OUT NET_BUF_QUEUE *NbufQue,
+ IN UINT32 Len
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ NET_BUF *Nbuf;
+ UINT32 Trimmed;
+
+ NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
+
+ if (Len == 0) {
+ return 0;
+ }
+
+ if (Len > NbufQue->BufSize) {
+ Len = NbufQue->BufSize;
+ }
+
+ NbufQue->BufSize -= Len;
+ Trimmed = 0;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &NbufQue->BufList) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ if (Len >= Nbuf->TotalSize) {
+ Trimmed += Nbuf->TotalSize;
+ Len -= Nbuf->TotalSize;
+
+ RemoveEntryList (Entry);
+ NetbufFree (Nbuf);
+
+ NbufQue->BufNum--;
+
+ if (Len == 0) {
+ break;
+ }
+
+ } else {
+ Trimmed += NetbufTrim (Nbuf, Len, NET_BUF_HEAD);
+ break;
+ }
+ }
+
+ return Trimmed;
+}
+
+
+/**
+ Flush the net buffer queue.
+
+ @param[in, out] NbufQue Pointer to the queue to be flushed.
+
+**/
+VOID
+EFIAPI
+NetbufQueFlush (
+ IN OUT NET_BUF_QUEUE *NbufQue
+ )
+{
+ NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
+
+ NetbufFreeList (&NbufQue->BufList);
+
+ NbufQue->BufNum = 0;
+ NbufQue->BufSize = 0;
+}
+
+
+/**
+ Compute the checksum for a bulk of data.
+
+ @param[in] Bulk Pointer to the data.
+ @param[in] Len Length of the data, in bytes.
+
+ @return The computed checksum.
+
+**/
+UINT16
+EFIAPI
+NetblockChecksum (
+ IN UINT8 *Bulk,
+ IN UINT32 Len
+ )
+{
+ register UINT32 Sum;
+
+ Sum = 0;
+
+ //
+ // Add left-over byte, if any
+ //
+ if (Len % 2 != 0) {
+ Sum += *(Bulk + Len - 1);
+ }
+
+ while (Len > 1) {
+ Sum += *(UINT16 *) Bulk;
+ Bulk += 2;
+ Len -= 2;
+ }
+
+ //
+ // Fold 32-bit sum to 16 bits
+ //
+ while ((Sum >> 16) != 0) {
+ Sum = (Sum & 0xffff) + (Sum >> 16);
+
+ }
+
+ return (UINT16) Sum;
+}
+
+
+/**
+ Add two checksums.
+
+ @param[in] Checksum1 The first checksum to be added.
+ @param[in] Checksum2 The second checksum to be added.
+
+ @return The new checksum.
+
+**/
+UINT16
+EFIAPI
+NetAddChecksum (
+ IN UINT16 Checksum1,
+ IN UINT16 Checksum2
+ )
+{
+ UINT32 Sum;
+
+ Sum = Checksum1 + Checksum2;
+
+ //
+ // two UINT16 can only add up to a carry of 1.
+ //
+ if ((Sum >> 16) != 0) {
+ Sum = (Sum & 0xffff) + 1;
+
+ }
+
+ return (UINT16) Sum;
+}
+
+
+/**
+ Compute the checksum for a NET_BUF.
+
+ @param[in] Nbuf Pointer to the net buffer.
+
+ @return The computed checksum.
+
+**/
+UINT16
+EFIAPI
+NetbufChecksum (
+ IN NET_BUF *Nbuf
+ )
+{
+ NET_BLOCK_OP *BlockOp;
+ UINT32 Offset;
+ UINT16 TotalSum;
+ UINT16 BlockSum;
+ UINT32 Index;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ TotalSum = 0;
+ Offset = 0;
+ BlockOp = Nbuf->BlockOp;
+
+ for (Index = 0; Index < Nbuf->BlockOpNum; Index++) {
+ if (BlockOp[Index].Size == 0) {
+ continue;
+ }
+
+ BlockSum = NetblockChecksum (BlockOp[Index].Head, BlockOp[Index].Size);
+
+ if ((Offset & 0x01) != 0) {
+ //
+ // The checksum starts with an odd byte, swap
+ // the checksum before added to total checksum
+ //
+ BlockSum = SwapBytes16 (BlockSum);
+ }
+
+ TotalSum = NetAddChecksum (BlockSum, TotalSum);
+ Offset += BlockOp[Index].Size;
+ }
+
+ return TotalSum;
+}
+
+
+/**
+ Compute the checksum for TCP/UDP pseudo header.
+
+ Src and Dst are in network byte order, and Len is in host byte order.
+
+ @param[in] Src The source address of the packet.
+ @param[in] Dst The destination address of the packet.
+ @param[in] Proto The protocol type of the packet.
+ @param[in] Len The length of the packet.
+
+ @return The computed checksum.
+
+**/
+UINT16
+EFIAPI
+NetPseudoHeadChecksum (
+ IN IP4_ADDR Src,
+ IN IP4_ADDR Dst,
+ IN UINT8 Proto,
+ IN UINT16 Len
+ )
+{
+ NET_PSEUDO_HDR Hdr;
+
+ //
+ // Zero the memory to relieve align problems
+ //
+ ZeroMem (&Hdr, sizeof (Hdr));
+
+ Hdr.SrcIp = Src;
+ Hdr.DstIp = Dst;
+ Hdr.Protocol = Proto;
+ Hdr.Len = HTONS (Len);
+
+ return NetblockChecksum ((UINT8 *) &Hdr, sizeof (Hdr));
+}
+
+/**
+ Compute the checksum for TCP6/UDP6 pseudo header.
+
+ Src and Dst are in network byte order, and Len is in host byte order.
+
+ @param[in] Src The source address of the packet.
+ @param[in] Dst The destination address of the packet.
+ @param[in] NextHeader The protocol type of the packet.
+ @param[in] Len The length of the packet.
+
+ @return The computed checksum.
+
+**/
+UINT16
+EFIAPI
+NetIp6PseudoHeadChecksum (
+ IN EFI_IPv6_ADDRESS *Src,
+ IN EFI_IPv6_ADDRESS *Dst,
+ IN UINT8 NextHeader,
+ IN UINT32 Len
+ )
+{
+ NET_IP6_PSEUDO_HDR Hdr;
+
+ //
+ // Zero the memory to relieve align problems
+ //
+ ZeroMem (&Hdr, sizeof (Hdr));
+
+ IP6_COPY_ADDRESS (&Hdr.SrcIp, Src);
+ IP6_COPY_ADDRESS (&Hdr.DstIp, Dst);
+
+ Hdr.NextHeader = NextHeader;
+ Hdr.Len = HTONL (Len);
+
+ return NetblockChecksum ((UINT8 *) &Hdr, sizeof (Hdr));
+}
+
+/**
+ The function frees the net buffer which allocated by the IP protocol. It releases
+ only the net buffer and doesn't call the external free function.
+
+ This function should be called after finishing the process of mIpSec->ProcessExt()
+ for outbound traffic. The (EFI_IPSEC2_PROTOCOL)->ProcessExt() allocates a new
+ buffer for the ESP, so there needs a function to free the old net buffer.
+
+ @param[in] Nbuf The network buffer to be freed.
+
+**/
+VOID
+NetIpSecNetbufFree (
+ NET_BUF *Nbuf
+ )
+{
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+ ASSERT (Nbuf->RefCnt > 0);
+
+ Nbuf->RefCnt--;
+
+ if (Nbuf->RefCnt == 0) {
+
+ //
+ // Update Vector only when NBuf is to be released. That is,
+ // all the sharing of Nbuf increse Vector's RefCnt by one
+ //
+ NET_CHECK_SIGNATURE (Nbuf->Vector, NET_VECTOR_SIGNATURE);
+ ASSERT (Nbuf->Vector->RefCnt > 0);
+
+ Nbuf->Vector->RefCnt--;
+
+ if (Nbuf->Vector->RefCnt > 0) {
+ return;
+ }
+
+ //
+ // If NET_VECTOR_OWN_FIRST is set, release the first block since it is
+ // allocated by us
+ //
+ if ((Nbuf->Vector->Flag & NET_VECTOR_OWN_FIRST) != 0) {
+ FreePool (Nbuf->Vector->Block[0].Bulk);
+ }
+ FreePool (Nbuf->Vector);
+ FreePool (Nbuf);
+ }
+}
+
diff --git a/NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.c b/NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.c
new file mode 100644
index 0000000000..341295d0b7
--- /dev/null
+++ b/NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.c
@@ -0,0 +1,1011 @@
+/** @file
+ This library is used to share code between UEFI network stack modules.
+ It provides the helper routines to access TCP service.
+
+Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+
+#include <Library/TcpIoLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+
+/**
+ The common notify function associated with various TcpIo events.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The context.
+
+**/
+VOID
+EFIAPI
+TcpIoCommonNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ if ((Event == NULL) || (Context == NULL)) {
+ return ;
+ }
+
+ *((BOOLEAN *) Context) = TRUE;
+}
+
+/**
+ The internal function for delay configuring TCP6 when IP6 driver is still in DAD.
+
+ @param[in] Tcp6 The EFI_TCP6_PROTOCOL protocol instance.
+ @param[in] Tcp6ConfigData The Tcp6 configuration data.
+
+ @retval EFI_SUCCESS The operational settings successfully
+ completed.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval Others Failed to finish the operation.
+
+**/
+EFI_STATUS
+TcpIoGetMapping (
+ IN EFI_TCP6_PROTOCOL *Tcp6,
+ IN EFI_TCP6_CONFIG_DATA *Tcp6ConfigData
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+
+ if ((Tcp6 == NULL) || (Tcp6ConfigData == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Event = NULL;
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = gBS->SetTimer (
+ Event,
+ TimerRelative,
+ TCP_GET_MAPPING_TIMEOUT
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ while (EFI_ERROR (gBS->CheckEvent (Event))) {
+
+ Tcp6->Poll (Tcp6);
+
+ Status = Tcp6->Configure (Tcp6, Tcp6ConfigData);
+
+ if (!EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ON_EXIT:
+
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ return Status;
+}
+
+/**
+ Create a TCP socket with the specified configuration data.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+ @param[in] TcpVersion The version of Tcp, TCP_VERSION_4 or TCP_VERSION_6.
+ @param[in] ConfigData The Tcp configuration data.
+ @param[out] TcpIo The TcpIo.
+
+ @retval EFI_SUCCESS The TCP socket is created and configured.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_UNSUPPORTED One or more of the control options are not
+ supported in the implementation.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Failed to create the TCP socket or configure it.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpIoCreateSocket (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN UINT8 TcpVersion,
+ IN TCP_IO_CONFIG_DATA *ConfigData,
+ OUT TCP_IO *TcpIo
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+ EFI_GUID *ServiceBindingGuid;
+ EFI_GUID *ProtocolGuid;
+ VOID **Interface;
+ EFI_TCP4_OPTION ControlOption;
+ EFI_TCP4_CONFIG_DATA Tcp4ConfigData;
+ EFI_TCP4_ACCESS_POINT *AccessPoint4;
+ EFI_TCP4_PROTOCOL *Tcp4;
+ EFI_TCP6_CONFIG_DATA Tcp6ConfigData;
+ EFI_TCP6_ACCESS_POINT *AccessPoint6;
+ EFI_TCP6_PROTOCOL *Tcp6;
+ EFI_TCP4_RECEIVE_DATA *RxData;
+
+ if ((Image == NULL) || (Controller == NULL) || (ConfigData == NULL) || (TcpIo == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Tcp4 = NULL;
+ Tcp6 = NULL;
+
+ ZeroMem (TcpIo, sizeof (TCP_IO));
+
+ if (TcpVersion == TCP_VERSION_4) {
+ ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
+ ProtocolGuid = &gEfiTcp4ProtocolGuid;
+ Interface = (VOID **) (&TcpIo->Tcp.Tcp4);
+ } else if (TcpVersion == TCP_VERSION_6) {
+ ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
+ ProtocolGuid = &gEfiTcp6ProtocolGuid;
+ Interface = (VOID **) (&TcpIo->Tcp.Tcp6);
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+
+ TcpIo->TcpVersion = TcpVersion;
+
+ //
+ // Create the TCP child instance and get the TCP protocol.
+ //
+ Status = NetLibCreateServiceChild (
+ Controller,
+ Image,
+ ServiceBindingGuid,
+ &TcpIo->Handle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ TcpIo->Handle,
+ ProtocolGuid,
+ Interface,
+ Image,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status) || (*Interface == NULL)) {
+ goto ON_ERROR;
+ }
+
+ if (TcpVersion == TCP_VERSION_4) {
+ Tcp4 = TcpIo->Tcp.Tcp4;
+ } else {
+ Tcp6 = TcpIo->Tcp.Tcp6;
+ }
+
+ TcpIo->Image = Image;
+ TcpIo->Controller = Controller;
+
+ //
+ // Set the configuration parameters.
+ //
+ ControlOption.ReceiveBufferSize = 0x200000;
+ ControlOption.SendBufferSize = 0x200000;
+ ControlOption.MaxSynBackLog = 0;
+ ControlOption.ConnectionTimeout = 0;
+ ControlOption.DataRetries = 6;
+ ControlOption.FinTimeout = 0;
+ ControlOption.TimeWaitTimeout = 0;
+ ControlOption.KeepAliveProbes = 4;
+ ControlOption.KeepAliveTime = 0;
+ ControlOption.KeepAliveInterval = 0;
+ ControlOption.EnableNagle = FALSE;
+ ControlOption.EnableTimeStamp = FALSE;
+ ControlOption.EnableWindowScaling = TRUE;
+ ControlOption.EnableSelectiveAck = FALSE;
+ ControlOption.EnablePathMtuDiscovery = FALSE;
+
+ if (TcpVersion == TCP_VERSION_4) {
+ Tcp4ConfigData.TypeOfService = 8;
+ Tcp4ConfigData.TimeToLive = 255;
+ Tcp4ConfigData.ControlOption = &ControlOption;
+
+ AccessPoint4 = &Tcp4ConfigData.AccessPoint;
+
+ ZeroMem (AccessPoint4, sizeof (EFI_TCP4_ACCESS_POINT));
+ AccessPoint4->StationPort = ConfigData->Tcp4IoConfigData.StationPort;
+ AccessPoint4->RemotePort = ConfigData->Tcp4IoConfigData.RemotePort;
+ AccessPoint4->ActiveFlag = ConfigData->Tcp4IoConfigData.ActiveFlag;
+
+ CopyMem (
+ &AccessPoint4->StationAddress,
+ &ConfigData->Tcp4IoConfigData.LocalIp,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ CopyMem (
+ &AccessPoint4->SubnetMask,
+ &ConfigData->Tcp4IoConfigData.SubnetMask,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+ CopyMem (
+ &AccessPoint4->RemoteAddress,
+ &ConfigData->Tcp4IoConfigData.RemoteIp,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+
+ ASSERT (Tcp4 != NULL);
+
+ //
+ // Configure the TCP4 protocol.
+ //
+ Status = Tcp4->Configure (Tcp4, &Tcp4ConfigData);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ if (!EFI_IP4_EQUAL (&ConfigData->Tcp4IoConfigData.Gateway, &mZeroIp4Addr)) {
+ //
+ // The gateway is not zero. Add the default route manually.
+ //
+ Status = Tcp4->Routes (
+ Tcp4,
+ FALSE,
+ &mZeroIp4Addr,
+ &mZeroIp4Addr,
+ &ConfigData->Tcp4IoConfigData.Gateway
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+ } else {
+ Tcp6ConfigData.TrafficClass = 0;
+ Tcp6ConfigData.HopLimit = 255;
+ Tcp6ConfigData.ControlOption = (EFI_TCP6_OPTION *) &ControlOption;
+
+ AccessPoint6 = &Tcp6ConfigData.AccessPoint;
+
+ ZeroMem (AccessPoint6, sizeof (EFI_TCP6_ACCESS_POINT));
+ AccessPoint6->StationPort = ConfigData->Tcp6IoConfigData.StationPort;
+ AccessPoint6->RemotePort = ConfigData->Tcp6IoConfigData.RemotePort;
+ AccessPoint6->ActiveFlag = ConfigData->Tcp6IoConfigData.ActiveFlag;
+
+ IP6_COPY_ADDRESS (&AccessPoint6->RemoteAddress, &ConfigData->Tcp6IoConfigData.RemoteIp);
+
+
+ ASSERT (Tcp6 != NULL);
+ //
+ // Configure the TCP6 protocol.
+ //
+ Status = Tcp6->Configure (Tcp6, &Tcp6ConfigData);
+ if (Status == EFI_NO_MAPPING) {
+ Status = TcpIoGetMapping (Tcp6, &Tcp6ConfigData);
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // Create events for variuos asynchronous operations.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ TcpIoCommonNotify,
+ &TcpIo->IsConnDone,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ TcpIo->ConnToken.Tcp4Token.CompletionToken.Event = Event;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ TcpIoCommonNotify,
+ &TcpIo->IsListenDone,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ TcpIo->ListenToken.Tcp4Token.CompletionToken.Event = Event;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ TcpIoCommonNotify,
+ &TcpIo->IsTxDone,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ TcpIo->TxToken.Tcp4Token.CompletionToken.Event = Event;
+
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ TcpIoCommonNotify,
+ &TcpIo->IsRxDone,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ TcpIo->RxToken.Tcp4Token.CompletionToken.Event = Event;
+
+ RxData = (EFI_TCP4_RECEIVE_DATA *) AllocateZeroPool (sizeof (EFI_TCP4_RECEIVE_DATA));
+ if (RxData == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ TcpIo->RxToken.Tcp4Token.Packet.RxData = RxData;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ TcpIoCommonNotify,
+ &TcpIo->IsCloseDone,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ TcpIo->CloseToken.Tcp4Token.CompletionToken.Event = Event;
+
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ TcpIoDestroySocket (TcpIo);
+
+ return Status;
+}
+
+/**
+ Destroy the socket.
+
+ @param[in] TcpIo The TcpIo which wraps the socket to be destroyed.
+
+**/
+VOID
+EFIAPI
+TcpIoDestroySocket (
+ IN TCP_IO *TcpIo
+ )
+{
+ EFI_EVENT Event;
+ EFI_TCP4_PROTOCOL *Tcp4;
+ EFI_TCP6_PROTOCOL *Tcp6;
+ UINT8 TcpVersion;
+ EFI_GUID *ServiceBindingGuid;
+ EFI_GUID *ProtocolGuid;
+ EFI_HANDLE ChildHandle;
+
+ if (TcpIo == NULL) {
+ return ;
+ }
+
+ TcpVersion = TcpIo->TcpVersion;
+
+ if ((TcpVersion != TCP_VERSION_4) && (TcpVersion != TCP_VERSION_6)) {
+ return ;
+ }
+
+ Event = TcpIo->ConnToken.Tcp4Token.CompletionToken.Event;
+
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ Event = TcpIo->ListenToken.Tcp4Token.CompletionToken.Event;
+
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ Event = TcpIo->TxToken.Tcp4Token.CompletionToken.Event;
+
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ Event = TcpIo->RxToken.Tcp4Token.CompletionToken.Event;
+
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ Event = TcpIo->CloseToken.Tcp4Token.CompletionToken.Event;
+
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ if (TcpIo->RxToken.Tcp4Token.Packet.RxData != NULL) {
+ FreePool (TcpIo->RxToken.Tcp4Token.Packet.RxData);
+ }
+
+ Tcp4 = NULL;
+ Tcp6 = NULL;
+
+
+ if (TcpVersion == TCP_VERSION_4) {
+ ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
+ ProtocolGuid = &gEfiTcp4ProtocolGuid;
+ Tcp4 = TcpIo->Tcp.Tcp4;
+ if (Tcp4 != NULL) {
+ Tcp4->Configure (Tcp4, NULL);
+ }
+ } else {
+ ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
+ ProtocolGuid = &gEfiTcp6ProtocolGuid;
+ Tcp6 = TcpIo->Tcp.Tcp6;
+ if (Tcp6 != NULL) {
+ Tcp6->Configure (Tcp6, NULL);
+ }
+ }
+
+ if ((Tcp4 != NULL) || (Tcp6 != NULL)) {
+
+ gBS->CloseProtocol (
+ TcpIo->Handle,
+ ProtocolGuid,
+ TcpIo->Image,
+ TcpIo->Controller
+ );
+ }
+
+ ChildHandle = NULL;
+
+ if (TcpIo->IsListenDone) {
+ if (TcpVersion == TCP_VERSION_4) {
+ Tcp4 = TcpIo->NewTcp.Tcp4;
+ if (Tcp4 != NULL) {
+ Tcp4->Configure (Tcp4, NULL);
+ ChildHandle = TcpIo->ListenToken.Tcp4Token.NewChildHandle;
+ }
+ } else {
+ Tcp6 = TcpIo->NewTcp.Tcp6;
+ if (Tcp6 != NULL) {
+ Tcp6->Configure (Tcp6, NULL);
+ ChildHandle = TcpIo->ListenToken.Tcp6Token.NewChildHandle;
+ }
+ }
+
+ if (ChildHandle != NULL) {
+
+ gBS->CloseProtocol (
+ ChildHandle,
+ ProtocolGuid,
+ TcpIo->Image,
+ TcpIo->Controller
+ );
+ }
+ }
+
+ NetLibDestroyServiceChild (
+ TcpIo->Controller,
+ TcpIo->Image,
+ ServiceBindingGuid,
+ TcpIo->Handle
+ );
+}
+
+/**
+ Connect to the other endpoint of the TCP socket.
+
+ @param[in, out] TcpIo The TcpIo wrapping the TCP socket.
+ @param[in] Timeout The time to wait for connection done. Set to NULL for infinite wait.
+
+ @retval EFI_SUCCESS Connect to the other endpoint of the TCP socket
+ successfully.
+ @retval EFI_TIMEOUT Failed to connect to the other endpoint of the
+ TCP socket in the specified time period.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_UNSUPPORTED One or more of the control options are not
+ supported in the implementation.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpIoConnect (
+ IN OUT TCP_IO *TcpIo,
+ IN EFI_EVENT Timeout OPTIONAL
+ )
+{
+ EFI_TCP4_PROTOCOL *Tcp4;
+ EFI_TCP6_PROTOCOL *Tcp6;
+ EFI_STATUS Status;
+
+ if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TcpIo->IsConnDone = FALSE;
+
+ Tcp4 = NULL;
+ Tcp6 = NULL;
+
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+ Tcp4 = TcpIo->Tcp.Tcp4;
+ Status = Tcp4->Connect (Tcp4, &TcpIo->ConnToken.Tcp4Token);
+ } else if (TcpIo->TcpVersion == TCP_VERSION_6) {
+ Tcp6 = TcpIo->Tcp.Tcp6;
+ Status = Tcp6->Connect (Tcp6, &TcpIo->ConnToken.Tcp6Token);
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ while (!TcpIo->IsConnDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+ Tcp4->Poll (Tcp4);
+ } else {
+ Tcp6->Poll (Tcp6);
+ }
+ }
+
+ if (!TcpIo->IsConnDone) {
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+ Tcp4->Cancel (Tcp4, &TcpIo->ConnToken.Tcp4Token.CompletionToken);
+ } else {
+ Tcp6->Cancel (Tcp6, &TcpIo->ConnToken.Tcp6Token.CompletionToken);
+ }
+ Status = EFI_TIMEOUT;
+ } else {
+ Status = TcpIo->ConnToken.Tcp4Token.CompletionToken.Status;
+ }
+
+ return Status;
+}
+
+/**
+ Accept the incomding request from the other endpoint of the TCP socket.
+
+ @param[in, out] TcpIo The TcpIo wrapping the TCP socket.
+ @param[in] Timeout The time to wait for connection done. Set to NULL for infinite wait.
+
+
+ @retval EFI_SUCCESS Connect to the other endpoint of the TCP socket
+ successfully.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_UNSUPPORTED One or more of the control options are not
+ supported in the implementation.
+
+ @retval EFI_TIMEOUT Failed to connect to the other endpoint of the
+ TCP socket in the specified time period.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpIoAccept (
+ IN OUT TCP_IO *TcpIo,
+ IN EFI_EVENT Timeout OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_GUID *ProtocolGuid;
+ EFI_TCP4_PROTOCOL *Tcp4;
+ EFI_TCP6_PROTOCOL *Tcp6;
+
+ if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TcpIo->IsListenDone = FALSE;
+
+ Tcp4 = NULL;
+ Tcp6 = NULL;
+
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+ Tcp4 = TcpIo->Tcp.Tcp4;
+ Status = Tcp4->Accept (Tcp4, &TcpIo->ListenToken.Tcp4Token);
+ } else if (TcpIo->TcpVersion == TCP_VERSION_6) {
+ Tcp6 = TcpIo->Tcp.Tcp6;
+ Status = Tcp6->Accept (Tcp6, &TcpIo->ListenToken.Tcp6Token);
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ while (!TcpIo->IsListenDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+ Tcp4->Poll (Tcp4);
+ } else {
+ Tcp6->Poll (Tcp6);
+ }
+ }
+
+ if (!TcpIo->IsListenDone) {
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+ Tcp4->Cancel (Tcp4, &TcpIo->ListenToken.Tcp4Token.CompletionToken);
+ } else {
+ Tcp6->Cancel (Tcp6, &TcpIo->ListenToken.Tcp6Token.CompletionToken);
+ }
+ Status = EFI_TIMEOUT;
+ } else {
+ Status = TcpIo->ListenToken.Tcp4Token.CompletionToken.Status;
+ }
+
+ //
+ // The new TCP instance handle created for the established connection is
+ // in ListenToken.
+ //
+ if (!EFI_ERROR (Status)) {
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+ ProtocolGuid = &gEfiTcp4ProtocolGuid;
+ } else {
+ ProtocolGuid = &gEfiTcp6ProtocolGuid;
+ }
+
+ Status = gBS->OpenProtocol (
+ TcpIo->ListenToken.Tcp4Token.NewChildHandle,
+ ProtocolGuid,
+ (VOID **) (&TcpIo->NewTcp.Tcp4),
+ TcpIo->Image,
+ TcpIo->Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ }
+
+ return Status;
+}
+
+/**
+ Reset the socket.
+
+ @param[in, out] TcpIo The TcpIo wrapping the TCP socket.
+
+**/
+VOID
+EFIAPI
+TcpIoReset (
+ IN OUT TCP_IO *TcpIo
+ )
+{
+ EFI_TCP4_PROTOCOL *Tcp4;
+ EFI_TCP6_PROTOCOL *Tcp6;
+ EFI_STATUS Status;
+
+ if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)) {
+ return ;
+ }
+
+ TcpIo->IsCloseDone = FALSE;
+ Tcp4 = NULL;
+ Tcp6 = NULL;
+
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+ TcpIo->CloseToken.Tcp4Token.AbortOnClose = TRUE;
+ Tcp4 = TcpIo->Tcp.Tcp4;
+ Status = Tcp4->Close (Tcp4, &TcpIo->CloseToken.Tcp4Token);
+ } else if (TcpIo->TcpVersion == TCP_VERSION_6) {
+ TcpIo->CloseToken.Tcp6Token.AbortOnClose = TRUE;
+ Tcp6 = TcpIo->Tcp.Tcp6;
+ Status = Tcp6->Close (Tcp6, &TcpIo->CloseToken.Tcp6Token);
+ } else {
+ return ;
+ }
+
+ if (EFI_ERROR (Status)) {
+ return ;
+ }
+
+ while (!TcpIo->IsCloseDone) {
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+ Tcp4->Poll (Tcp4);
+ } else {
+ Tcp6->Poll (Tcp6);
+ }
+ }
+}
+
+
+/**
+ Transmit the Packet to the other endpoint of the socket.
+
+ @param[in] TcpIo The TcpIo wrapping the TCP socket.
+ @param[in] Packet The packet to transmit.
+
+ @retval EFI_SUCCESS The packet is trasmitted.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_UNSUPPORTED One or more of the control options are not
+ supported in the implementation.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpIoTransmit (
+ IN TCP_IO *TcpIo,
+ IN NET_BUF *Packet
+ )
+{
+ EFI_STATUS Status;
+ VOID *Data;
+ EFI_TCP4_PROTOCOL *Tcp4;
+ EFI_TCP6_PROTOCOL *Tcp6;
+ UINTN Size;
+
+ if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)|| (Packet == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+
+ Size = sizeof (EFI_TCP4_TRANSMIT_DATA) +
+ (Packet->BlockOpNum - 1) * sizeof (EFI_TCP4_FRAGMENT_DATA);
+ } else if (TcpIo->TcpVersion == TCP_VERSION_6) {
+ Size = sizeof (EFI_TCP6_TRANSMIT_DATA) +
+ (Packet->BlockOpNum - 1) * sizeof (EFI_TCP6_FRAGMENT_DATA);
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+
+ Data = AllocatePool (Size);
+ if (Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ((EFI_TCP4_TRANSMIT_DATA *) Data)->Push = TRUE;
+ ((EFI_TCP4_TRANSMIT_DATA *) Data)->Urgent = FALSE;
+ ((EFI_TCP4_TRANSMIT_DATA *) Data)->DataLength = Packet->TotalSize;
+
+ //
+ // Build the fragment table.
+ //
+ ((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount = Packet->BlockOpNum;
+
+ NetbufBuildExt (
+ Packet,
+ (NET_FRAGMENT *) &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentTable[0],
+ &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount
+ );
+
+ Tcp4 = NULL;
+ Tcp6 = NULL;
+ Status = EFI_DEVICE_ERROR;
+
+ //
+ // Trasnmit the packet.
+ //
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+ TcpIo->TxToken.Tcp4Token.Packet.TxData = (EFI_TCP4_TRANSMIT_DATA *) Data;
+ Tcp4 = TcpIo->Tcp.Tcp4;
+ if (TcpIo->IsListenDone) {
+ Tcp4 = TcpIo->NewTcp.Tcp4;
+ }
+
+ if (Tcp4 == NULL) {
+ goto ON_EXIT;
+ }
+
+ Status = Tcp4->Transmit (Tcp4, &TcpIo->TxToken.Tcp4Token);
+ } else {
+ TcpIo->TxToken.Tcp6Token.Packet.TxData = (EFI_TCP6_TRANSMIT_DATA *) Data;
+ Tcp6 = TcpIo->Tcp.Tcp6;
+ if (TcpIo->IsListenDone) {
+ Tcp6 = TcpIo->NewTcp.Tcp6;
+ }
+
+ if (Tcp6 == NULL) {
+ goto ON_EXIT;
+ }
+
+ Status = Tcp6->Transmit (Tcp6, &TcpIo->TxToken.Tcp6Token);
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ while (!TcpIo->IsTxDone) {
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+ Tcp4->Poll (Tcp4);
+ } else {
+ Tcp6->Poll (Tcp6);
+ }
+ }
+
+ TcpIo->IsTxDone = FALSE;
+ Status = TcpIo->TxToken.Tcp4Token.CompletionToken.Status;
+
+ON_EXIT:
+
+ FreePool (Data);
+
+ return Status;
+}
+
+/**
+ Receive data from the socket.
+
+ @param[in, out] TcpIo The TcpIo which wraps the socket to be destroyed.
+ @param[in] Packet The buffer to hold the data copy from the socket rx buffer.
+ @param[in] AsyncMode Is this receive asyncronous or not.
+ @param[in] Timeout The time to wait for receiving the amount of data the Packet
+ can hold. Set to NULL for infinite wait.
+
+ @retval EFI_SUCCESS The required amount of data is received from the socket.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate momery.
+ @retval EFI_TIMEOUT Failed to receive the required amount of data in the
+ specified time period.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpIoReceive (
+ IN OUT TCP_IO *TcpIo,
+ IN NET_BUF *Packet,
+ IN BOOLEAN AsyncMode,
+ IN EFI_EVENT Timeout OPTIONAL
+ )
+{
+ EFI_TCP4_PROTOCOL *Tcp4;
+ EFI_TCP6_PROTOCOL *Tcp6;
+ EFI_TCP4_RECEIVE_DATA *RxData;
+ EFI_STATUS Status;
+ NET_FRAGMENT *Fragment;
+ UINT32 FragmentCount;
+ UINT32 CurrentFragment;
+
+ if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)|| (Packet == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ RxData = TcpIo->RxToken.Tcp4Token.Packet.RxData;
+ if (RxData == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Tcp4 = NULL;
+ Tcp6 = NULL;
+
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+ Tcp4 = TcpIo->Tcp.Tcp4;
+
+ if (TcpIo->IsListenDone) {
+ Tcp4 = TcpIo->NewTcp.Tcp4;
+ }
+
+ if (Tcp4 == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ } else if (TcpIo->TcpVersion == TCP_VERSION_6) {
+ Tcp6 = TcpIo->Tcp.Tcp6;
+
+ if (TcpIo->IsListenDone) {
+ Tcp6 = TcpIo->NewTcp.Tcp6;
+ }
+
+ if (Tcp6 == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+
+ FragmentCount = Packet->BlockOpNum;
+ Fragment = AllocatePool (FragmentCount * sizeof (NET_FRAGMENT));
+ if (Fragment == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ //
+ // Build the fragment table.
+ //
+ NetbufBuildExt (Packet, Fragment, &FragmentCount);
+
+ RxData->FragmentCount = 1;
+ CurrentFragment = 0;
+ Status = EFI_SUCCESS;
+
+ while (CurrentFragment < FragmentCount) {
+ RxData->DataLength = Fragment[CurrentFragment].Len;
+ RxData->FragmentTable[0].FragmentLength = Fragment[CurrentFragment].Len;
+ RxData->FragmentTable[0].FragmentBuffer = Fragment[CurrentFragment].Bulk;
+
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+ Status = Tcp4->Receive (Tcp4, &TcpIo->RxToken.Tcp4Token);
+ } else {
+ Status = Tcp6->Receive (Tcp6, &TcpIo->RxToken.Tcp6Token);
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ while (!TcpIo->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {
+ //
+ // Poll until some data is received or an error occurs.
+ //
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+ Tcp4->Poll (Tcp4);
+ } else {
+ Tcp6->Poll (Tcp6);
+ }
+ }
+
+ if (!TcpIo->IsRxDone) {
+ //
+ // Timeout occurs, cancel the receive request.
+ //
+ if (TcpIo->TcpVersion == TCP_VERSION_4) {
+ Tcp4->Cancel (Tcp4, &TcpIo->RxToken.Tcp4Token.CompletionToken);
+ } else {
+ Tcp6->Cancel (Tcp6, &TcpIo->RxToken.Tcp6Token.CompletionToken);
+ }
+
+ Status = EFI_TIMEOUT;
+ goto ON_EXIT;
+ } else {
+ TcpIo->IsRxDone = FALSE;
+ }
+
+ Status = TcpIo->RxToken.Tcp4Token.CompletionToken.Status;
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Fragment[CurrentFragment].Len -= RxData->FragmentTable[0].FragmentLength;
+ if (Fragment[CurrentFragment].Len == 0) {
+ CurrentFragment++;
+ } else {
+ Fragment[CurrentFragment].Bulk += RxData->FragmentTable[0].FragmentLength;
+ }
+ }
+
+ON_EXIT:
+
+ if (Fragment != NULL) {
+ FreePool (Fragment);
+ }
+
+ return Status;
+}
diff --git a/NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.inf b/NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.inf
new file mode 100644
index 0000000000..66873d7837
--- /dev/null
+++ b/NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.inf
@@ -0,0 +1,44 @@
+## @file
+# This library instance provides TCP services by EFI TCPv4/TCPv6 Protocols.
+#
+# Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxeTcpIoLib
+ MODULE_UNI_FILE = DxeTcpIoLib.uni
+ FILE_GUID = D4608509-1AB0-4cc7-827A-AB8E1E7BD3E6
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = TcpIoLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ DxeTcpIoLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ BaseMemoryLib
+
+[Protocols]
+ gEfiTcp4ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiTcp4ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiTcp6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiTcp6ProtocolGuid ## SOMETIMES_CONSUMES
diff --git a/NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.uni b/NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.uni
new file mode 100644
index 0000000000..79ad484a65
--- /dev/null
+++ b/NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.uni
@@ -0,0 +1,16 @@
+// /** @file
+// This library instance provides TCP services by EFI TCPv4/TCPv6 Protocols.
+//
+// This library instance provides TCP services by EFI TCPv4/TCPv6 Protocols.
+//
+// Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Provides TCP services by EFI TCPv4/TCPv6 Protocols"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This library instance provides TCP services by EFI TCPv4/TCPv6 Protocols."
+
diff --git a/NetworkPkg/Library/DxeUdpIoLib/DxeUdpIoLib.c b/NetworkPkg/Library/DxeUdpIoLib/DxeUdpIoLib.c
new file mode 100644
index 0000000000..155cb31049
--- /dev/null
+++ b/NetworkPkg/Library/DxeUdpIoLib/DxeUdpIoLib.c
@@ -0,0 +1,1119 @@
+/** @file
+ Help functions to access UDP service, it is used by both the DHCP and MTFTP.
+
+Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <Protocol/Udp4.h>
+#include <Protocol/Udp6.h>
+
+#include <Library/UdpIoLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DpcLib.h>
+
+
+/**
+ Free a UDP_TX_TOKEN. The TX event is closed.
+
+ @param[in] TxToken The UDP_TX_TOKEN to release.
+
+**/
+VOID
+UdpIoFreeTxToken (
+ IN UDP_TX_TOKEN *TxToken
+ )
+{
+
+ if (TxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
+ gBS->CloseEvent (TxToken->Token.Udp4.Event);
+ } else if (TxToken->UdpIo->UdpVersion == UDP_IO_UDP6_VERSION) {
+ gBS->CloseEvent (TxToken->Token.Udp6.Event);
+ } else {
+ ASSERT (FALSE);
+ }
+
+ FreePool (TxToken);
+}
+
+/**
+ Free a UDP_RX_TOKEN. The RX event is closed.
+
+ @param[in] RxToken The UDP_RX_TOKEN to release.
+
+**/
+VOID
+UdpIoFreeRxToken (
+ IN UDP_RX_TOKEN *RxToken
+ )
+{
+ if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
+ gBS->CloseEvent (RxToken->Token.Udp4.Event);
+ } else if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP6_VERSION) {
+ gBS->CloseEvent (RxToken->Token.Udp6.Event);
+ } else {
+ ASSERT (FALSE);
+ }
+
+ FreePool (RxToken);
+}
+
+/**
+ The callback function when the packet is sent by UDP.
+
+ It will remove the packet from the local list then call
+ the packet owner's callback function set by UdpIoSendDatagram.
+
+ @param[in] Context The UDP TX Token.
+
+**/
+VOID
+EFIAPI
+UdpIoOnDgramSentDpc (
+ IN VOID *Context
+ )
+{
+ UDP_TX_TOKEN *TxToken;
+
+ TxToken = (UDP_TX_TOKEN *) Context;
+ ASSERT (TxToken->Signature == UDP_IO_TX_SIGNATURE);
+ ASSERT ((TxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
+ (TxToken->UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
+
+ RemoveEntryList (&TxToken->Link);
+
+ if (TxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
+ TxToken->CallBack (TxToken->Packet, NULL, TxToken->Token.Udp4.Status, TxToken->Context);
+ } else {
+ TxToken->CallBack (TxToken->Packet, NULL, TxToken->Token.Udp6.Status, TxToken->Context);
+ }
+
+ UdpIoFreeTxToken (TxToken);
+}
+
+/**
+ Request UdpIoOnDgramSentDpc as a DPC at TPL_CALLBACK.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The UDP TX Token.
+
+**/
+VOID
+EFIAPI
+UdpIoOnDgramSent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request UdpIoOnDgramSentDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, UdpIoOnDgramSentDpc, Context);
+}
+
+/**
+ Recycle the received UDP data.
+
+ @param[in] Context The UDP_RX_TOKEN.
+
+**/
+VOID
+EFIAPI
+UdpIoRecycleDgram (
+ IN VOID *Context
+ )
+{
+ UDP_RX_TOKEN *RxToken;
+
+ RxToken = (UDP_RX_TOKEN *) Context;
+
+ if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
+ gBS->SignalEvent (RxToken->Token.Udp4.Packet.RxData->RecycleSignal);
+ } else if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP6_VERSION) {
+ gBS->SignalEvent (RxToken->Token.Udp6.Packet.RxData->RecycleSignal);
+ } else {
+ ASSERT (FALSE);
+ }
+
+ UdpIoFreeRxToken (RxToken);
+}
+
+/**
+ The event handle for UDP receive request.
+
+ It will build a NET_BUF from the recieved UDP data, then deliver it
+ to the receiver.
+
+ @param[in] Context The UDP RX token.
+
+**/
+VOID
+EFIAPI
+UdpIoOnDgramRcvdDpc (
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ VOID *Token;
+ VOID *RxData;
+ VOID *Session;
+ UDP_RX_TOKEN *RxToken;
+ UDP_END_POINT EndPoint;
+ NET_BUF *Netbuf;
+
+ RxToken = (UDP_RX_TOKEN *) Context;
+
+ ZeroMem (&EndPoint, sizeof(UDP_END_POINT));
+
+ ASSERT ((RxToken->Signature == UDP_IO_RX_SIGNATURE) &&
+ (RxToken == RxToken->UdpIo->RecvRequest));
+
+ ASSERT ((RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
+ (RxToken->UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
+
+ //
+ // Clear the receive request first in case that the caller
+ // wants to restart the receive in the callback.
+ //
+ RxToken->UdpIo->RecvRequest = NULL;
+
+ if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
+ Token = &RxToken->Token.Udp4;
+ RxData = ((EFI_UDP4_COMPLETION_TOKEN *) Token)->Packet.RxData;
+ Status = ((EFI_UDP4_COMPLETION_TOKEN *) Token)->Status;
+ } else {
+ Token = &RxToken->Token.Udp6;
+ RxData = ((EFI_UDP6_COMPLETION_TOKEN *) Token)->Packet.RxData;
+ Status = ((EFI_UDP6_COMPLETION_TOKEN *) Token)->Status;
+ }
+
+ if (EFI_ERROR (Status) || RxData == NULL) {
+ if (Status != EFI_ABORTED) {
+ //
+ // Invoke the CallBack only if the reception is not actively aborted.
+ //
+ RxToken->CallBack (NULL, NULL, Status, RxToken->Context);
+ }
+
+ UdpIoFreeRxToken (RxToken);
+ return;
+ }
+
+ //
+ // Build a NET_BUF from the UDP receive data, then deliver it up.
+ //
+ if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
+ if (((EFI_UDP4_RECEIVE_DATA *) RxData)->DataLength == 0) {
+ //
+ // Discard zero length data payload packet.
+ //
+ goto Resume;
+ }
+
+ Netbuf = NetbufFromExt (
+ (NET_FRAGMENT *)((EFI_UDP4_RECEIVE_DATA *) RxData)->FragmentTable,
+ ((EFI_UDP4_RECEIVE_DATA *) RxData)->FragmentCount,
+ 0,
+ (UINT32) RxToken->HeadLen,
+ UdpIoRecycleDgram,
+ RxToken
+ );
+
+ if (Netbuf == NULL) {
+ gBS->SignalEvent (((EFI_UDP4_RECEIVE_DATA *) RxData)->RecycleSignal);
+ RxToken->CallBack (NULL, NULL, EFI_OUT_OF_RESOURCES, RxToken->Context);
+
+ UdpIoFreeRxToken (RxToken);
+ return;
+ }
+
+ Session = &((EFI_UDP4_RECEIVE_DATA *) RxData)->UdpSession;
+ EndPoint.LocalPort = ((EFI_UDP4_SESSION_DATA *) Session)->DestinationPort;
+ EndPoint.RemotePort = ((EFI_UDP4_SESSION_DATA *) Session)->SourcePort;
+
+ CopyMem (
+ &EndPoint.LocalAddr,
+ &((EFI_UDP4_SESSION_DATA *) Session)->DestinationAddress,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+
+ CopyMem (
+ &EndPoint.RemoteAddr,
+ &((EFI_UDP4_SESSION_DATA *) Session)->SourceAddress,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+
+ EndPoint.LocalAddr.Addr[0] = NTOHL (EndPoint.LocalAddr.Addr[0]);
+ EndPoint.RemoteAddr.Addr[0] = NTOHL (EndPoint.RemoteAddr.Addr[0]);
+ } else {
+ if (((EFI_UDP6_RECEIVE_DATA *) RxData)->DataLength == 0) {
+ //
+ // Discard zero length data payload packet.
+ //
+ goto Resume;
+ }
+
+ Netbuf = NetbufFromExt (
+ (NET_FRAGMENT *)((EFI_UDP6_RECEIVE_DATA *) RxData)->FragmentTable,
+ ((EFI_UDP6_RECEIVE_DATA *) RxData)->FragmentCount,
+ 0,
+ (UINT32) RxToken->HeadLen,
+ UdpIoRecycleDgram,
+ RxToken
+ );
+
+ if (Netbuf == NULL) {
+ gBS->SignalEvent (((EFI_UDP6_RECEIVE_DATA *) RxData)->RecycleSignal);
+ RxToken->CallBack (NULL, NULL, EFI_OUT_OF_RESOURCES, RxToken->Context);
+
+ UdpIoFreeRxToken (RxToken);
+ return;
+ }
+
+ Session = &((EFI_UDP6_RECEIVE_DATA *) RxData)->UdpSession;
+ EndPoint.LocalPort = ((EFI_UDP6_SESSION_DATA *) Session)->DestinationPort;
+ EndPoint.RemotePort = ((EFI_UDP6_SESSION_DATA *) Session)->SourcePort;
+
+ CopyMem (
+ &EndPoint.LocalAddr,
+ &((EFI_UDP6_SESSION_DATA *) Session)->DestinationAddress,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+
+ CopyMem (
+ &EndPoint.RemoteAddr,
+ &((EFI_UDP6_SESSION_DATA *) Session)->SourceAddress,
+ sizeof (EFI_IPv6_ADDRESS)
+ );
+
+ Ip6Swap128 (&EndPoint.LocalAddr.v6);
+ Ip6Swap128 (&EndPoint.RemoteAddr.v6);
+ }
+
+ RxToken->CallBack (Netbuf, &EndPoint, EFI_SUCCESS, RxToken->Context);
+ return;
+
+Resume:
+ if (RxToken->UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
+ gBS->SignalEvent (((EFI_UDP4_RECEIVE_DATA *) RxData)->RecycleSignal);
+ RxToken->UdpIo->Protocol.Udp4->Receive (RxToken->UdpIo->Protocol.Udp4, &RxToken->Token.Udp4);
+ } else {
+ gBS->SignalEvent (((EFI_UDP6_RECEIVE_DATA *) RxData)->RecycleSignal);
+ RxToken->UdpIo->Protocol.Udp6->Receive (RxToken->UdpIo->Protocol.Udp6, &RxToken->Token.Udp6);
+ }
+}
+
+/**
+ Request UdpIoOnDgramRcvdDpc() as a DPC at TPL_CALLBACK.
+
+ @param[in] Event The UDP receive request event.
+ @param[in] Context The UDP RX token.
+
+**/
+VOID
+EFIAPI
+UdpIoOnDgramRcvd (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request UdpIoOnDgramRcvdDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, UdpIoOnDgramRcvdDpc, Context);
+}
+
+/**
+ Create a UDP_RX_TOKEN to wrap the request.
+
+ @param[in] UdpIo The UdpIo to receive packets from.
+ @param[in] CallBack The function to call when receive finished.
+ @param[in] Context The opaque parameter to the CallBack.
+ @param[in] HeadLen The head length to reserver for the packet.
+
+ @return The Wrapped request or NULL if failed to allocate resources or some errors happened.
+
+**/
+UDP_RX_TOKEN *
+UdpIoCreateRxToken (
+ IN UDP_IO *UdpIo,
+ IN UDP_IO_CALLBACK CallBack,
+ IN VOID *Context,
+ IN UINT32 HeadLen
+ )
+{
+ UDP_RX_TOKEN *Token;
+ EFI_STATUS Status;
+
+ ASSERT ((UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
+ (UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
+
+ Token = AllocatePool (sizeof (UDP_RX_TOKEN));
+
+ if (Token == NULL) {
+ return NULL;
+ }
+
+ Token->Signature = UDP_IO_RX_SIGNATURE;
+ Token->UdpIo = UdpIo;
+ Token->CallBack = CallBack;
+ Token->Context = Context;
+ Token->HeadLen = HeadLen;
+
+ if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
+
+ Token->Token.Udp4.Status = EFI_NOT_READY;
+ Token->Token.Udp4.Packet.RxData = NULL;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ UdpIoOnDgramRcvd,
+ Token,
+ &Token->Token.Udp4.Event
+ );
+ } else {
+
+ Token->Token.Udp6.Status = EFI_NOT_READY;
+ Token->Token.Udp6.Packet.RxData = NULL;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ UdpIoOnDgramRcvd,
+ Token,
+ &Token->Token.Udp6.Event
+ );
+ }
+
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Token);
+ return NULL;
+ }
+
+ return Token;
+}
+
+/**
+ Wrap a transmit request into a new created UDP_TX_TOKEN.
+
+ If Packet is NULL, then ASSERT().
+ If Udp version is not UDP_IO_UDP4_VERSION or UDP_IO_UDP6_VERSION, then ASSERT().
+
+ @param[in] UdpIo The UdpIo to send packet to.
+ @param[in] Packet The user's packet.
+ @param[in] EndPoint The local and remote access point.
+ @param[in] Gateway The overrided next hop.
+ @param[in] CallBack The function to call when transmission completed.
+ @param[in] Context The opaque parameter to the call back.
+
+ @return The wrapped transmission request or NULL if failed to allocate resources
+ or for some errors.
+
+**/
+UDP_TX_TOKEN *
+UdpIoCreateTxToken (
+ IN UDP_IO *UdpIo,
+ IN NET_BUF *Packet,
+ IN UDP_END_POINT *EndPoint OPTIONAL,
+ IN EFI_IP_ADDRESS *Gateway OPTIONAL,
+ IN UDP_IO_CALLBACK CallBack,
+ IN VOID *Context
+ )
+{
+ UDP_TX_TOKEN *TxToken;
+ VOID *Token;
+ VOID *Data;
+ EFI_STATUS Status;
+ UINT32 Count;
+ UINTN Size;
+ IP4_ADDR Ip;
+
+ ASSERT (Packet != NULL);
+ ASSERT ((UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
+ (UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
+
+ if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
+ Size = sizeof (UDP_TX_TOKEN) + sizeof (EFI_UDP4_FRAGMENT_DATA) * (Packet->BlockOpNum - 1);
+ } else {
+ Size = sizeof (UDP_TX_TOKEN) + sizeof (EFI_UDP6_FRAGMENT_DATA) * (Packet->BlockOpNum - 1);
+ }
+
+ TxToken = AllocatePool (Size);
+
+ if (TxToken == NULL) {
+ return NULL;
+ }
+
+ TxToken->Signature = UDP_IO_TX_SIGNATURE;
+ InitializeListHead (&TxToken->Link);
+
+ TxToken->UdpIo = UdpIo;
+ TxToken->CallBack = CallBack;
+ TxToken->Packet = Packet;
+ TxToken->Context = Context;
+
+ Token = &(TxToken->Token);
+ Count = Packet->BlockOpNum;
+
+ if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
+
+ ((EFI_UDP4_COMPLETION_TOKEN *) Token)->Status = EFI_NOT_READY;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ UdpIoOnDgramSent,
+ TxToken,
+ &((EFI_UDP4_COMPLETION_TOKEN *) Token)->Event
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (TxToken);
+ return NULL;
+ }
+
+ Data = &(TxToken->Data.Udp4);
+ ((EFI_UDP4_COMPLETION_TOKEN *) Token)->Packet.TxData = Data;
+
+ ((EFI_UDP4_TRANSMIT_DATA *) Data)->UdpSessionData = NULL;
+ ((EFI_UDP4_TRANSMIT_DATA *) Data)->GatewayAddress = NULL;
+ ((EFI_UDP4_TRANSMIT_DATA *) Data)->DataLength = Packet->TotalSize;
+
+ NetbufBuildExt (
+ Packet,
+ (NET_FRAGMENT *)((EFI_UDP4_TRANSMIT_DATA *) Data)->FragmentTable,
+ &Count
+ );
+
+ ((EFI_UDP4_TRANSMIT_DATA *) Data)->FragmentCount = Count;
+
+ if (EndPoint != NULL) {
+ Ip = HTONL (EndPoint->LocalAddr.Addr[0]);
+ CopyMem (
+ &TxToken->Session.Udp4.SourceAddress,
+ &Ip,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+
+ Ip = HTONL (EndPoint->RemoteAddr.Addr[0]);
+ CopyMem (
+ &TxToken->Session.Udp4.DestinationAddress,
+ &Ip,
+ sizeof (EFI_IPv4_ADDRESS)
+ );
+
+ TxToken->Session.Udp4.SourcePort = EndPoint->LocalPort;
+ TxToken->Session.Udp4.DestinationPort = EndPoint->RemotePort;
+ ((EFI_UDP4_TRANSMIT_DATA *) Data)->UdpSessionData = &(TxToken->Session.Udp4);
+ }
+
+ if (Gateway != NULL && (Gateway->Addr[0] != 0)) {
+ Ip = HTONL (Gateway->Addr[0]);
+ CopyMem (&TxToken->Gateway, &Ip, sizeof (EFI_IPv4_ADDRESS));
+ ((EFI_UDP4_TRANSMIT_DATA *) Data)->GatewayAddress = &TxToken->Gateway;
+ }
+
+ } else {
+
+ ((EFI_UDP6_COMPLETION_TOKEN *) Token)->Status = EFI_NOT_READY;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ UdpIoOnDgramSent,
+ TxToken,
+ &((EFI_UDP6_COMPLETION_TOKEN *) Token)->Event
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (TxToken);
+ return NULL;
+ }
+
+ Data = &(TxToken->Data.Udp6);
+ ((EFI_UDP6_COMPLETION_TOKEN *) Token)->Packet.TxData = Data;
+ ((EFI_UDP6_TRANSMIT_DATA *) Data)->UdpSessionData = NULL;
+ ((EFI_UDP6_TRANSMIT_DATA *) Data)->DataLength = Packet->TotalSize;
+
+ NetbufBuildExt (
+ Packet,
+ (NET_FRAGMENT *)((EFI_UDP6_TRANSMIT_DATA *) Data)->FragmentTable,
+ &Count
+ );
+
+ ((EFI_UDP6_TRANSMIT_DATA *) Data)->FragmentCount = Count;
+
+ if (EndPoint != NULL) {
+ CopyMem (
+ &TxToken->Session.Udp6.SourceAddress,
+ &EndPoint->LocalAddr.v6,
+ sizeof(EFI_IPv6_ADDRESS)
+ );
+
+ CopyMem (
+ &TxToken->Session.Udp6.DestinationAddress,
+ &EndPoint->RemoteAddr.v6,
+ sizeof(EFI_IPv6_ADDRESS)
+ );
+
+ TxToken->Session.Udp6.SourcePort = EndPoint->LocalPort;
+ TxToken->Session.Udp6.DestinationPort = EndPoint->RemotePort;
+ ((EFI_UDP6_TRANSMIT_DATA *) Data)->UdpSessionData = &(TxToken->Session.Udp6);
+ }
+ }
+
+ return TxToken;
+}
+
+/**
+ Creates a UDP_IO to access the UDP service. It creates and configures
+ a UDP child.
+
+ If Configure is NULL, then ASSERT().
+ If Udp version is not UDP_IO_UDP4_VERSION or UDP_IO_UDP6_VERSION, then ASSERT().
+
+ It locates the UDP service binding prototype on the Controller parameter
+ uses the UDP service binding prototype to create a UDP child (also known as
+ a UDP instance) configures the UDP child by calling Configure function prototype.
+ Any failures in creating or configuring the UDP child return NULL for failure.
+
+ @param[in] Controller The controller that has the UDP service binding.
+ protocol installed.
+ @param[in] ImageHandle The image handle for the driver.
+ @param[in] Configure The function to configure the created UDP child.
+ @param[in] UdpVersion The UDP protocol version, UDP4 or UDP6.
+ @param[in] Context The opaque parameter for the Configure funtion.
+
+ @return Newly-created UDP_IO or NULL if failed.
+
+**/
+UDP_IO *
+EFIAPI
+UdpIoCreateIo (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ImageHandle,
+ IN UDP_IO_CONFIG Configure,
+ IN UINT8 UdpVersion,
+ IN VOID *Context
+ )
+{
+ UDP_IO *UdpIo;
+ EFI_STATUS Status;
+
+ ASSERT (Configure != NULL);
+ ASSERT ((UdpVersion == UDP_IO_UDP4_VERSION) || (UdpVersion == UDP_IO_UDP6_VERSION));
+
+ UdpIo = AllocatePool (sizeof (UDP_IO));
+
+ if (UdpIo == NULL) {
+ return NULL;
+ }
+
+ UdpIo->UdpVersion = UdpVersion;
+ UdpIo->Signature = UDP_IO_SIGNATURE;
+ InitializeListHead (&UdpIo->Link);
+ UdpIo->RefCnt = 1;
+
+ UdpIo->Controller = Controller;
+ UdpIo->Image = ImageHandle;
+
+ InitializeListHead (&UdpIo->SentDatagram);
+ UdpIo->RecvRequest = NULL;
+ UdpIo->UdpHandle = NULL;
+
+ if (UdpVersion == UDP_IO_UDP4_VERSION) {
+ //
+ // Create a UDP child then open and configure it
+ //
+ Status = NetLibCreateServiceChild (
+ Controller,
+ ImageHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ &UdpIo->UdpHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto FREE_MEM;
+ }
+
+ Status = gBS->OpenProtocol (
+ UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &UdpIo->Protocol.Udp4,
+ ImageHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto FREE_CHILD;
+ }
+
+ if (EFI_ERROR (Configure (UdpIo, Context))) {
+ goto CLOSE_PROTOCOL;
+ }
+
+ Status = UdpIo->Protocol.Udp4->GetModeData (
+ UdpIo->Protocol.Udp4,
+ NULL,
+ NULL,
+ NULL,
+ &UdpIo->SnpMode
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto CLOSE_PROTOCOL;
+ }
+
+ } else {
+
+ Status = NetLibCreateServiceChild (
+ Controller,
+ ImageHandle,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ &UdpIo->UdpHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto FREE_MEM;
+ }
+
+ Status = gBS->OpenProtocol (
+ UdpIo->UdpHandle,
+ &gEfiUdp6ProtocolGuid,
+ (VOID **) &UdpIo->Protocol.Udp6,
+ ImageHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto FREE_CHILD;
+ }
+
+ if (EFI_ERROR (Configure (UdpIo, Context))) {
+ goto CLOSE_PROTOCOL;
+ }
+
+ Status = UdpIo->Protocol.Udp6->GetModeData (
+ UdpIo->Protocol.Udp6,
+ NULL,
+ NULL,
+ NULL,
+ &UdpIo->SnpMode
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto CLOSE_PROTOCOL;
+ }
+ }
+
+ return UdpIo;
+
+CLOSE_PROTOCOL:
+ if (UdpVersion == UDP_IO_UDP4_VERSION) {
+ gBS->CloseProtocol (UdpIo->UdpHandle, &gEfiUdp4ProtocolGuid, ImageHandle, Controller);
+ } else {
+ gBS->CloseProtocol (UdpIo->UdpHandle, &gEfiUdp6ProtocolGuid, ImageHandle, Controller);
+ }
+
+FREE_CHILD:
+ if (UdpVersion == UDP_IO_UDP4_VERSION) {
+ NetLibDestroyServiceChild (
+ Controller,
+ ImageHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ UdpIo->UdpHandle
+ );
+ } else {
+ NetLibDestroyServiceChild (
+ Controller,
+ ImageHandle,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ UdpIo->UdpHandle
+ );
+ }
+
+FREE_MEM:
+ FreePool (UdpIo);
+ return NULL;
+}
+
+/**
+ Cancel all the sent datagram that pass the selection criteria of ToCancel.
+
+ If ToCancel is NULL, all the datagrams are cancelled.
+ If Udp version is not UDP_IO_UDP4_VERSION or UDP_IO_UDP6_VERSION, then ASSERT().
+
+ @param[in] UdpIo The UDP_IO to cancel packet.
+ @param[in] IoStatus The IoStatus to return to the packet owners.
+ @param[in] ToCancel The select funtion to test whether to cancel this
+ packet or not.
+ @param[in] Context The opaque parameter to the ToCancel.
+
+**/
+VOID
+EFIAPI
+UdpIoCancelDgrams (
+ IN UDP_IO *UdpIo,
+ IN EFI_STATUS IoStatus,
+ IN UDP_IO_TO_CANCEL ToCancel, OPTIONAL
+ IN VOID *Context OPTIONAL
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ UDP_TX_TOKEN *TxToken;
+
+ ASSERT ((UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
+ (UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &UdpIo->SentDatagram) {
+ TxToken = NET_LIST_USER_STRUCT (Entry, UDP_TX_TOKEN, Link);
+
+ if ((ToCancel == NULL) || (ToCancel (TxToken, Context))) {
+
+ if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
+ UdpIo->Protocol.Udp4->Cancel (UdpIo->Protocol.Udp4, &TxToken->Token.Udp4);
+ } else {
+ UdpIo->Protocol.Udp6->Cancel (UdpIo->Protocol.Udp6, &TxToken->Token.Udp6);
+ }
+ }
+ }
+}
+
+/**
+ Free the UDP_IO and all its related resources.
+
+ If Udp version is not UDP_IO_UDP4_VERSION or UDP_IO_UDP6_VERSION, then ASSERT().
+
+ The function will cancel all sent datagram and receive request.
+
+ @param[in] UdpIo The UDP_IO to free.
+
+ @retval EFI_SUCCESS The UDP_IO is freed.
+ @retval Others Failed to free UDP_IO.
+
+**/
+EFI_STATUS
+EFIAPI
+UdpIoFreeIo (
+ IN UDP_IO *UdpIo
+ )
+{
+ EFI_STATUS Status;
+ UDP_RX_TOKEN *RxToken;
+
+ ASSERT ((UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
+ (UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
+
+ //
+ // Cancel all the sent datagram and receive requests. The
+ // callbacks of transmit requests are executed to allow the
+ // caller to release the resource. The callback of receive
+ // request are NOT executed. This is because it is most
+ // likely that the current user of the UDP IO port is closing
+ // itself.
+ //
+ UdpIoCancelDgrams (UdpIo, EFI_ABORTED, NULL, NULL);
+
+ if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
+
+ if ((RxToken = UdpIo->RecvRequest) != NULL) {
+ Status = UdpIo->Protocol.Udp4->Cancel (UdpIo->Protocol.Udp4, &RxToken->Token.Udp4);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Close then destroy the Udp4 child
+ //
+ Status = gBS->CloseProtocol (
+ UdpIo->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ UdpIo->Image,
+ UdpIo->Controller
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = NetLibDestroyServiceChild (
+ UdpIo->Controller,
+ UdpIo->Image,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ UdpIo->UdpHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ } else {
+
+ if ((RxToken = UdpIo->RecvRequest) != NULL) {
+ Status = UdpIo->Protocol.Udp6->Cancel (UdpIo->Protocol.Udp6, &RxToken->Token.Udp6);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Close then destroy the Udp6 child
+ //
+ Status = gBS->CloseProtocol (
+ UdpIo->UdpHandle,
+ &gEfiUdp6ProtocolGuid,
+ UdpIo->Image,
+ UdpIo->Controller
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = NetLibDestroyServiceChild (
+ UdpIo->Controller,
+ UdpIo->Image,
+ &gEfiUdp6ServiceBindingProtocolGuid,
+ UdpIo->UdpHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if (!IsListEmpty(&UdpIo->Link)) {
+ RemoveEntryList (&UdpIo->Link);
+ }
+
+ FreePool (UdpIo);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Clean up the UDP_IO without freeing it. The function is called when
+ user wants to re-use the UDP_IO later.
+
+ If Udp version is not UDP_IO_UDP4_VERSION or UDP_IO_UDP6_VERSION, then ASSERT().
+
+ It will release all the transmitted datagrams and receive request. It will
+ also configure NULL for the UDP instance.
+
+ @param[in] UdpIo The UDP_IO to clean up.
+
+**/
+VOID
+EFIAPI
+UdpIoCleanIo (
+ IN UDP_IO *UdpIo
+ )
+{
+ UDP_RX_TOKEN *RxToken;
+
+ ASSERT ((UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
+ (UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
+
+ //
+ // Cancel all the sent datagram and receive requests.
+ //
+ UdpIoCancelDgrams (UdpIo, EFI_ABORTED, NULL, NULL);
+
+ if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
+ if ((RxToken = UdpIo->RecvRequest) != NULL) {
+ UdpIo->Protocol.Udp4->Cancel (UdpIo->Protocol.Udp4, &RxToken->Token.Udp4);
+ }
+
+ UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, NULL);
+
+ } else {
+ if ((RxToken = UdpIo->RecvRequest) != NULL) {
+ UdpIo->Protocol.Udp6->Cancel (UdpIo->Protocol.Udp6, &RxToken->Token.Udp6);
+ }
+
+ UdpIo->Protocol.Udp6->Configure (UdpIo->Protocol.Udp6, NULL);
+ }
+}
+
+/**
+ Send a packet through the UDP_IO.
+
+ If Udp version is not UDP_IO_UDP4_VERSION or UDP_IO_UDP6_VERSION, then ASSERT().
+
+ The packet will be wrapped in UDP_TX_TOKEN. Function Callback will be called
+ when the packet is sent. The optional parameter EndPoint overrides the default
+ address pair if specified.
+
+ @param[in] UdpIo The UDP_IO to send the packet through.
+ @param[in] Packet The packet to send.
+ @param[in] EndPoint The local and remote access point. Override the
+ default address pair set during configuration.
+ @param[in] Gateway The gateway to use.
+ @param[in] CallBack The function being called when packet is
+ transmitted or failed.
+ @param[in] Context The opaque parameter passed to CallBack.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the packet.
+ @retval EFI_SUCCESS The packet is successfully delivered to UDP for
+ transmission.
+
+**/
+EFI_STATUS
+EFIAPI
+UdpIoSendDatagram (
+ IN UDP_IO *UdpIo,
+ IN NET_BUF *Packet,
+ IN UDP_END_POINT *EndPoint OPTIONAL,
+ IN EFI_IP_ADDRESS *Gateway OPTIONAL,
+ IN UDP_IO_CALLBACK CallBack,
+ IN VOID *Context
+ )
+{
+ UDP_TX_TOKEN *TxToken;
+ EFI_STATUS Status;
+
+ ASSERT ((UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
+ (UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
+
+ TxToken = UdpIoCreateTxToken (UdpIo, Packet, EndPoint, Gateway, CallBack, Context);
+
+ if (TxToken == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Insert the tx token into SendDatagram list before transmitting it. Remove
+ // it from the list if the returned status is not EFI_SUCCESS.
+ //
+ InsertHeadList (&UdpIo->SentDatagram, &TxToken->Link);
+
+ if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
+ Status = UdpIo->Protocol.Udp4->Transmit (UdpIo->Protocol.Udp4, &TxToken->Token.Udp4);
+ } else {
+ Status = UdpIo->Protocol.Udp6->Transmit (UdpIo->Protocol.Udp6, &TxToken->Token.Udp6);
+ }
+
+ if (EFI_ERROR (Status)) {
+ RemoveEntryList (&TxToken->Link);
+ UdpIoFreeTxToken (TxToken);
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ The select function to cancel a single sent datagram.
+
+ @param[in] Token The UDP_TX_TOKEN to test against
+ @param[in] Context The NET_BUF of the sent datagram
+
+ @retval TRUE The packet is to be cancelled.
+ @retval FALSE The packet is not to be cancelled.
+**/
+BOOLEAN
+EFIAPI
+UdpIoCancelSingleDgram (
+ IN UDP_TX_TOKEN *Token,
+ IN VOID *Context
+ )
+{
+ NET_BUF *Packet;
+
+ Packet = (NET_BUF *) Context;
+
+ if (Token->Packet == Packet) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Cancel a single sent datagram.
+
+ @param[in] UdpIo The UDP_IO to cancel the packet from
+ @param[in] Packet The packet to cancel
+
+**/
+VOID
+EFIAPI
+UdpIoCancelSentDatagram (
+ IN UDP_IO *UdpIo,
+ IN NET_BUF *Packet
+ )
+{
+ UdpIoCancelDgrams (UdpIo, EFI_ABORTED, UdpIoCancelSingleDgram, Packet);
+}
+
+/**
+ Issue a receive request to the UDP_IO.
+
+ If Udp version is not UDP_IO_UDP4_VERSION or UDP_IO_UDP6_VERSION, then ASSERT().
+
+ This function is called when upper-layer needs packet from UDP for processing.
+ Only one receive request is acceptable at a time so a common usage model is
+ to invoke this function inside its Callback function when the former packet
+ is processed.
+
+ @param[in] UdpIo The UDP_IO to receive the packet from.
+ @param[in] CallBack The call back function to execute when the packet
+ is received.
+ @param[in] Context The opaque context passed to Callback.
+ @param[in] HeadLen The length of the upper-layer's protocol header.
+
+ @retval EFI_ALREADY_STARTED There is already a pending receive request. Only
+ one receive request is supported at a time.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources.
+ @retval EFI_SUCCESS The receive request is issued successfully.
+ @retval EFI_UNSUPPORTED The UDP version in UDP_IO is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+UdpIoRecvDatagram (
+ IN UDP_IO *UdpIo,
+ IN UDP_IO_CALLBACK CallBack,
+ IN VOID *Context,
+ IN UINT32 HeadLen
+ )
+{
+ UDP_RX_TOKEN *RxToken;
+ EFI_STATUS Status;
+
+ ASSERT ((UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) ||
+ (UdpIo->UdpVersion == UDP_IO_UDP6_VERSION));
+
+ if (UdpIo->RecvRequest != NULL) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ RxToken = UdpIoCreateRxToken (UdpIo, CallBack, Context, HeadLen);
+
+ if (RxToken == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ UdpIo->RecvRequest = RxToken;
+ if (UdpIo->UdpVersion == UDP_IO_UDP4_VERSION) {
+ Status = UdpIo->Protocol.Udp4->Receive (UdpIo->Protocol.Udp4, &RxToken->Token.Udp4);
+ } else {
+ Status = UdpIo->Protocol.Udp6->Receive (UdpIo->Protocol.Udp6, &RxToken->Token.Udp6);
+ }
+
+ if (EFI_ERROR (Status)) {
+ UdpIo->RecvRequest = NULL;
+ UdpIoFreeRxToken (RxToken);
+ }
+
+ return Status;
+}
diff --git a/NetworkPkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf b/NetworkPkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf
new file mode 100644
index 0000000000..483c2cda02
--- /dev/null
+++ b/NetworkPkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf
@@ -0,0 +1,46 @@
+## @file
+# This library instance provides UDP services by consuming EFI UDPv4/UDPv6 Protocols.
+#
+# Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxeUpdIoLib
+ MODULE_UNI_FILE = DxeUpdIoLib.uni
+ FILE_GUID = 7E615AA1-41EE-49d4-B7E9-1D7A60AA5C8D
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = UdpIoLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ DxeUdpIoLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ BaseMemoryLib
+ DpcLib
+
+[Protocols]
+ gEfiUdp4ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiUdp4ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiUdp6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiUdp6ProtocolGuid ## SOMETIMES_CONSUMES
+
diff --git a/NetworkPkg/Library/DxeUdpIoLib/DxeUpdIoLib.uni b/NetworkPkg/Library/DxeUdpIoLib/DxeUpdIoLib.uni
new file mode 100644
index 0000000000..bc94905995
--- /dev/null
+++ b/NetworkPkg/Library/DxeUdpIoLib/DxeUpdIoLib.uni
@@ -0,0 +1,16 @@
+// /** @file
+// This library instance provides UDP services by consuming EFI UDPv4/UDPv6 Protocols.
+//
+// This library instance provides UDP services by consuming EFI UDPv4/UDPv6 Protocols.
+//
+// Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Provides UDP services by consuming EFI UDPv4/UDPv6 Protocols"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This library instance provides UDP services by consuming EFI UDPv4/UDPv6 Protocols."
+