From: Zhang Lubo Date: Mon, 9 Nov 2015 03:30:42 +0000 (+0000) Subject: NetworkPkg:Enable Http Boot over Ipv6 stack X-Git-Tag: edk2-stable201903~8622 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=b659408b933f40765960e877de3e1f8ceaab52cb NetworkPkg:Enable Http Boot over Ipv6 stack Add new features to support Http boot over ipv6 stack. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Zhang Lubo Reviewed-by: Fu Siyuan Reviewed-by: Ye Ting Reviewed-by: Wu Jiaxin git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@18743 6f19259b-4bc3-4df7-8a09-765794883524 --- diff --git a/NetworkPkg/HttpBootDxe/HttpBootClient.c b/NetworkPkg/HttpBootDxe/HttpBootClient.c index 5669c5f37c..b81b03c960 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootClient.c +++ b/NetworkPkg/HttpBootDxe/HttpBootClient.c @@ -54,14 +54,27 @@ HttpBootUpdateDevicePath ( Node->Ipv4.StaticIpAddress = FALSE; CopyMem (&Node->Ipv4.GatewayIpAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS)); CopyMem (&Node->Ipv4.SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS)); - - TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); - FreePool (Node); - if (TmpDevicePath == NULL) { + } else { + Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH)); + if (Node == NULL) { return EFI_OUT_OF_RESOURCES; } - } else { - ASSERT (FALSE); + Node->Ipv6.Header.Type = MESSAGING_DEVICE_PATH; + Node->Ipv6.Header.SubType = MSG_IPv6_DP; + SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH)); + Node->Ipv6.PrefixLength = IP6_PREFIX_LENGTH; + Node->Ipv6.RemotePort = Private->Port; + Node->Ipv6.Protocol = EFI_IP_PROTO_TCP; + Node->Ipv6.IpAddressOrigin = 0; + CopyMem (&Node->Ipv6.LocalIpAddress, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Node->Ipv6.RemoteIpAddress, &Private->ServerIp.v6, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Node->Ipv6.GatewayIpAddress, &Private->GatewayIp.v6, sizeof (EFI_IPv6_ADDRESS)); + } + + TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + if (TmpDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; } // @@ -85,21 +98,39 @@ HttpBootUpdateDevicePath ( return EFI_OUT_OF_RESOURCES; } - // - // Reinstall the device path protocol of the child handle. - // - Status = gBS->ReinstallProtocolInterface ( - Private->ChildHandle, - &gEfiDevicePathProtocolGuid, - Private->DevicePath, - NewDevicePath - ); - if (EFI_ERROR (Status)) { - return Status; + if (!Private->UsingIpv6) { + // + // Reinstall the device path protocol of the child handle. + // + Status = gBS->ReinstallProtocolInterface ( + Private->Ip4Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip4Nic->DevicePath, + NewDevicePath + ); + if (EFI_ERROR (Status)) { + return Status; + } + + FreePool (Private->Ip4Nic->DevicePath); + Private->Ip4Nic->DevicePath = NewDevicePath; + } else { + // + // Reinstall the device path protocol of the child handle. + // + Status = gBS->ReinstallProtocolInterface ( + Private->Ip6Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip6Nic->DevicePath, + NewDevicePath + ); + if (EFI_ERROR (Status)) { + return Status; + } + FreePool (Private->Ip6Nic->DevicePath); + Private->Ip6Nic->DevicePath = NewDevicePath; } - FreePool (Private->DevicePath); - Private->DevicePath = NewDevicePath; return EFI_SUCCESS; } @@ -113,7 +144,7 @@ HttpBootUpdateDevicePath ( **/ EFI_STATUS -HttpBootExtractUriInfo ( +HttpBootDhcp4ExtractUriInfo ( IN HTTP_BOOT_PRIVATE_DATA *Private ) { @@ -192,6 +223,159 @@ HttpBootExtractUriInfo ( return Status; } +/** + Parse the boot file URI information from the selected Dhcp6 offer packet. + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Successfully parsed out all the boot information. + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +HttpBootDhcp6ExtractUriInfo ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + HTTP_BOOT_DHCP6_PACKET_CACHE *SelectOffer; + HTTP_BOOT_DHCP6_PACKET_CACHE *HttpOffer; + UINT32 SelectIndex; + UINT32 ProxyIndex; + EFI_DHCP6_PACKET_OPTION *Option; + EFI_IPv6_ADDRESS IpAddr; + CHAR8 *HostName; + CHAR16 *HostNameStr; + EFI_STATUS Status; + + ASSERT (Private != NULL); + ASSERT (Private->SelectIndex != 0); + SelectIndex = Private->SelectIndex - 1; + ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM); + + Status = EFI_SUCCESS; + HostName = NULL; + // + // SelectOffer contains the IP address configuration and name server configuration. + // HttpOffer contains the boot file URL. + // + SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp6; + if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) { + HttpOffer = SelectOffer; + } else { + ASSERT (Private->SelectProxyType != HttpOfferTypeMax); + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; + HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp6; + } + + // + // Set the Local station address to IP layer. + // + Status = HttpBootSetIp6Address (Private); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Configure the default DNS server if server assigned. + // + if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || (SelectOffer->OfferType == HttpOfferTypeDhcpDns)) { + Option = SelectOffer->OptList[HTTP_BOOT_DHCP6_IDX_DNS_SERVER]; + ASSERT (Option != NULL); + Status = HttpBootSetIp6Dns ( + Private, + HTONS (Option->OpLen), + Option->Data + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Extract the HTTP server Ip frome URL. This is used to Check route table + // whether can send message to HTTP Server Ip through the GateWay. + // + Status = HttpUrlGetIp6 ( + (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data, + HttpOffer->UriParser, + &IpAddr + ); + + if (EFI_ERROR (Status)) { + // + // The Http server address is expressed by Name Ip, so perform DNS resolution + // + Status = HttpUrlGetHostName ( + (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data, + HttpOffer->UriParser, + &HostName + ); + if (EFI_ERROR (Status)) { + return Status; + } + + HostNameStr = AllocateZeroPool ((AsciiStrLen (HostName) + 1) * sizeof (CHAR16)); + if (HostNameStr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + AsciiStrToUnicodeStr (HostName, HostNameStr); + Status = HttpBootDns (Private, HostNameStr, &IpAddr); + FreePool (HostNameStr); + if (EFI_ERROR (Status)) { + goto Error; + } + } + + CopyMem (&Private->ServerIp.v6, &IpAddr, sizeof (EFI_IPv6_ADDRESS)); + + // + // register the IPv6 gateway address to the network device. + // + Status = HttpBootSetIp6Gateway (Private); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Extract the port from URL, and use default HTTP port 80 if not provided. + // + Status = HttpUrlGetPort ( + (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data, + HttpOffer->UriParser, + &Private->Port + ); + if (EFI_ERROR (Status) || Private->Port == 0) { + Private->Port = 80; + } + + // + // Record the URI of boot file from the selected HTTP offer. + // + Private->BootFileUriParser = HttpOffer->UriParser; + Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data; + + + // + // All boot informations are valid here. + // + AsciiPrint ("\n URI: %a", Private->BootFileUri); + // + // Update the device path to include the IP and boot URI information. + // + Status = HttpBootUpdateDevicePath (Private); + +Error: + + if (HostName != NULL) { + FreePool (HostName); + } + + return Status; +} + + /** Discover all the boot information for boot file. @@ -218,9 +402,9 @@ HttpBootDiscoverBootInfo ( } if (!Private->UsingIpv6) { - Status = HttpBootExtractUriInfo (Private); + Status = HttpBootDhcp4ExtractUriInfo (Private); } else { - ASSERT (FALSE); + Status = HttpBootDhcp6ExtractUriInfo (Private); } return Status; @@ -247,12 +431,14 @@ HttpBootCreateHttpIo ( ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA)); if (!Private->UsingIpv6) { - ConfigData.Config4.HttpVersion = HttpVersion11; + ConfigData.Config4.HttpVersion = HttpVersion11; ConfigData.Config4.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT; IP4_COPY_ADDRESS (&ConfigData.Config4.LocalIp, &Private->StationIp.v4); IP4_COPY_ADDRESS (&ConfigData.Config4.SubnetMask, &Private->SubnetMask.v4); } else { - ASSERT (FALSE); + ConfigData.Config6.HttpVersion = HttpVersion11; + ConfigData.Config6.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT; + IP6_COPY_ADDRESS (&ConfigData.Config6.LocalIp, &Private->StationIp.v6); } Status = HttpIoCreateIo ( diff --git a/NetworkPkg/HttpBootDxe/HttpBootComponentName.c b/NetworkPkg/HttpBootDxe/HttpBootComponentName.c index 0708598c4f..2c39089da3 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootComponentName.c +++ b/NetworkPkg/HttpBootDxe/HttpBootComponentName.c @@ -151,7 +151,10 @@ HttpBootDxeComponentNameGetControllerName ( NicHandle = HttpBootGetNicByIp4Children (ControllerHandle); if (NicHandle == NULL) { - return EFI_UNSUPPORTED; + NicHandle = HttpBootGetNicByIp6Children(ControllerHandle); + if (NicHandle == NULL) { + return EFI_UNSUPPORTED; + } } // diff --git a/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c b/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c index 217c608233..9a947a6ea6 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c +++ b/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c @@ -319,7 +319,7 @@ HttpBootParseDhcp4Packet ( } // - // The offer with "HttpClient" is a Http offer. + // The offer with "HTTPClient" is a Http offer. // Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_CLASS_ID]; if ((Option != NULL) && (Option->Length >= 9) && @@ -461,13 +461,13 @@ HttpBootCacheDhcp4Offer ( } /** - Select an DHCPv4 offer, and record SelectIndex and SelectProxyType. + Select an DHCPv4 or DHCP6 offer, and record SelectIndex and SelectProxyType. @param[in] Private Pointer to HTTP boot driver private data. **/ VOID -HttpBootSelectDhcp4Offer ( +HttpBootSelectDhcpOffer ( IN HTTP_BOOT_PRIVATE_DATA *Private ) { @@ -590,7 +590,7 @@ HttpBootDhcp4CallBack ( // Select offer according to the priority in UEFI spec, and record the SelectIndex // and SelectProxyType. // - HttpBootSelectDhcp4Offer (Private); + HttpBootSelectDhcpOffer (Private); if (Private->SelectIndex == 0) { Status = EFI_ABORTED; @@ -689,7 +689,7 @@ HttpBootRegisterIp4Dns ( **/ EFI_STATUS -HttpBootSetIpPolicy ( +HttpBootSetIp4Policy ( IN HTTP_BOOT_PRIVATE_DATA *Private ) { @@ -752,7 +752,7 @@ HttpBootDhcp4Dora ( Dhcp4 = Private->Dhcp4; ASSERT (Dhcp4 != NULL); - Status = HttpBootSetIpPolicy (Private); + Status = HttpBootSetIp4Policy (Private); if (EFI_ERROR (Status)) { return Status; } diff --git a/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h b/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h index 200501666b..2bc46deafd 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h +++ b/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h @@ -245,6 +245,17 @@ typedef struct { EFI_DHCP4_PACKET_OPTION *OptList[HTTP_BOOT_DHCP4_TAG_INDEX_MAX]; } HTTP_BOOT_DHCP4_PACKET_CACHE; +/** + Select an DHCPv4 or DHCP6 offer, and record SelectIndex and SelectProxyType. + + @param[in] Private Pointer to HTTP boot driver private data. + +**/ +VOID +HttpBootSelectDhcpOffer ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + /** Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other Http boot information. diff --git a/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c b/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c new file mode 100644 index 0000000000..e5cf894714 --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c @@ -0,0 +1,984 @@ +/** @file + Functions implementation related with DHCPv6 for HTTP boot driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpBootDxe.h" + +/** + Build the options buffer for the DHCPv6 request packet. + + @param[in] Private The pointer to HTTP BOOT driver private data. + @param[out] OptList The pointer to the option pointer array. + @param[in] Buffer The pointer to the buffer to contain the option list. + + @return Index The count of the built-in options. + +**/ +UINT32 +HttpBootBuildDhcp6Options ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + OUT EFI_DHCP6_PACKET_OPTION **OptList, + IN UINT8 *Buffer + ) +{ + HTTP_BOOT_DHCP6_OPTION_ENTRY OptEnt; + UINT16 Value; + UINT32 Index; + + Index = 0; + OptList[0] = (EFI_DHCP6_PACKET_OPTION *) Buffer; + + // + // Append client option request option + // + OptList[Index]->OpCode = HTONS (HTTP_BOOT_DHCP6_OPT_ORO); + OptList[Index]->OpLen = HTONS (8); + OptEnt.Oro = (HTTP_BOOT_DHCP6_OPTION_ORO *) OptList[Index]->Data; + OptEnt.Oro->OpCode[0] = HTONS(HTTP_BOOT_DHCP6_OPT_BOOT_FILE_URL); + OptEnt.Oro->OpCode[1] = HTONS(HTTP_BOOT_DHCP6_OPT_BOOT_FILE_PARAM); + OptEnt.Oro->OpCode[2] = HTONS(HTTP_BOOT_DHCP6_OPT_DNS_SERVERS); + OptEnt.Oro->OpCode[3] = HTONS(HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS); + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append client network device interface option + // + OptList[Index]->OpCode = HTONS (HTTP_BOOT_DHCP6_OPT_UNDI); + OptList[Index]->OpLen = HTONS ((UINT16)3); + OptEnt.Undi = (HTTP_BOOT_DHCP6_OPTION_UNDI *) OptList[Index]->Data; + + if (Private->Nii != NULL) { + OptEnt.Undi->Type = Private->Nii->Type; + OptEnt.Undi->MajorVer = Private->Nii->MajorVer; + OptEnt.Undi->MinorVer = Private->Nii->MinorVer; + } else { + OptEnt.Undi->Type = DEFAULT_UNDI_TYPE; + OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR; + OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR; + } + + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append client system architecture option + // + OptList[Index]->OpCode = HTONS (HTTP_BOOT_DHCP6_OPT_ARCH); + OptList[Index]->OpLen = HTONS ((UINT16) sizeof (HTTP_BOOT_DHCP6_OPTION_ARCH)); + OptEnt.Arch = (HTTP_BOOT_DHCP6_OPTION_ARCH *) OptList[Index]->Data; + Value = HTONS (EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE); + CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16)); + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append vendor class identify option. + // + OptList[Index]->OpCode = HTONS (HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS); + OptList[Index]->OpLen = HTONS ((UINT16) sizeof (HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS)); + OptEnt.VendorClass = (HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data; + OptEnt.VendorClass->Vendor = HTONL (HTTP_BOOT_DHCP6_ENTERPRISE_NUM); + OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (HTTP_BOOT_CLASS_ID)); + CopyMem ( + &OptEnt.VendorClass->ClassId, + DEFAULT_CLASS_ID_DATA, + sizeof (HTTP_BOOT_CLASS_ID) + ); + HttpBootUintnToAscDecWithFormat ( + EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE, + OptEnt.VendorClass->ClassId.ArchitectureType, + sizeof (OptEnt.VendorClass->ClassId.ArchitectureType) + ); + + if (Private->Nii != NULL) { + CopyMem ( + OptEnt.VendorClass->ClassId.InterfaceName, + Private->Nii->StringId, + sizeof (OptEnt.VendorClass->ClassId.InterfaceName) + ); + HttpBootUintnToAscDecWithFormat ( + Private->Nii->MajorVer, + OptEnt.VendorClass->ClassId.UndiMajor, + sizeof (OptEnt.VendorClass->ClassId.UndiMajor) + ); + HttpBootUintnToAscDecWithFormat ( + Private->Nii->MinorVer, + OptEnt.VendorClass->ClassId.UndiMinor, + sizeof (OptEnt.VendorClass->ClassId.UndiMinor) + ); + } + + Index++; + + return Index; +} + +/** + Parse out a DHCPv6 option by OptTag, and find the position in buffer. + + @param[in] Buffer The pointer to the option buffer. + @param[in] Length Length of the option buffer. + @param[in] OptTag The required option tag. + + @retval NULL Failed to parse the required option. + @retval Others The postion of the required option in buffer. + +**/ +EFI_DHCP6_PACKET_OPTION * +HttpBootParseDhcp6Options ( + IN UINT8 *Buffer, + IN UINT32 Length, + IN UINT16 OptTag + ) +{ + EFI_DHCP6_PACKET_OPTION *Option; + UINT32 Offset; + + Option = (EFI_DHCP6_PACKET_OPTION *) Buffer; + Offset = 0; + + // + // OpLen and OpCode here are both stored in network order. + // + while (Offset < Length) { + + if (NTOHS (Option->OpCode) == OptTag) { + + return Option; + } + + Offset += (NTOHS(Option->OpLen) + 4); + Option = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset); + } + + return NULL; + +} + +/** + Parse the cached DHCPv6 packet, including all the options. + + @param[in] Cache6 The pointer to a cached DHCPv6 packet. + + @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet. + +**/ +EFI_STATUS +HttpBootParseDhcp6Packet ( + IN HTTP_BOOT_DHCP6_PACKET_CACHE *Cache6 + ) +{ + EFI_DHCP6_PACKET *Offer; + EFI_DHCP6_PACKET_OPTION **Options; + EFI_DHCP6_PACKET_OPTION *Option; + HTTP_BOOT_OFFER_TYPE OfferType; + EFI_IPv6_ADDRESS IpAddr; + BOOLEAN IsProxyOffer; + BOOLEAN IsHttpOffer; + BOOLEAN IsDnsOffer; + BOOLEAN IpExpressedUri; + EFI_STATUS Status; + UINT32 Offset; + UINT32 Length; + + IsDnsOffer = FALSE; + IpExpressedUri = FALSE; + IsProxyOffer = TRUE; + IsHttpOffer = FALSE; + Offer = &Cache6->Packet.Offer; + Options = Cache6->OptList; + + ZeroMem (Cache6->OptList, sizeof (Cache6->OptList)); + + Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option); + Offset = 0; + Length = GET_DHCP6_OPTION_SIZE (Offer); + + // + // OpLen and OpCode here are both stored in network order, since they are from original packet. + // + while (Offset < Length) { + + if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_IA_NA) { + Options[HTTP_BOOT_DHCP6_IDX_IA_NA] = Option; + } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_BOOT_FILE_URL) { + // + // The server sends this option to inform the client about an URL to a boot file. + // + Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL] = Option; + } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_BOOT_FILE_PARAM) { + Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_PARAM] = Option; + } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS) { + Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS] = Option; + } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_DNS_SERVERS) { + Options[HTTP_BOOT_DHCP6_IDX_DNS_SERVER] = Option; + } + + Offset += (NTOHS (Option->OpLen) + 4); + Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset); + } + // + // The offer with assigned client address is NOT a proxy offer. + // An ia_na option, embeded with valid ia_addr option and a status_code of success. + // + Option = Options[HTTP_BOOT_DHCP6_IDX_IA_NA]; + if (Option != NULL) { + Option = HttpBootParseDhcp6Options ( + Option->Data + 12, + NTOHS (Option->OpLen), + HTTP_BOOT_DHCP6_OPT_STATUS_CODE + ); + if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) { + IsProxyOffer = FALSE; + } + } + + // + // The offer with "HTTPClient" is a Http offer. + // + Option = Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS]; + + if (Option != NULL && + NTOHS(Option->OpLen) >= 10 && + CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 10) == 0) { + IsHttpOffer = TRUE; + } + + // + // The offer with Domain Server is a DNS offer. + // + Option = Options[HTTP_BOOT_DHCP6_IDX_DNS_SERVER]; + if (Option != NULL) { + IsDnsOffer = TRUE; + } + + // + // Http offer must have a boot URI. + // + if (IsHttpOffer && Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL] == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // Try to retrieve the IP of HTTP server from URI. + // + if (IsHttpOffer) { + Status = HttpParseUrl ( + (CHAR8*) Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data, + (UINT32) AsciiStrLen ((CHAR8*) Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data), + FALSE, + &Cache6->UriParser + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Status = HttpUrlGetIp6 ( + (CHAR8*) Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data, + Cache6->UriParser, + &IpAddr + ); + IpExpressedUri = !EFI_ERROR (Status); + } + + // + // Determine offer type of the DHCPv6 packet. + // + if (IsHttpOffer) { + if (IpExpressedUri) { + OfferType = IsProxyOffer ? HttpOfferTypeProxyIpUri : HttpOfferTypeDhcpIpUri; + } else { + if (!IsProxyOffer) { + OfferType = IsDnsOffer ? HttpOfferTypeDhcpNameUriDns : HttpOfferTypeDhcpNameUri; + } else { + OfferType = HttpOfferTypeProxyNameUri; + } + } + + } else { + if (!IsProxyOffer) { + OfferType = IsDnsOffer ? HttpOfferTypeDhcpDns : HttpOfferTypeDhcpOnly; + } else { + return EFI_DEVICE_ERROR; + } + } + + Cache6->OfferType = OfferType; + return EFI_SUCCESS; +} + +/** + Cache the DHCPv6 packet. + + @param[in] Dst The pointer to the cache buffer for DHCPv6 packet. + @param[in] Src The pointer to the DHCPv6 packet to be cached. + +**/ +VOID +HttpBootCacheDhcp6Packet ( + IN EFI_DHCP6_PACKET *Dst, + IN EFI_DHCP6_PACKET *Src + ) +{ + ASSERT (Dst->Size >= Src->Length); + + CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length); + Dst->Length = Src->Length; +} + +/** + Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + @param[in] RcvdOffer The pointer to the received offer packet. + +**/ +VOID +HttpBootCacheDhcp6Offer ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN EFI_DHCP6_PACKET *RcvdOffer + ) +{ + HTTP_BOOT_DHCP6_PACKET_CACHE *Cache6; + EFI_DHCP6_PACKET *Offer; + HTTP_BOOT_OFFER_TYPE OfferType; + + Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6; + Offer = &Cache6->Packet.Offer; + + // + // Cache the content of DHCPv6 packet firstly. + // + HttpBootCacheDhcp6Packet(Offer, RcvdOffer); + + // + // Validate the DHCPv6 packet, and parse the options and offer type. + // + if (EFI_ERROR (HttpBootParseDhcp6Packet (Cache6))) { + return ; + } + + // + // Determine whether cache the current offer by type, and record OfferIndex and OfferCount. + // + OfferType = Cache6->OfferType; + ASSERT (OfferType < HttpOfferTypeMax); + ASSERT (Private->OfferCount[OfferType] < HTTP_BOOT_OFFER_MAX_NUM); + Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; + Private->OfferCount[OfferType]++; + Private->OfferNum++; +} + +/** + EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver + to intercept events that occurred in the configuration process. + + @param[in] This The pointer to the EFI DHCPv6 Protocol. + @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure(). + @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver. + @param[in] Dhcp6Event The event that occurs in the current state, which usually means a + state transition. + @param[in] Packet The DHCPv6 packet that is going to be sent or was already received. + @param[out] NewPacket The packet that is used to replace the Packet above. + + @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process. + @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol + driver will continue to wait for more packets. + @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process. + +**/ +EFI_STATUS +EFIAPI +HttpBootDhcp6CallBack ( + IN EFI_DHCP6_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP6_STATE CurrentState, + IN EFI_DHCP6_EVENT Dhcp6Event, + IN EFI_DHCP6_PACKET *Packet, + OUT EFI_DHCP6_PACKET **NewPacket OPTIONAL + ) +{ + HTTP_BOOT_PRIVATE_DATA *Private; + EFI_DHCP6_PACKET *SelectAd; + EFI_STATUS Status; + if ((Dhcp6Event != Dhcp6RcvdAdvertise) && (Dhcp6Event != Dhcp6SelectAdvertise)) { + return EFI_SUCCESS; + } + + ASSERT (Packet != NULL); + + Private = (HTTP_BOOT_PRIVATE_DATA *) Context; + Status = EFI_SUCCESS; + switch (Dhcp6Event) { + + case Dhcp6RcvdAdvertise: + Status = EFI_NOT_READY; + if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) { + // + // Cache the dhcp offers to OfferBuffer[] for select later, and record + // the OfferIndex and OfferCount. + // + HttpBootCacheDhcp6Offer (Private, Packet); + } + break; + + case Dhcp6SelectAdvertise: + // + // Select offer by the default policy or by order, and record the SelectIndex + // and SelectProxyType. + // + HttpBootSelectDhcpOffer (Private); + + if (Private->SelectIndex == 0) { + Status = EFI_ABORTED; + } else { + ASSERT (NewPacket != NULL); + SelectAd = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer; + *NewPacket = AllocateZeroPool (SelectAd->Size); + ASSERT (*NewPacket != NULL); + CopyMem (*NewPacket, SelectAd, SelectAd->Size); + } + break; + + default: + break; + } + + return Status; +} + +/** + Check whether IP driver could route the message which will be sent to ServerIp address. + + This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid + route is found in IP6 route table, the address will be filed in GatewayAddr and return. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + @param[in] TimeOutInSecond Timeout value in seconds. + @param[out] GatewayAddr Pointer to store the gateway IP address. + + @retval EFI_SUCCESS Found a valid gateway address successfully. + @retval EFI_TIMEOUT The operation is time out. + @retval Other Unexpect error happened. + +**/ +EFI_STATUS +HttpBootCheckRouteTable ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN UINTN TimeOutInSecond, + OUT EFI_IPv6_ADDRESS *GatewayAddr + ) +{ + EFI_STATUS Status; + EFI_IP6_PROTOCOL *Ip6; + EFI_IP6_MODE_DATA Ip6ModeData; + UINTN Index; + EFI_EVENT TimeOutEvt; + UINTN RetryCount; + BOOLEAN GatewayIsFound; + + ASSERT (GatewayAddr != NULL); + ASSERT (Private != NULL); + + Ip6 = Private->Ip6; + GatewayIsFound = FALSE; + RetryCount = 0; + TimeOutEvt = NULL; + Status = EFI_SUCCESS; + ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS)); + + while (TRUE) { + Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Find out the gateway address which can route the message which send to ServerIp. + // + for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) { + if (NetIp6IsNetEqual (&Private->ServerIp.v6, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) { + IP6_COPY_ADDRESS (GatewayAddr, &Ip6ModeData.RouteTable[Index].Gateway); + GatewayIsFound = TRUE; + break; + } + } + + 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); + } + + if (GatewayIsFound || RetryCount == TimeOutInSecond) { + break; + } + + RetryCount++; + + // + // Delay 1 second then recheck it again. + // + if (TimeOutEvt == NULL) { + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeOutEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) { + Ip6->Poll (Ip6); + } + } + +ON_EXIT: + if (TimeOutEvt != NULL) { + gBS->CloseEvent (TimeOutEvt); + } + + if (GatewayIsFound) { + Status = EFI_SUCCESS; + } else if (RetryCount == TimeOutInSecond) { + Status = EFI_TIMEOUT; + } + + return Status; +} + +/** + Set the IP6 policy to Automatic. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + + @retval EFI_SUCCESS Switch the IP policy succesfully. + @retval Others Unexpect error happened. + +**/ +EFI_STATUS +HttpBootSetIp6Policy ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_IP6_CONFIG_POLICY Policy; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_STATUS Status; + UINTN DataSize; + + Ip6Config = Private->Ip6Config; + DataSize = sizeof (EFI_IP6_CONFIG_POLICY); + + // + // Get and store the current policy of IP6 driver. + // + Status = Ip6Config->GetData ( + Ip6Config, + Ip6ConfigDataTypePolicy, + &DataSize, + &Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Policy == Ip6ConfigPolicyManual) { + Policy = Ip6ConfigPolicyAutomatic; + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypePolicy, + sizeof(EFI_IP6_CONFIG_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + return EFI_SUCCESS; +} + +/** + This function will register the default DNS addresses to the network device. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + @param[in] DataLength Size of the buffer pointed to by DnsServerData in bytes. + @param[in] DnsServerData Point a list of DNS server address in an array + of EFI_IPv6_ADDRESS instances. + + @retval EFI_SUCCESS The DNS configuration has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootSetIp6Dns ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN UINTN DataLength, + IN VOID *DnsServerData + ) +{ + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + + ASSERT (Private->UsingIpv6); + + Ip6Config = Private->Ip6Config; + + return Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeDnsServer, + DataLength, + DnsServerData + ); +} + +/** + This function will register the IPv6 gateway address to the network device. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP configuration has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootSetIp6Gateway ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_STATUS Status; + + ASSERT (Private->UsingIpv6); + Ip6Config = Private->Ip6Config; + + // + // Set the default gateway address. + // + if (!Private->NoGateway && !NetIp6IsUnspecifiedAddr (&Private->GatewayIp.v6)) { + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeGateway, + sizeof (EFI_IPv6_ADDRESS), + &Private->GatewayIp.v6 + ); + if (EFI_ERROR(Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + This function will register the station IP address. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP address has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootSetIp6Address ( + IN HTTP_BOOT_PRIVATE_DATA *Private +) +{ + EFI_STATUS Status; + EFI_IP6_PROTOCOL *Ip6; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_IP6_CONFIG_POLICY Policy; + EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr; + EFI_IPv6_ADDRESS *Ip6Addr; + EFI_IPv6_ADDRESS GatewayAddr; + EFI_IP6_CONFIG_DATA Ip6CfgData; + EFI_EVENT MappedEvt; + UINTN DataSize; + BOOLEAN IsAddressOk; + UINTN Index; + + ASSERT (Private->UsingIpv6); + + MappedEvt = NULL; + IsAddressOk = FALSE; + Ip6Addr = NULL; + Ip6Cfg = Private->Ip6Config; + Ip6 = Private->Ip6; + + ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); + CopyMem (&CfgAddr, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + ZeroMem (&Ip6CfgData, sizeof (EFI_IP6_CONFIG_DATA)); + + Ip6CfgData.AcceptIcmpErrors = TRUE; + Ip6CfgData.DefaultProtocol = IP6_ICMP; + Ip6CfgData.HopLimit = HTTP_BOOT_DEFAULT_HOPLIMIT; + Ip6CfgData.ReceiveTimeout = HTTP_BOOT_DEFAULT_LIFETIME; + Ip6CfgData.TransmitTimeout = HTTP_BOOT_DEFAULT_LIFETIME; + + Status = Ip6->Configure (Ip6, &Ip6CfgData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Retrieve the gateway address from IP6 route table. + // + Status = HttpBootCheckRouteTable (Private, HTTP_BOOT_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr); + if (EFI_ERROR (Status)) { + Private->NoGateway = TRUE; + } else { + IP6_COPY_ADDRESS (&Private->GatewayIp.v6, &GatewayAddr); + } + + // + // Set the new address by Ip6ConfigProtocol manually. + // + Policy = Ip6ConfigPolicyManual; + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + sizeof(EFI_IP6_CONFIG_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Create a notify event to set address flag when DAD if IP6 driver succeeded. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpBootCommonNotify, + &IsAddressOk, + &MappedEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Set static host ip6 address. This is a asynchronous process. + // + Status = Ip6Cfg->RegisterDataNotify ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + MappedEvt + ); + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS), + &CfgAddr + ); + if (EFI_ERROR (Status) && Status != EFI_NOT_READY) { + goto ON_EXIT; + } else if (Status == EFI_NOT_READY) { + // + // Poll the network until the asynchronous process is finished. + // + while (!IsAddressOk) { + Ip6->Poll (Ip6); + } + // + // Check whether the Ip6 Address setting is successed. + // + DataSize = 0; + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + &DataSize, + NULL + ); + if (Status != EFI_BUFFER_TOO_SMALL || DataSize == 0) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Ip6Addr = AllocatePool (DataSize); + if (Ip6Addr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + &DataSize, + (VOID *) Ip6Addr + ); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index ++) { + if (CompareMem (Ip6Addr + Index, &CfgAddr, sizeof (EFI_IPv6_ADDRESS)) == 0) { + break; + } + } + if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) { + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + +ON_EXIT: + if (MappedEvt != NULL) { + Ip6Cfg->UnregisterDataNotify ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + MappedEvt + ); + gBS->CloseEvent (MappedEvt); + } + + if (Ip6Addr != NULL) { + FreePool (Ip6Addr); + } + + return Status; +} + +/** + Start the S.A.R.R DHCPv6 process to acquire the IPv6 address and other Http boot information. + + @param[in] Private Pointer to HTTP_BOOT private data. + + @retval EFI_SUCCESS The S.A.R.R process successfully finished. + @retval Others Failed to finish the S.A.R.R process. + +**/ +EFI_STATUS +HttpBootDhcp6Sarr ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_DHCP6_PROTOCOL *Dhcp6; + EFI_DHCP6_CONFIG_DATA Config; + EFI_DHCP6_MODE_DATA Mode; + EFI_DHCP6_RETRANSMISSION *Retransmit; + EFI_DHCP6_PACKET_OPTION *OptList[HTTP_BOOT_DHCP6_OPTION_MAX_NUM]; + UINT32 OptCount; + UINT8 Buffer[HTTP_BOOT_DHCP6_OPTION_MAX_SIZE]; + EFI_STATUS Status; + + Dhcp6 = Private->Dhcp6; + ASSERT (Dhcp6 != NULL); + + // + // Build options list for the request packet. + // + OptCount = HttpBootBuildDhcp6Options (Private, OptList, Buffer); + ASSERT (OptCount >0); + + Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION)); + if (Retransmit == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA)); + ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA)); + + Config.OptionCount = OptCount; + Config.OptionList = OptList; + Config.Dhcp6Callback = HttpBootDhcp6CallBack; + Config.CallbackContext = Private; + Config.IaInfoEvent = NULL; + Config.RapidCommit = FALSE; + Config.ReconfigureAccept = FALSE; + Config.IaDescriptor.IaId = NET_RANDOM (NetRandomInitSeed ()); + Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA; + Config.SolicitRetransmission = Retransmit; + Retransmit->Irt = 4; + Retransmit->Mrc = 4; + Retransmit->Mrt = 32; + Retransmit->Mrd = 60; + + // + // Configure the DHCPv6 instance for HTTP boot. + // + Status = Dhcp6->Configure (Dhcp6, &Config); + FreePool (Retransmit); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Initialize the record fields for DHCPv6 offer in private data. + // + Private->OfferNum = 0; + Private->SelectIndex = 0; + ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); + ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); + + // + // Start DHCPv6 S.A.R.R. process to acquire IPv6 address. + // + Status = Dhcp6->Start (Dhcp6); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Get the acquired IPv6 address and store them. + // + Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + ASSERT (Mode.Ia->State == Dhcp6Bound); + CopyMem (&Private->StationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS)); + + AsciiPrint ("\n Station IPv6 address is "); + HttpBootShowIp6Addr (&Private->StationIp.v6); + AsciiPrint ("\n"); + +ON_EXIT: + if (EFI_ERROR (Status)) { + Dhcp6->Stop (Dhcp6); + Dhcp6->Configure (Dhcp6, NULL); + } else { + ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA)); + ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA)); + Dhcp6->Configure (Dhcp6, &Config); + } + + return Status; + +} + diff --git a/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h b/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h new file mode 100644 index 0000000000..59ca19e464 --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h @@ -0,0 +1,198 @@ +/** @file + Functions declaration related with DHCPv6 for HTTP boot driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + + +#ifndef __EFI_HTTP_BOOT_DHCP6_H__ +#define __EFI_HTTP_BOOT_DHCP6_H__ + +#define HTTP_BOOT_OFFER_MAX_NUM 16 +#define HTTP_BOOT_DHCP6_OPTION_MAX_NUM 16 +#define HTTP_BOOT_DHCP6_OPTION_MAX_SIZE 312 +#define HTTP_BOOT_DHCP6_PACKET_MAX_SIZE 1472 +#define HTTP_BOOT_IP6_ROUTE_TABLE_TIMEOUT 10 +#define HTTP_BOOT_DEFAULT_HOPLIMIT 64 +#define HTTP_BOOT_DEFAULT_LIFETIME 50000 + + +#define HTTP_BOOT_DHCP6_OPT_CLIENT_ID 1 +#define HTTP_BOOT_DHCP6_OPT_SERVER_ID 2 +#define HTTP_BOOT_DHCP6_OPT_IA_NA 3 +#define HTTP_BOOT_DHCP6_OPT_IA_TA 4 +#define HTTP_BOOT_DHCP6_OPT_IAADDR 5 +#define HTTP_BOOT_DHCP6_OPT_ORO 6 +#define HTTP_BOOT_DHCP6_OPT_PREFERENCE 7 +#define HTTP_BOOT_DHCP6_OPT_ELAPSED_TIME 8 +#define HTTP_BOOT_DHCP6_OPT_REPLAY_MSG 9 +#define HTTP_BOOT_DHCP6_OPT_AUTH 11 +#define HTTP_BOOT_DHCP6_OPT_UNICAST 12 +#define HTTP_BOOT_DHCP6_OPT_STATUS_CODE 13 +#define HTTP_BOOT_DHCP6_OPT_RAPID_COMMIT 14 +#define HTTP_BOOT_DHCP6_OPT_USER_CLASS 15 +#define HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS 16 +#define HTTP_BOOT_DHCP6_OPT_VENDOR_OPTS 17 +#define HTTP_BOOT_DHCP6_OPT_INTERFACE_ID 18 +#define HTTP_BOOT_DHCP6_OPT_RECONFIG_MSG 19 +#define HTTP_BOOT_DHCP6_OPT_RECONFIG_ACCEPT 20 +#define HTTP_BOOT_DHCP6_OPT_DNS_SERVERS 23 +#define HTTP_BOOT_DHCP6_OPT_BOOT_FILE_URL 59 // Assigned by IANA, RFC 5970 +#define HTTP_BOOT_DHCP6_OPT_BOOT_FILE_PARAM 60 // Assigned by IANA, RFC 5970 +#define HTTP_BOOT_DHCP6_OPT_ARCH 61 // Assigned by IANA, RFC 5970 +#define HTTP_BOOT_DHCP6_OPT_UNDI 62 // Assigned by IANA, RFC 5970 +#define HTTP_BOOT_DHCP6_ENTERPRISE_NUM 343 // TODO: IANA TBD: temporarily using Intel's +#define HTTP_BOOT_DHCP6_MAX_BOOT_FILE_SIZE 65535 // It's a limitation of bit length, 65535*512 bytes. + +#define HTTP_BOOT_DHCP6_IDX_IA_NA 0 +#define HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL 1 +#define HTTP_BOOT_DHCP6_IDX_BOOT_FILE_PARAM 2 +#define HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS 3 +#define HTTP_BOOT_DHCP6_IDX_DNS_SERVER 4 +#define HTTP_BOOT_DHCP6_IDX_MAX 5 + +#pragma pack(1) +typedef struct { + UINT16 OpCode[256]; +} HTTP_BOOT_DHCP6_OPTION_ORO; + +typedef struct { + UINT8 Type; + UINT8 MajorVer; + UINT8 MinorVer; +} HTTP_BOOT_DHCP6_OPTION_UNDI; + +typedef struct { + UINT16 Type; +} HTTP_BOOT_DHCP6_OPTION_ARCH; + +typedef struct { + UINT8 ClassIdentifier[10]; + UINT8 ArchitecturePrefix[5]; + UINT8 ArchitectureType[5]; + UINT8 Lit3[1]; + UINT8 InterfaceName[4]; + UINT8 Lit4[1]; + UINT8 UndiMajor[3]; + UINT8 UndiMinor[3]; +} HTTP_BOOT_CLASS_ID; + +typedef struct { + UINT32 Vendor; + UINT16 ClassLen; + HTTP_BOOT_CLASS_ID ClassId; +} HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS; + +#pragma pack() + +typedef union { + HTTP_BOOT_DHCP6_OPTION_ORO *Oro; + HTTP_BOOT_DHCP6_OPTION_UNDI *Undi; + HTTP_BOOT_DHCP6_OPTION_ARCH *Arch; + HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS *VendorClass; +} HTTP_BOOT_DHCP6_OPTION_ENTRY; + +typedef union { + EFI_DHCP6_PACKET Offer; + EFI_DHCP6_PACKET Ack; + UINT8 Buffer[HTTP_BOOT_DHCP6_PACKET_MAX_SIZE]; +} HTTP_BOOT_DHCP6_PACKET; + +typedef struct { + HTTP_BOOT_DHCP6_PACKET Packet; + HTTP_BOOT_OFFER_TYPE OfferType; + EFI_DHCP6_PACKET_OPTION *OptList[HTTP_BOOT_DHCP6_IDX_MAX]; + VOID *UriParser; +} HTTP_BOOT_DHCP6_PACKET_CACHE; + +#define GET_NEXT_DHCP6_OPTION(Opt) \ + (EFI_DHCP6_PACKET_OPTION *) ((UINT8 *) (Opt) + \ + sizeof (EFI_DHCP6_PACKET_OPTION) + (NTOHS ((Opt)->OpLen)) - 1) + +#define GET_DHCP6_OPTION_SIZE(Pkt) \ + ((Pkt)->Length - sizeof (EFI_DHCP6_HEADER)) + +/** + Start the S.A.R.R DHCPv6 process to acquire the IPv6 address and other Http boot information. + + @param[in] Private Pointer to HTTP_BOOT private data. + + @retval EFI_SUCCESS The S.A.R.R process successfully finished. + @retval Others Failed to finish the S.A.R.R process. + +**/ +EFI_STATUS +HttpBootDhcp6Sarr ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +/** + Set the IP6 policy to Automatic. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + + @retval EFI_SUCCESS Switch the IP policy succesfully. + @retval Others Unexpect error happened. + +**/ +EFI_STATUS +HttpBootSetIp6Policy ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +/** + This function will register the default DNS addresses to the network device. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + @param[in] DataLength Size of the buffer pointed to by DnsServerData in bytes. + @param[in] DnsServerData Point a list of DNS server address in an array + of EFI_IPv6_ADDRESS instances. + + @retval EFI_SUCCESS The DNS configuration has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootSetIp6Dns ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN UINTN DataLength, + IN VOID *DnsServerData + ); + +/** + This function will register the IPv6 gateway address to the network device. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP configuration has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootSetIp6Gateway ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +/** + This function will register the station IP address. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP address has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootSetIp6Address ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +#endif diff --git a/NetworkPkg/HttpBootDxe/HttpBootDxe.c b/NetworkPkg/HttpBootDxe/HttpBootDxe.c index 49be59b8c8..a7fc8a8e2e 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootDxe.c +++ b/NetworkPkg/HttpBootDxe/HttpBootDxe.c @@ -26,6 +26,15 @@ EFI_DRIVER_BINDING_PROTOCOL gHttpBootIp4DxeDriverBinding = { NULL }; +EFI_DRIVER_BINDING_PROTOCOL gHttpBootIp6DxeDriverBinding = { + HttpBootIp6DxeDriverBindingSupported, + HttpBootIp6DxeDriverBindingStart, + HttpBootIp6DxeDriverBindingStop, + HTTP_BOOT_DXE_VERSION, + NULL, + NULL +}; + /** Destroy the HTTP child based on IPv4 stack. @@ -45,11 +54,11 @@ HttpBootDestroyIp4Children ( if (Private->Dhcp4Child != NULL) { gBS->CloseProtocol ( - Private->Dhcp4Child, - &gEfiDhcp4ProtocolGuid, - This->DriverBindingHandle, - Private->Controller - ); + Private->Dhcp4Child, + &gEfiDhcp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); NetLibDestroyServiceChild ( Private->Controller, @@ -64,25 +73,102 @@ HttpBootDestroyIp4Children ( Private->HttpCreated = FALSE; } - gBS->CloseProtocol ( - Private->Controller, - &gEfiCallerIdGuid, - This->DriverBindingHandle, - Private->ChildHandle - ); + if (Private->Ip4Nic != NULL) { + + gBS->CloseProtocol ( + Private->Controller, + &gEfiCallerIdGuid, + This->DriverBindingHandle, + Private->Ip4Nic->Controller + ); + + gBS->UninstallMultipleProtocolInterfaces ( + Private->Ip4Nic->Controller, + &gEfiLoadFileProtocolGuid, + &Private->Ip4Nic->LoadFile, + &gEfiDevicePathProtocolGuid, + Private->Ip4Nic->DevicePath, + NULL + ); + FreePool (Private->Ip4Nic); + Private->Ip4Nic = NULL; + } + +} + +/** + Destroy the HTTP child based on IPv6 stack. + + @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL. + @param[in] Private Pointer to HTTP_BOOT_PRIVATE_DATA. + +**/ +VOID +HttpBootDestroyIp6Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + ASSERT (This != NULL); + ASSERT (Private != NULL); + ASSERT (Private->UsingIpv6 == TRUE); + + if (Private->Ip6Child != NULL) { + gBS->CloseProtocol ( + Private->Ip6Child, + &gEfiIp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiIp6ServiceBindingProtocolGuid, + Private->Ip6Child + ); + } + + if (Private->Dhcp6Child != NULL) { + gBS->CloseProtocol ( + Private->Dhcp6Child, + &gEfiDhcp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); - gBS->UninstallMultipleProtocolInterfaces ( - Private->ChildHandle, - &gEfiLoadFileProtocolGuid, - &Private->LoadFile, - &gEfiDevicePathProtocolGuid, - Private->DevicePath, - NULL - ); + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + Private->Dhcp6Child + ); + } - if (Private->DevicePath != NULL) { - FreePool (Private->DevicePath); - Private->DevicePath = NULL; + if (Private->HttpCreated) { + HttpIoDestroyIo(&Private->HttpIo); + Private->HttpCreated = FALSE; + } + + if (Private->Ip6Nic != NULL) { + + gBS->CloseProtocol ( + Private->Controller, + &gEfiCallerIdGuid, + This->DriverBindingHandle, + Private->Ip6Nic->Controller + ); + + gBS->UninstallMultipleProtocolInterfaces ( + Private->Ip6Nic->Controller, + &gEfiLoadFileProtocolGuid, + &Private->Ip6Nic->LoadFile, + &gEfiDevicePathProtocolGuid, + Private->Ip6Nic->DevicePath, + NULL + ); + FreePool (Private->Ip6Nic); + Private->Ip6Nic = NULL; } } @@ -142,37 +228,37 @@ HttpBootIp4DxeDriverBindingSupported ( // Try to open the DHCP4, HTTP4 and Device Path protocol. // Status = gBS->OpenProtocol ( - ControllerHandle, - &gEfiDhcp4ServiceBindingProtocolGuid, - NULL, - This->DriverBindingHandle, - ControllerHandle, - EFI_OPEN_PROTOCOL_TEST_PROTOCOL - ); + ControllerHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); if (EFI_ERROR (Status)) { return Status; } Status = gBS->OpenProtocol ( - ControllerHandle, - &gEfiHttpServiceBindingProtocolGuid, - NULL, - This->DriverBindingHandle, - ControllerHandle, - EFI_OPEN_PROTOCOL_TEST_PROTOCOL - ); + ControllerHandle, + &gEfiHttpServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); if (EFI_ERROR (Status)) { return Status; } Status = gBS->OpenProtocol ( - ControllerHandle, - &gEfiDevicePathProtocolGuid, - NULL, - This->DriverBindingHandle, - ControllerHandle, - EFI_OPEN_PROTOCOL_TEST_PROTOCOL - ); + ControllerHandle, + &gEfiDevicePathProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); return Status; } @@ -235,25 +321,83 @@ HttpBootIp4DxeDriverBindingStart ( ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); + if (!EFI_ERROR (Status)) { - return EFI_ALREADY_STARTED; - } + Private = HTTP_BOOT_PRIVATE_DATA_FROM_ID(Id); + } else { + // + // Initialize the private data structure. + // + Private = AllocateZeroPool (sizeof (HTTP_BOOT_PRIVATE_DATA)); + if (Private == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Private->Signature = HTTP_BOOT_PRIVATE_DATA_SIGNATURE; + Private->Controller = ControllerHandle; + Private->Image = This->ImageHandle; + InitializeListHead (&Private->CacheList); + // + // Get the NII interface if it exists, it's not required. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + (VOID **) &Private->Nii, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Private->Nii = NULL; + } - // - // Initialize the private data structure. - // - Private = AllocateZeroPool (sizeof (HTTP_BOOT_PRIVATE_DATA)); - if (Private == NULL) { + // + // Open Device Path Protocol to prepare for appending IP and URI node. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &Private->ParentDevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Install a protocol with Caller Id Guid to the NIC, this is just to build the relationship between + // NIC handle and the private data. + // + Status = gBS->InstallProtocolInterface ( + &ControllerHandle, + &gEfiCallerIdGuid, + EFI_NATIVE_INTERFACE, + &Private->Id + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + } + + if (Private->Ip4Nic != NULL) { + // + // Already created before + // + return EFI_SUCCESS; + } + + Private->Ip4Nic = AllocateZeroPool (sizeof (HTTP_BOOT_VIRTUAL_NIC)); + if (Private->Ip4Nic == NULL) { return EFI_OUT_OF_RESOURCES; } - Private->Signature = HTTP_BOOT_PRIVATE_DATA_SIGNATURE; - Private->Controller = ControllerHandle; - Private->Image = This->ImageHandle; - Private->UsingIpv6 = FALSE; - InitializeListHead (&Private->CacheList); - + Private->Ip4Nic->Private = Private; + Private->Ip4Nic->Signature = HTTP_BOOT_VIRTUAL_NIC_SIGNATURE; + // - // Create DHCP child instance. + // Create DHCP4 child instance. // Status = NetLibCreateServiceChild ( ControllerHandle, @@ -264,7 +408,7 @@ HttpBootIp4DxeDriverBindingStart ( if (EFI_ERROR (Status)) { goto ON_ERROR; } - + Status = gBS->OpenProtocol ( Private->Dhcp4Child, &gEfiDhcp4ProtocolGuid, @@ -276,7 +420,7 @@ HttpBootIp4DxeDriverBindingStart ( if (EFI_ERROR (Status)) { goto ON_ERROR; } - + // // Get the Ip4Config2 protocol, it's required to configure the default gateway address. // @@ -291,37 +435,7 @@ HttpBootIp4DxeDriverBindingStart ( if (EFI_ERROR (Status)) { goto ON_ERROR; } - - // - // Get the NII interface if it exists, it's not required. - // - Status = gBS->OpenProtocol ( - ControllerHandle, - &gEfiNetworkInterfaceIdentifierProtocolGuid_31, - (VOID **) &Private->Nii, - This->DriverBindingHandle, - ControllerHandle, - EFI_OPEN_PROTOCOL_GET_PROTOCOL - ); - if (EFI_ERROR (Status)) { - Private->Nii = NULL; - } - - // - // Open Device Path Protocol to prepare for appending IP and URI node. - // - Status = gBS->OpenProtocol ( - ControllerHandle, - &gEfiDevicePathProtocolGuid, - (VOID **) &Private->ParentDevicePath, - This->DriverBindingHandle, - ControllerHandle, - EFI_OPEN_PROTOCOL_GET_PROTOCOL - ); - if (EFI_ERROR (Status)) { - goto ON_ERROR; - } - + // // Append IPv4 device path node. // @@ -340,7 +454,7 @@ HttpBootIp4DxeDriverBindingStart ( Status = EFI_OUT_OF_RESOURCES; goto ON_ERROR; } - + // // Append URI device path node. // @@ -352,62 +466,49 @@ HttpBootIp4DxeDriverBindingStart ( Node->DevPath.Type = MESSAGING_DEVICE_PATH; Node->DevPath.SubType = MSG_URI_DP; SetDevicePathNodeLength (Node, sizeof (EFI_DEVICE_PATH_PROTOCOL)); - Private->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + Private->Ip4Nic->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); FreePool (Node); FreePool (DevicePath); - if (Private->DevicePath == NULL) { + if (Private->Ip4Nic->DevicePath == NULL) { Status = EFI_OUT_OF_RESOURCES; goto ON_ERROR; } - + // // Create a child handle for the HTTP boot and install DevPath and Load file protocol on it. // - CopyMem (&Private->LoadFile, &gHttpBootDxeLoadFile, sizeof (Private->LoadFile)); + CopyMem (&Private->Ip4Nic->LoadFile, &gHttpBootDxeLoadFile, sizeof (EFI_LOAD_FILE_PROTOCOL)); Status = gBS->InstallMultipleProtocolInterfaces ( - &Private->ChildHandle, + &Private->Ip4Nic->Controller, &gEfiLoadFileProtocolGuid, - &Private->LoadFile, + &Private->Ip4Nic->LoadFile, &gEfiDevicePathProtocolGuid, - Private->DevicePath, + Private->Ip4Nic->DevicePath, NULL ); if (EFI_ERROR (Status)) { goto ON_ERROR; } - - // - // Install a protocol with Caller Id Guid to the NIC, this is just to build the relationship between - // NIC handle and the private data. - // - Status = gBS->InstallProtocolInterface ( - &ControllerHandle, - &gEfiCallerIdGuid, - EFI_NATIVE_INTERFACE, - &Private->Id - ); - if (EFI_ERROR (Status)) { - goto ON_ERROR; - } - + // // Open the Caller Id child to setup a parent-child relationship between - // real NIC handle and the HTTP boot child handle. + // real NIC handle and the HTTP boot Ipv4 NIC handle. // Status = gBS->OpenProtocol ( ControllerHandle, &gEfiCallerIdGuid, (VOID **) &Id, This->DriverBindingHandle, - Private->ChildHandle, + Private->Ip4Nic->Controller, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER ); if (EFI_ERROR (Status)) { goto ON_ERROR; } - + return EFI_SUCCESS; + ON_ERROR: HttpBootDestroyIp4Children (This, Private); @@ -416,6 +517,7 @@ ON_ERROR: return Status; } + /** Stops a device controller or a bus controller. @@ -509,51 +611,560 @@ HttpBootIp4DxeDriverBindingStop ( // Destory all child instance and uninstall protocol interface. // HttpBootDestroyIp4Children (This, Private); + + if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) { + // + // Release the cached data. + // + HttpBootFreeCacheList (Private); + + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiCallerIdGuid, + &Private->Id + ); + FreePool (Private); - // - // Release the cached data. - // - HttpBootFreeCacheList (Private); - - gBS->UninstallProtocolInterface ( - NicHandle, - &gEfiCallerIdGuid, - &Private->Id - ); - FreePool (Private); + } return EFI_SUCCESS; } /** - This is the declaration of an EFI image entry point. This entry point is - the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including - both device drivers and bus drivers. + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. - @param[in] ImageHandle The firmware allocated handle for the UEFI image. - @param[in] SystemTable A pointer to the EFI System Table. + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. - @retval EFI_SUCCESS The operation completed successfully. - @retval Others An unexpected error occurred. + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. **/ EFI_STATUS EFIAPI -HttpBootDxeDriverEntryPoint ( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE *SystemTable +HttpBootIp6DxeDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL ) { + EFI_STATUS Status; + // - // Install UEFI Driver Model protocol(s). + // Try to open the DHCP6, HTTP and Device Path protocol. // - return EfiLibInstallDriverBindingComponentName2 ( - ImageHandle, - SystemTable, - &gHttpBootIp4DxeDriverBinding, - ImageHandle, - &gHttpBootDxeComponentName, - &gHttpBootDxeComponentName2 - ); + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiHttpServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; + +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpBootIp6DxeDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + HTTP_BOOT_PRIVATE_DATA *Private; + EFI_DEV_PATH *Node; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINT32 *Id; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + Private = HTTP_BOOT_PRIVATE_DATA_FROM_ID(Id); + } else { + // + // Initialize the private data structure. + // + Private = AllocateZeroPool (sizeof (HTTP_BOOT_PRIVATE_DATA)); + if (Private == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Private->Signature = HTTP_BOOT_PRIVATE_DATA_SIGNATURE; + Private->Controller = ControllerHandle; + Private->Image = This->ImageHandle; + InitializeListHead (&Private->CacheList); + // + // Get the NII interface if it exists, it's not required. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + (VOID **) &Private->Nii, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Private->Nii = NULL; + } + + // + // Open Device Path Protocol to prepare for appending IP and URI node. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &Private->ParentDevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Install a protocol with Caller Id Guid to the NIC, this is just to build the relationship between + // NIC handle and the private data. + // + Status = gBS->InstallProtocolInterface ( + &ControllerHandle, + &gEfiCallerIdGuid, + EFI_NATIVE_INTERFACE, + &Private->Id + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + } + + if (Private->Ip6Nic != NULL) { + // + // Already created before + // + return EFI_SUCCESS; + } + + Private->Ip6Nic = AllocateZeroPool (sizeof (HTTP_BOOT_VIRTUAL_NIC)); + if (Private->Ip6Nic == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Private->Ip6Nic->Private = Private; + Private->Ip6Nic->Signature = HTTP_BOOT_VIRTUAL_NIC_SIGNATURE; + + // + // Create Dhcp6 child and open Dhcp6 protocol + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + &Private->Dhcp6Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Dhcp6Child, + &gEfiDhcp6ProtocolGuid, + (VOID **) &Private->Dhcp6, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Ip6 child and open Ip6 protocol for background ICMP packets. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiIp6ServiceBindingProtocolGuid, + &Private->Ip6Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Ip6Child, + &gEfiIp6ProtocolGuid, + (VOID **) &Private->Ip6, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Locate Ip6Config protocol, it's required to configure the default gateway address. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp6ConfigProtocolGuid, + (VOID **) &Private->Ip6Config, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Append IPv6 device path node. + // + Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH)); + if (Node == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + Node->Ipv6.Header.Type = MESSAGING_DEVICE_PATH; + Node->Ipv6.Header.SubType = MSG_IPv6_DP; + Node->Ipv6.PrefixLength = IP6_PREFIX_LENGTH; + SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH)); + DevicePath = AppendDevicePathNode(Private->ParentDevicePath, (EFI_DEVICE_PATH*) Node); + FreePool(Node); + if (DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Append URI device path node. + // + Node = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL)); + if (Node == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + Node->DevPath.Type = MESSAGING_DEVICE_PATH; + Node->DevPath.SubType = MSG_URI_DP; + SetDevicePathNodeLength (Node, sizeof (EFI_DEVICE_PATH_PROTOCOL)); + Private->Ip6Nic->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + FreePool (DevicePath); + if (Private->Ip6Nic->DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Create a child handle for the HTTP boot and install DevPath and Load file protocol on it. + // + CopyMem (&Private->Ip6Nic->LoadFile, &gHttpBootDxeLoadFile, sizeof (Private->LoadFile)); + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Ip6Nic->Controller, + &gEfiLoadFileProtocolGuid, + &Private->Ip6Nic->LoadFile, + &gEfiDevicePathProtocolGuid, + Private->Ip6Nic->DevicePath, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Open the Caller Id child to setup a parent-child relationship between + // real NIC handle and the HTTP boot child handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + Private->Ip6Nic->Controller, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + + HttpBootDestroyIp6Children(This, Private); + FreePool (Private); + + return Status; + +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpBootIp6DxeDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_LOAD_FILE_PROTOCOL *LoadFile; + HTTP_BOOT_PRIVATE_DATA *Private; + EFI_HANDLE NicHandle; + UINT32 *Id; + + // + // Try to get the Load File Protocol from the controller handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiLoadFileProtocolGuid, + (VOID **) &LoadFile, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + // + // If failed, try to find the NIC handle for this controller. + // + NicHandle = HttpBootGetNicByIp6Children (ControllerHandle); + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + // + // Try to retrieve the private data by the Caller Id Guid. + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + Private = HTTP_BOOT_PRIVATE_DATA_FROM_ID (Id); + } else { + Private = HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE (LoadFile); + NicHandle = Private->Controller; + } + + // + // Disable the HTTP boot function. + // + Status = HttpBootStop (Private); + if (Status != EFI_SUCCESS && Status != EFI_NOT_STARTED) { + return Status; + } + + // + // Destory all child instance and uninstall protocol interface. + // + HttpBootDestroyIp6Children (This, Private); + + if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) { + // + // Release the cached data. + // + HttpBootFreeCacheList (Private); + + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiCallerIdGuid, + &Private->Id + ); + FreePool (Private); + + } + + return EFI_SUCCESS; +} +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval Others An unexpected error occurred. + +**/ +EFI_STATUS +EFIAPI +HttpBootDxeDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + // + // Install UEFI Driver Model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gHttpBootIp4DxeDriverBinding, + ImageHandle, + &gHttpBootDxeComponentName, + &gHttpBootDxeComponentName2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gHttpBootIp6DxeDriverBinding, + NULL, + &gHttpBootDxeComponentName, + &gHttpBootDxeComponentName2 + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces( + ImageHandle, + &gEfiDriverBindingProtocolGuid, + &gHttpBootIp4DxeDriverBinding, + &gEfiComponentName2ProtocolGuid, + &gHttpBootDxeComponentName2, + &gEfiComponentNameProtocolGuid, + &gHttpBootDxeComponentName, + NULL + ); + } + return Status; } diff --git a/NetworkPkg/HttpBootDxe/HttpBootDxe.h b/NetworkPkg/HttpBootDxe/HttpBootDxe.h index 08415f6e0c..452c8f4906 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootDxe.h +++ b/NetworkPkg/HttpBootDxe/HttpBootDxe.h @@ -41,9 +41,11 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. // #include #include +#include +#include #include #include - +#include // // Produced Protocols // @@ -65,29 +67,45 @@ extern EFI_COMPONENT_NAME_PROTOCOL gHttpBootDxeComponentName; // Private data structure // typedef struct _HTTP_BOOT_PRIVATE_DATA HTTP_BOOT_PRIVATE_DATA; +typedef struct _HTTP_BOOT_VIRTUAL_NIC HTTP_BOOT_VIRTUAL_NIC; // // Include files with internal function prototypes // #include "HttpBootComponentName.h" #include "HttpBootDhcp4.h" +#include "HttpBootDhcp6.h" #include "HttpBootImpl.h" #include "HttpBootSupport.h" #include "HttpBootClient.h" typedef union { HTTP_BOOT_DHCP4_PACKET_CACHE Dhcp4; + HTTP_BOOT_DHCP6_PACKET_CACHE Dhcp6; } HTTP_BOOT_DHCP_PACKET_CACHE; +struct _HTTP_BOOT_VIRTUAL_NIC { + UINT32 Signature; + EFI_HANDLE Controller; + EFI_LOAD_FILE_PROTOCOL LoadFile; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + HTTP_BOOT_PRIVATE_DATA *Private; +}; + struct _HTTP_BOOT_PRIVATE_DATA { UINT32 Signature; EFI_HANDLE Controller; EFI_HANDLE Image; + HTTP_BOOT_VIRTUAL_NIC *Ip4Nic; + HTTP_BOOT_VIRTUAL_NIC *Ip6Nic; + // // Cousumed children // + EFI_HANDLE Ip6Child; EFI_HANDLE Dhcp4Child; + EFI_HANDLE Dhcp6Child; HTTP_IO HttpIo; BOOLEAN HttpCreated; @@ -95,14 +113,13 @@ struct _HTTP_BOOT_PRIVATE_DATA { // Consumed protocol // EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii; + EFI_IP6_PROTOCOL *Ip6; EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; EFI_DHCP4_PROTOCOL *Dhcp4; + EFI_DHCP6_PROTOCOL *Dhcp6; EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; - // - // Produced children - // - EFI_HANDLE ChildHandle; // // Produced protocol @@ -119,10 +136,12 @@ struct _HTTP_BOOT_PRIVATE_DATA { EFI_IP_ADDRESS StationIp; EFI_IP_ADDRESS SubnetMask; EFI_IP_ADDRESS GatewayIp; + EFI_IP_ADDRESS ServerIp; UINT16 Port; CHAR8 *BootFileUri; VOID *BootFileUriParser; UINTN BootFileSize; + BOOLEAN NoGateway; // // Cached HTTP data @@ -167,9 +186,10 @@ struct _HTTP_BOOT_PRIVATE_DATA { }; #define HTTP_BOOT_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('H', 'B', 'P', 'D') +#define HTTP_BOOT_VIRTUAL_NIC_SIGNATURE SIGNATURE_32 ('H', 'B', 'V', 'N') #define HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE(a) CR (a, HTTP_BOOT_PRIVATE_DATA, LoadFile, HTTP_BOOT_PRIVATE_DATA_SIGNATURE) #define HTTP_BOOT_PRIVATE_DATA_FROM_ID(a) CR (a, HTTP_BOOT_PRIVATE_DATA, Id, HTTP_BOOT_PRIVATE_DATA_SIGNATURE) - +#define HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE(a) CR (a, HTTP_BOOT_VIRTUAL_NIC, LoadFile, HTTP_BOOT_VIRTUAL_NIC_SIGNATURE) extern EFI_LOAD_FILE_PROTOCOL gHttpBootDxeLoadFile; /** @@ -300,4 +320,131 @@ HttpBootIp4DxeDriverBindingStop ( IN EFI_HANDLE *ChildHandleBuffer OPTIONAL ); +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpBootIp6DxeDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpBootIp6DxeDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpBootIp6DxeDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); #endif diff --git a/NetworkPkg/HttpBootDxe/HttpBootDxe.inf b/NetworkPkg/HttpBootDxe/HttpBootDxe.inf index 18f8f796f0..e24b568ddc 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootDxe.inf +++ b/NetworkPkg/HttpBootDxe/HttpBootDxe.inf @@ -35,6 +35,8 @@ HttpBootImpl.c HttpBootDhcp4.h HttpBootDhcp4.c + HttpBootDhcp6.h + HttpBootDhcp6.c HttpBootSupport.h HttpBootSupport.c HttpBootClient.h @@ -62,6 +64,13 @@ gEfiDhcp4ServiceBindingProtocolGuid ## TO_START gEfiDhcp4ProtocolGuid ## TO_START gEfiIp4Config2ProtocolGuid ## TO_START + gEfiDhcp6ServiceBindingProtocolGuid ## TO_START + gEfiDhcp6ProtocolGuid ## TO_START + gEfiDns6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiDns6ProtocolGuid ## SOMETIMES_CONSUMES + gEfiIp6ServiceBindingProtocolGuid ## TO_START + gEfiIp6ProtocolGuid ## TO_START + gEfiIp6ConfigProtocolGuid ## TO_START gEfiNetworkInterfaceIdentifierProtocolGuid_31 ## SOMETIMES_CONSUMES [UserExtensions.TianoCore."ExtraFiles"] diff --git a/NetworkPkg/HttpBootDxe/HttpBootDxe.uni b/NetworkPkg/HttpBootDxe/HttpBootDxe.uni index 68a33794b2..fe743df852 100644 Binary files a/NetworkPkg/HttpBootDxe/HttpBootDxe.uni and b/NetworkPkg/HttpBootDxe/HttpBootDxe.uni differ diff --git a/NetworkPkg/HttpBootDxe/HttpBootImpl.c b/NetworkPkg/HttpBootDxe/HttpBootImpl.c index eee63c21d3..9ea0d7f95f 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootImpl.c +++ b/NetworkPkg/HttpBootDxe/HttpBootImpl.c @@ -18,6 +18,9 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. Enable the use of UEFI HTTP boot function. @param[in] Private The pointer to the driver's private data. + @param[in] UsingIpv6 Specifies the type of IP addresses that are to be + used during the session that is being started. + Set to TRUE for IPv6, and FALSE for IPv4. @retval EFI_SUCCESS HTTP boot was successfully enabled. @retval EFI_INVALID_PARAMETER Private is NULL. @@ -26,10 +29,12 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ EFI_STATUS HttpBootStart ( - IN HTTP_BOOT_PRIVATE_DATA *Private + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN BOOLEAN UsingIpv6 ) { - UINTN Index; + UINTN Index; + EFI_STATUS Status; if (Private == NULL) { return EFI_INVALID_PARAMETER; @@ -39,25 +44,47 @@ HttpBootStart ( return EFI_ALREADY_STARTED; } + // + // Detect whether using ipv6 or not, and set it to the private data. + // + if (UsingIpv6 && Private->Ip6Nic != NULL) { + Private->UsingIpv6 = TRUE; + } else if (!UsingIpv6 && Private->Ip4Nic != NULL) { + Private->UsingIpv6 = FALSE; + } else { + return EFI_UNSUPPORTED; + } + + // + // Init the content of cached DHCP offer list. + // + ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer)); if (!Private->UsingIpv6) { - // - // Init the content of cached DHCP offer list. - // - ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer)); for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) { Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = HTTP_BOOT_DHCP4_PACKET_MAX_SIZE; } } else { - ASSERT (FALSE); + for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) { + Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = HTTP_BOOT_DHCP6_PACKET_MAX_SIZE; + } } + if (Private->UsingIpv6) { + // + // Set Ip6 policy to Automatic to start the Ip6 router discovery. + // + Status = HttpBootSetIp6Policy (Private); + if (EFI_ERROR (Status)) { + return Status; + } + } Private->Started = TRUE; return EFI_SUCCESS; } /** - Attempt to complete a DHCPv4 D.O.R.A sequence to retrieve the boot resource information. + Attempt to complete a DHCPv4 D.O.R.A or DHCPv6 S.R.A.A sequence to retrieve the boot resource information. @param[in] Private The pointer to the driver's private data. @@ -86,9 +113,15 @@ HttpBootDhcp ( Status = EFI_DEVICE_ERROR; if (!Private->UsingIpv6) { + // + // Start D.O.R.A process to get a IPv4 address and other boot information. + // Status = HttpBootDhcp4Dora (Private); } else { - ASSERT (FALSE); + // + // Start S.A.R.R process to get a IPv6 address and other boot information. + // + Status = HttpBootDhcp6Sarr (Private); } return Status; @@ -241,7 +274,7 @@ HttpBootStop ( Private->BootFileUriParser = NULL; Private->BootFileSize = 0; Private->SelectIndex = 0; - Private->SelectProxyType = HttpOfferTypeMax; + Private->SelectProxyType = HttpOfferTypeMax; if (!Private->UsingIpv6) { // @@ -256,7 +289,17 @@ HttpBootStop ( } } } else { - ASSERT (FALSE); + // + // Stop and release the DHCP6 child. + // + Private->Dhcp6->Stop (Private->Dhcp6); + Private->Dhcp6->Configure (Private->Dhcp6, NULL); + + for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) { + if (Private->OfferBuffer[Index].Dhcp6.UriParser) { + HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp6.UriParser); + } + } } ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer)); @@ -309,7 +352,9 @@ HttpBootDxeLoadFile ( ) { HTTP_BOOT_PRIVATE_DATA *Private; + HTTP_BOOT_VIRTUAL_NIC *VirtualNic; BOOLEAN MediaPresent; + BOOLEAN UsingIpv6; EFI_STATUS Status; if (This == NULL || BufferSize == NULL) { @@ -323,8 +368,10 @@ HttpBootDxeLoadFile ( return EFI_UNSUPPORTED; } - Private = HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE (This); - + VirtualNic = HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE (This); + Private = VirtualNic->Private; + UsingIpv6 = FALSE; + // // Check media status before HTTP boot start // @@ -334,10 +381,26 @@ HttpBootDxeLoadFile ( return EFI_NO_MEDIA; } + // + // Check whether the virtual nic is using IPv6 or not. + // + if (VirtualNic == Private->Ip6Nic) { + UsingIpv6 = TRUE; + } + // // Initialize HTTP boot and load the boot file. // - Status = HttpBootStart (Private); + Status = HttpBootStart (Private, UsingIpv6); + if (Status == EFI_ALREADY_STARTED && UsingIpv6 != Private->UsingIpv6) { + // + // Http boot Driver has already been started but not on the required IP version, restart it. + // + Status = HttpBootStop (Private); + if (!EFI_ERROR (Status)) { + Status = HttpBootStart (Private, UsingIpv6); + } + } if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) { Status = HttpBootLoadFile (Private, BufferSize, Buffer); } @@ -345,11 +408,19 @@ HttpBootDxeLoadFile ( if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) { HttpBootStop (Private); } else { - // - // Stop and release the DHCP4 child. - // - Private->Dhcp4->Stop (Private->Dhcp4); - Private->Dhcp4->Configure (Private->Dhcp4, NULL); + if (!Private->UsingIpv6) { + // + // Stop and release the DHCP4 child. + // + Private->Dhcp4->Stop (Private->Dhcp4); + Private->Dhcp4->Configure (Private->Dhcp4, NULL); + } else { + // + // Stop and release the DHCP6 child. + // + Private->Dhcp6->Stop (Private->Dhcp6); + Private->Dhcp6->Configure (Private->Dhcp6, NULL); + } } return Status; diff --git a/NetworkPkg/HttpBootDxe/HttpBootImpl.h b/NetworkPkg/HttpBootDxe/HttpBootImpl.h index a2e9f5a328..7066338175 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootImpl.h +++ b/NetworkPkg/HttpBootDxe/HttpBootImpl.h @@ -15,7 +15,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define __EFI_HTTP_BOOT_IMPL_H__ /** - Attempt to complete a DHCPv4 D.O.R.A sequence to retrieve the boot resource information. + Attempt to complete a DHCPv4 D.O.R.A or DHCPv6 S.R.A.A sequence to retrieve the boot resource information. @param[in] Private The pointer to the driver's private data. diff --git a/NetworkPkg/HttpBootDxe/HttpBootSupport.c b/NetworkPkg/HttpBootDxe/HttpBootSupport.c index 761643141f..d08111f661 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootSupport.c +++ b/NetworkPkg/HttpBootDxe/HttpBootSupport.c @@ -42,6 +42,31 @@ HttpBootGetNicByIp4Children ( return NicHandle; } +/** + Get the Nic handle using any child handle in the IPv6 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv6. + + @return NicHandle The pointer to the Nic handle. + @return NULL Can't find the Nic handle. + +**/ +EFI_HANDLE +HttpBootGetNicByIp6Children ( + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_HANDLE NicHandle; + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid); + if (NicHandle == NULL) { + return NULL; + } + } + + return NicHandle; +} /** This function is to convert UINTN to ASCII string with the required formatting. @@ -89,6 +114,242 @@ HttpBootShowIp4Addr ( } } +/** + This function is to display the IPv6 address. + + @param[in] Ip The pointer to the IPv6 address. + +**/ +VOID +HttpBootShowIp6Addr ( + IN EFI_IPv6_ADDRESS *Ip + ) +{ + UINTN Index; + + for (Index = 0; Index < 16; Index++) { + + if (Ip->Addr[Index] != 0) { + AsciiPrint ("%x", Ip->Addr[Index]); + } + Index++; + if (Index > 15) { + return; + } + if (((Ip->Addr[Index] & 0xf0) == 0) && (Ip->Addr[Index - 1] != 0)) { + AsciiPrint ("0"); + } + AsciiPrint ("%x", Ip->Addr[Index]); + if (Index < 15) { + AsciiPrint (":"); + } + } +} + +/** + Notify the callback function when an event is triggered. + + @param[in] Event The triggered event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +HttpBootCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + *((BOOLEAN *) Context) = TRUE; +} + +/** + Retrieve the host address using the EFI_DNS6_PROTOCOL. + + @param[in] Private The pointer to the driver's private data. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv6 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. +**/ +EFI_STATUS +HttpBootDns ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN CHAR16 *HostName, + OUT EFI_IPv6_ADDRESS *IpAddress + ) +{ + EFI_STATUS Status; + EFI_DNS6_PROTOCOL *Dns6; + EFI_DNS6_CONFIG_DATA Dns6ConfigData; + EFI_DNS6_COMPLETION_TOKEN Token; + EFI_HANDLE Dns6Handle; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_IPv6_ADDRESS *DnsServerList; + UINTN DnsServerListCount; + UINTN DataSize; + BOOLEAN IsDone; + + DnsServerList = NULL; + DnsServerListCount = 0; + Dns6 = NULL; + Dns6Handle = NULL; + ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN)); + + // + // Get DNS server list from EFI IPv6 Configuration protocol. + // + Status = gBS->HandleProtocol (Private->Controller, &gEfiIp6ConfigProtocolGuid, (VOID **) &Ip6Config); + if (!EFI_ERROR (Status)) { + // + // Get the required size. + // + DataSize = 0; + Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, NULL); + if (Status == EFI_BUFFER_TOO_SMALL) { + DnsServerList = AllocatePool (DataSize); + if (DnsServerList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, DnsServerList); + if (EFI_ERROR (Status)) { + FreePool (DnsServerList); + DnsServerList = NULL; + } else { + DnsServerListCount = DataSize / sizeof (EFI_IPv6_ADDRESS); + } + } + } + // + // Create a DNSv6 child instance and get the protocol. + // + Status = NetLibCreateServiceChild ( + Private->Controller, + Private->Image, + &gEfiDns6ServiceBindingProtocolGuid, + &Dns6Handle + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->OpenProtocol ( + Dns6Handle, + &gEfiDns6ProtocolGuid, + (VOID **) &Dns6, + Private->Image, + Private->Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Configure DNS6 instance for the DNS server address and protocol. + // + ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA)); + Dns6ConfigData.DnsServerCount = (UINT32)DnsServerListCount; + Dns6ConfigData.DnsServerList = DnsServerList; + Dns6ConfigData.EnableDnsCache = TRUE; + Dns6ConfigData.Protocol = EFI_IP_PROTO_UDP; + IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp,&Private->StationIp.v6); + Status = Dns6->Configure ( + Dns6, + &Dns6ConfigData + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Token.Status = EFI_NOT_READY; + IsDone = FALSE; + // + // Create event to set the IsDone flag when name resolution is finished. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpBootCommonNotify, + &IsDone, + &Token.Event + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Start asynchronous name resolution. + // + Status = Dns6->HostNameToIp (Dns6, HostName, &Token); + if (EFI_ERROR (Status)) { + goto Exit; + } + + while (!IsDone) { + Dns6->Poll (Dns6); + } + + // + // Name resolution is done, check result. + // + Status = Token.Status; + if (!EFI_ERROR (Status)) { + if (Token.RspData.H2AData == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // We just return the first IPv6 address from DNS protocol. + // + IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList); + Status = EFI_SUCCESS; + } +Exit: + + if (Token.Event != NULL) { + gBS->CloseEvent (Token.Event); + } + if (Token.RspData.H2AData != NULL) { + if (Token.RspData.H2AData->IpList != NULL) { + FreePool (Token.RspData.H2AData->IpList); + } + FreePool (Token.RspData.H2AData); + } + + if (Dns6 != NULL) { + Dns6->Configure (Dns6, NULL); + + gBS->CloseProtocol ( + Dns6Handle, + &gEfiDns6ProtocolGuid, + Private->Image, + Private->Controller + ); + } + + if (Dns6Handle != NULL) { + NetLibDestroyServiceChild ( + Private->Controller, + Private->Image, + &gEfiDns6ServiceBindingProtocolGuid, + Dns6Handle + ); + } + + if (DnsServerList != NULL) { + FreePool (DnsServerList); + } + + return Status; +} /** Create a HTTP_IO_HEADER to hold the HTTP header items. @@ -100,7 +361,7 @@ HttpBootShowIp4Addr ( HTTP_IO_HEADER * HttpBootCreateHeader ( UINTN MaxHeaderCount -) + ) { HTTP_IO_HEADER *HttpIoHeader; @@ -254,23 +515,6 @@ HttpBootSetHeader ( return EFI_SUCCESS; } -/** - Notify the callback function when an event is triggered. - - @param[in] Event The triggered event. - @param[in] Context The opaque parameter to the function. - -**/ -VOID -EFIAPI -HttpIoCommonNotify ( - IN EFI_EVENT Event, - IN VOID *Context - ) -{ - *((BOOLEAN *) Context) = TRUE; -} - /** Create a HTTP_IO to access the HTTP service. It will create and configure a HTTP child handle. @@ -301,6 +545,7 @@ HttpIoCreateIo ( EFI_STATUS Status; EFI_HTTP_CONFIG_DATA HttpConfigData; EFI_HTTPv4_ACCESS_POINT Http4AccessPoint; + EFI_HTTPv6_ACCESS_POINT Http6AccessPoint; EFI_HTTP_PROTOCOL *Http; EFI_EVENT Event; @@ -359,7 +604,10 @@ HttpIoCreateIo ( IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask); HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint; } else { - ASSERT (FALSE); + HttpConfigData.LocalAddressIsIPv6 = TRUE; + Http6AccessPoint.LocalPort = ConfigData->Config6.LocalPort; + IP6_COPY_ADDRESS (&Http6AccessPoint.LocalAddress, &ConfigData->Config6.LocalIp); + HttpConfigData.AccessPoint.IPv6Node = &Http6AccessPoint; } Status = Http->Configure (Http, &HttpConfigData); @@ -373,7 +621,7 @@ HttpIoCreateIo ( Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, TPL_NOTIFY, - HttpIoCommonNotify, + HttpBootCommonNotify, &HttpIo->IsTxDone, &Event ); @@ -386,7 +634,7 @@ HttpIoCreateIo ( Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, TPL_NOTIFY, - HttpIoCommonNotify, + HttpBootCommonNotify, &HttpIo->IsRxDone, &Event ); @@ -579,7 +827,7 @@ HttpIoRecvResponse ( // // Store the received data into the wrapper. // - Status = HttpIo->ReqToken.Status; + Status = HttpIo->RspToken.Status; if (!EFI_ERROR (Status)) { ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount; ResponseData->Headers = HttpIo->RspToken.Message->Headers; diff --git a/NetworkPkg/HttpBootDxe/HttpBootSupport.h b/NetworkPkg/HttpBootDxe/HttpBootSupport.h index bef80e81d8..d5956720a7 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootSupport.h +++ b/NetworkPkg/HttpBootDxe/HttpBootSupport.h @@ -29,6 +29,20 @@ HttpBootGetNicByIp4Children ( IN EFI_HANDLE ControllerHandle ); +/** + Get the Nic handle using any child handle in the IPv6 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv6. + + @return NicHandle The pointer to the Nic handle. + @return NULL Can't find the Nic handle. + +**/ +EFI_HANDLE +HttpBootGetNicByIp6Children ( + IN EFI_HANDLE ControllerHandle + ); + /** This function is to convert UINTN to ASCII string with the required formatting. @@ -56,6 +70,17 @@ HttpBootShowIp4Addr ( IN EFI_IPv4_ADDRESS *Ip ); +/** + This function is to display the IPv6 address. + + @param[in] Ip The pointer to the IPv6 address. + +**/ +VOID +HttpBootShowIp6Addr ( + IN EFI_IPv6_ADDRESS *Ip + ); + // // A wrapper structure to hold the HTTP headers. // @@ -122,11 +147,24 @@ typedef struct { UINT16 LocalPort; } HTTP4_IO_CONFIG_DATA; +// +// HTTP_IO configuration data for IPv6 +// +typedef struct { + EFI_HTTP_VERSION HttpVersion; + UINT32 RequestTimeOut; // In milliseconds. + BOOLEAN UseDefaultAddress; + EFI_IPv6_ADDRESS LocalIp; + UINT16 LocalPort; +} HTTP6_IO_CONFIG_DATA; + + // // HTTP_IO configuration // typedef union { HTTP4_IO_CONFIG_DATA Config4; + HTTP6_IO_CONFIG_DATA Config6; } HTTP_IO_CONFIG_DATA; // @@ -160,6 +198,38 @@ typedef struct { CHAR8 *Body; } HTTP_IO_RESOPNSE_DATA; +/** + Retrieve the host address using the EFI_DNS6_PROTOCOL. + + @param[in] Private The pointer to the driver's private data. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv6 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. +**/ +EFI_STATUS +HttpBootDns ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN CHAR16 *HostName, + OUT EFI_IPv6_ADDRESS *IpAddress + ); + +/** + Notify the callback function when an event is triggered. + + @param[in] Event The triggered event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +HttpBootCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + /** Create a HTTP_IO to access the HTTP service. It will create and configure a HTTP child handle. diff --git a/NetworkPkg/HttpDxe/HttpDns.c b/NetworkPkg/HttpDxe/HttpDns.c index daebc173b5..0f5fe18072 100644 --- a/NetworkPkg/HttpDxe/HttpDns.c +++ b/NetworkPkg/HttpDxe/HttpDns.c @@ -194,11 +194,11 @@ Exit: Dns4->Configure (Dns4, NULL); gBS->CloseProtocol ( - Dns4Handle, - &gEfiDns4ProtocolGuid, - Service->ImageHandle, - Service->ControllerHandle - ); + Dns4Handle, + &gEfiDns4ProtocolGuid, + Service->ImageHandle, + Service->ControllerHandle + ); } if (Dns4Handle != NULL) { @@ -216,3 +216,200 @@ Exit: return Status; } + +/** + Retrieve the host address using the EFI_DNS6_PROTOCOL. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL instance. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv6 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpDns6 ( + IN HTTP_PROTOCOL *HttpInstance, + IN CHAR16 *HostName, + OUT EFI_IPv6_ADDRESS *IpAddress + ) +{ + EFI_STATUS Status; + HTTP_SERVICE *Service; + EFI_DNS6_PROTOCOL *Dns6; + EFI_DNS6_CONFIG_DATA Dns6ConfigData; + EFI_DNS6_COMPLETION_TOKEN Token; + EFI_HANDLE Dns6Handle; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_IPv6_ADDRESS *DnsServerList; + UINTN DnsServerListCount; + UINTN DataSize; + BOOLEAN IsDone; + + + Service = HttpInstance->Service; + ASSERT (Service != NULL); + + DnsServerList = NULL; + DnsServerListCount = 0; + Dns6 = NULL; + Dns6Handle = NULL; + ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN)); + + // + // Get DNS server list from EFI IPv6 Configuration protocol. + // + Status = gBS->HandleProtocol (Service->ControllerHandle, &gEfiIp6ConfigProtocolGuid, (VOID **) &Ip6Config); + if (!EFI_ERROR (Status)) { + // + // Get the required size. + // + DataSize = 0; + Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, NULL); + if (Status == EFI_BUFFER_TOO_SMALL) { + DnsServerList = AllocatePool (DataSize); + if (DnsServerList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, DnsServerList); + if (EFI_ERROR (Status)) { + FreePool (DnsServerList); + DnsServerList = NULL; + } else { + DnsServerListCount = DataSize / sizeof (EFI_IPv6_ADDRESS); + } + } + } + + // + // Create a DNSv6 child instance and get the protocol. + // + Status = NetLibCreateServiceChild ( + Service->ControllerHandle, + Service->ImageHandle, + &gEfiDns6ServiceBindingProtocolGuid, + &Dns6Handle + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->OpenProtocol ( + Dns6Handle, + &gEfiDns6ProtocolGuid, + (VOID **) &Dns6, + Service->ImageHandle, + Service->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Configure DNS6 instance for the DNS server address and protocol. + // + ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA)); + Dns6ConfigData.DnsServerCount = (UINT32)DnsServerListCount; + Dns6ConfigData.DnsServerList = DnsServerList; + Dns6ConfigData.EnableDnsCache = TRUE; + Dns6ConfigData.Protocol = EFI_IP_PROTO_UDP; + IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp, &HttpInstance->Ipv6Node.LocalAddress); + Status = Dns6->Configure ( + Dns6, + &Dns6ConfigData + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Token.Status = EFI_NOT_READY; + IsDone = FALSE; + // + // Create event to set the IsDone flag when name resolution is finished. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &IsDone, + &Token.Event + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Start asynchronous name resolution. + // + Status = Dns6->HostNameToIp (Dns6, HostName, &Token); + if (EFI_ERROR (Status)) { + goto Exit; + } + + while (!IsDone) { + Dns6->Poll (Dns6); + } + + // + // Name resolution is done, check result. + // + Status = Token.Status; + if (!EFI_ERROR (Status)) { + if (Token.RspData.H2AData == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // We just return the first IPv6 address from DNS protocol. + // + IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList); + Status = EFI_SUCCESS; + } + +Exit: + + if (Token.Event != NULL) { + gBS->CloseEvent (Token.Event); + } + if (Token.RspData.H2AData != NULL) { + if (Token.RspData.H2AData->IpList != NULL) { + FreePool (Token.RspData.H2AData->IpList); + } + FreePool (Token.RspData.H2AData); + } + + if (Dns6 != NULL) { + Dns6->Configure (Dns6, NULL); + + gBS->CloseProtocol ( + Dns6Handle, + &gEfiDns6ProtocolGuid, + Service->ImageHandle, + Service->ControllerHandle + ); + } + + if (Dns6Handle != NULL) { + NetLibDestroyServiceChild ( + Service->ControllerHandle, + Service->ImageHandle, + &gEfiDns6ServiceBindingProtocolGuid, + Dns6Handle + ); + } + + if (DnsServerList != NULL) { + FreePool (DnsServerList); + } + + return Status; +} diff --git a/NetworkPkg/HttpDxe/HttpDns.h b/NetworkPkg/HttpDxe/HttpDns.h index 0fb418635c..fa0c8f4a99 100644 --- a/NetworkPkg/HttpDxe/HttpDns.h +++ b/NetworkPkg/HttpDxe/HttpDns.h @@ -35,4 +35,24 @@ HttpDns4 ( OUT EFI_IPv4_ADDRESS *IpAddress ); +/** + Retrieve the host address using the EFI_DNS6_PROTOCOL. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL instance. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv6 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpDns6 ( + IN HTTP_PROTOCOL *HttpInstance, + IN CHAR16 *HostName, + OUT EFI_IPv6_ADDRESS *IpAddress + ); + #endif \ No newline at end of file diff --git a/NetworkPkg/HttpDxe/HttpDriver.c b/NetworkPkg/HttpDxe/HttpDriver.c index bd1d04e78c..2518f4e707 100644 --- a/NetworkPkg/HttpDxe/HttpDriver.c +++ b/NetworkPkg/HttpDxe/HttpDriver.c @@ -20,15 +20,25 @@ EFI_HTTP_UTILITIES_PROTOCOL *mHttpUtilities = NULL; /// /// Driver Binding Protocol instance /// -EFI_DRIVER_BINDING_PROTOCOL gHttpDxeDriverBinding = { - HttpDxeDriverBindingSupported, - HttpDxeDriverBindingStart, - HttpDxeDriverBindingStop, +EFI_DRIVER_BINDING_PROTOCOL gHttpDxeIp4DriverBinding = { + HttpDxeIp4DriverBindingSupported, + HttpDxeIp4DriverBindingStart, + HttpDxeIp4DriverBindingStop, HTTP_DRIVER_VERSION, NULL, NULL }; +EFI_DRIVER_BINDING_PROTOCOL gHttpDxeIp6DriverBinding = { + HttpDxeIp6DriverBindingSupported, + HttpDxeIp6DriverBindingStart, + HttpDxeIp6DriverBindingStop, + HTTP_DRIVER_VERSION, + NULL, + NULL +}; + + /** Create a HTTP driver service binding private instance. @@ -73,33 +83,59 @@ HttpCreateService ( /** Release all the resource used the HTTP service binding instance. - @param HttpService The HTTP private instance. - + @param[in] HttpService The HTTP private instance. + @param[in] UsingIpv6 Indicate use TCP4 protocol or TCP6 protocol. + if TRUE, use Tcp6 protocol. + if FALSE, use Tcp4 protocl. **/ VOID HttpCleanService ( - IN HTTP_SERVICE *HttpService + IN HTTP_SERVICE *HttpService, + IN BOOLEAN UsingIpv6 ) -{ +{ + if (HttpService == NULL) { return ; } - - if (HttpService->TcpChildHandle != NULL) { - gBS->CloseProtocol ( - HttpService->TcpChildHandle, - &gEfiTcp4ProtocolGuid, - HttpService->ImageHandle, - HttpService->ControllerHandle - ); - - NetLibDestroyServiceChild ( - HttpService->ControllerHandle, - HttpService->ImageHandle, - &gEfiTcp4ServiceBindingProtocolGuid, - HttpService->TcpChildHandle - ); + if (!UsingIpv6) { + if (HttpService->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpService->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpService->ImageHandle, + HttpService->ControllerHandle + ); + + NetLibDestroyServiceChild ( + HttpService->ControllerHandle, + HttpService->ImageHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + HttpService->Tcp4ChildHandle + ); + + HttpService->Tcp4ChildHandle = NULL; + } + } else { + if (HttpService->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpService->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpService->ImageHandle, + HttpService->ControllerHandle + ); + + NetLibDestroyServiceChild ( + HttpService->ControllerHandle, + HttpService->ImageHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + HttpService->Tcp6ChildHandle + ); + + HttpService->Tcp6ChildHandle = NULL; + } } + } /** @@ -107,7 +143,7 @@ HttpCleanService ( in the system. @param[in] Event Not used. - @param[in] Context The pointer to the IP4 config2 instance data. + @param[in] Context The pointer to the IP4 config2 instance data or IP6 Config instance data. **/ VOID @@ -122,7 +158,7 @@ HttpUtilitiesInstalledCallback ( NULL, (VOID **) &mHttpUtilities ); - + // // Close the event if Http utilities protocol is loacted. // @@ -150,6 +186,7 @@ HttpDxeDriverEntryPoint ( IN EFI_SYSTEM_TABLE *SystemTable ) { + EFI_STATUS Status; VOID *Registration; gBS->LocateProtocol ( @@ -174,14 +211,39 @@ HttpDxeDriverEntryPoint ( // // Install UEFI Driver Model protocol(s). // - return EfiLibInstallDriverBindingComponentName2 ( - ImageHandle, - SystemTable, - &gHttpDxeDriverBinding, + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gHttpDxeIp4DriverBinding, + ImageHandle, + &gHttpDxeComponentName, + &gHttpDxeComponentName2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gHttpDxeIp6DriverBinding, + NULL, + &gHttpDxeComponentName, + &gHttpDxeComponentName2 + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( ImageHandle, + &gEfiDriverBindingProtocolGuid, + &gHttpDxeIp4DriverBinding, + &gEfiComponentName2ProtocolGuid, + &gHttpDxeComponentName2, + &gEfiComponentNameProtocolGuid, &gHttpDxeComponentName, - &gHttpDxeComponentName2 + NULL ); + } + return Status; } /** @@ -223,6 +285,309 @@ HttpDestroyChildEntryInHandleBuffer ( return ServiceBinding->DestroyChild (ServiceBinding, HttpInstance->Handle); } +/** + Test to see if this driver supports ControllerHandle. This is the worker function for + HttpDxeIp4(6)DriverBindingSupported. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + EFI_GUID *TcpServiceBindingProtocolGuid; + + if (IpVersion == IP_VERSION_4) { + TcpServiceBindingProtocolGuid = &gEfiTcp4ServiceBindingProtocolGuid; + } else { + TcpServiceBindingProtocolGuid = &gEfiTcp6ServiceBindingProtocolGuid; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + TcpServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Start this driver on ControllerHandle. This is the worker function for + HttpDxeIp4(6)DriverBindingStart. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + HTTP_SERVICE *HttpService; + VOID *Interface; + BOOLEAN UsingIpv6; + + UsingIpv6 = FALSE; + + // + // Test for the Http service binding protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiHttpServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + HttpService = HTTP_SERVICE_FROM_PROTOCOL (ServiceBinding); + } else { + Status = HttpCreateService (ControllerHandle, This->DriverBindingHandle, &HttpService); + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (HttpService != NULL); + + // + // Install the HttpServiceBinding Protocol onto Controller + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiHttpServiceBindingProtocolGuid, + &HttpService->ServiceBinding, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + if (IpVersion == IP_VERSION_4) { + + if (HttpService->Tcp4ChildHandle == NULL) { + // + // Create a TCP4 child instance, but do not configure it. This will establish the parent-child relationship. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + &HttpService->Tcp4ChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpService->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + &Interface, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + } else { + return EFI_ALREADY_STARTED; + } + + } else { + UsingIpv6 = TRUE; + + if (HttpService->Tcp6ChildHandle == NULL) { + // + // Create a TCP6 child instance, but do not configure it. This will establish the parent-child relationship. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + &HttpService->Tcp6ChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpService->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + &Interface, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + } else { + return EFI_ALREADY_STARTED; + } + + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (HttpService != NULL) { + HttpCleanService (HttpService, UsingIpv6); + if (HttpService->Tcp4ChildHandle == NULL && HttpService->Tcp6ChildHandle == NULL) { + FreePool (HttpService); + } + } + + return Status; + + +} + +/** + Stop this driver on ControllerHandle. This is the worker function for + HttpDxeIp4(6)DriverBindingStop. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCESS This driver was removed ControllerHandle. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +HttpDxeStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer, + IN UINT8 IpVersion + ) +{ + EFI_HANDLE NicHandle; + EFI_STATUS Status; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + HTTP_SERVICE *HttpService; + LIST_ENTRY *List; + HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context; + BOOLEAN UsingIpv6; + + // + // HTTP driver opens TCP4(6) child, So, Controller is a TCP4(6) + // child handle. Locate the Nic handle first. Then get the + // HTTP private data back. + // + if (IpVersion == IP_VERSION_4) { + UsingIpv6 = FALSE; + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiTcp4ProtocolGuid); + } else { + UsingIpv6 = TRUE; + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiTcp6ProtocolGuid); + } + + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiHttpServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + + HttpService = HTTP_SERVICE_FROM_PROTOCOL (ServiceBinding); + + if (NumberOfChildren != 0) { + // + // Destroy the HTTP child instance in ChildHandleBuffer. + // + List = &HttpService->ChildrenList; + Context.ServiceBinding = ServiceBinding; + Context.NumberOfChildren = NumberOfChildren; + Context.ChildHandleBuffer = ChildHandleBuffer; + Status = NetDestroyLinkList ( + List, + HttpDestroyChildEntryInHandleBuffer, + &Context, + NULL + ); + } else { + + HttpCleanService (HttpService, UsingIpv6); + + if (HttpService->Tcp4ChildHandle == NULL && HttpService->Tcp6ChildHandle == NULL) { + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiHttpServiceBindingProtocolGuid, + ServiceBinding + ); + FreePool (HttpService); + } + Status = EFI_SUCCESS; + } + } + + return Status; + +} + /** Tests to see if this driver supports a given controller. If a child device is provided, it further tests to see if this driver supports creating a handle for the specified child device. @@ -267,41 +632,18 @@ HttpDestroyChildEntryInHandleBuffer ( **/ EFI_STATUS EFIAPI -HttpDxeDriverBindingSupported ( +HttpDxeIp4DriverBindingSupported ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL ) { - EFI_STATUS Status; - - // - // Test for the HttpServiceBinding protocol. - // - Status = gBS->OpenProtocol ( - ControllerHandle, - &gEfiHttpServiceBindingProtocolGuid, - NULL, - This->DriverBindingHandle, - ControllerHandle, - EFI_OPEN_PROTOCOL_TEST_PROTOCOL - ); - if (!EFI_ERROR (Status)) { - return EFI_ALREADY_STARTED; - } - - // - // Test for the Tcp4 Protocol - // - return gBS->OpenProtocol ( - ControllerHandle, - &gEfiTcp4ServiceBindingProtocolGuid, - NULL, - This->DriverBindingHandle, - ControllerHandle, - EFI_OPEN_PROTOCOL_TEST_PROTOCOL - ); - + return HttpDxeSupported ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_4 + ); } /** @@ -342,90 +684,173 @@ HttpDxeDriverBindingSupported ( **/ EFI_STATUS EFIAPI -HttpDxeDriverBindingStart ( +HttpDxeIp4DriverBindingStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL ) { - EFI_STATUS Status; - HTTP_SERVICE *HttpService; - VOID *Interface; + return HttpDxeStart ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_4 + ); +} + +/** + Stops a device controller or a bus controller. - // - // Test for the Http service binding protocol - // - Status = gBS->OpenProtocol ( - ControllerHandle, - &gEfiHttpServiceBindingProtocolGuid, - NULL, - This->DriverBindingHandle, - ControllerHandle, - EFI_OPEN_PROTOCOL_TEST_PROTOCOL - ); + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. - if (Status == EFI_SUCCESS) { - return EFI_ALREADY_STARTED; - } + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. - Status = HttpCreateService (ControllerHandle, This->DriverBindingHandle, &HttpService); - if (EFI_ERROR (Status)) { - return Status; - } +**/ +EFI_STATUS +EFIAPI +HttpDxeIp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + return HttpDxeStop ( + This, + ControllerHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_4 + ); +} - ASSERT (HttpService != NULL); +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. - // - // Create a TCP child instance, but do not configure it. This will establish the parent-child relationship. - // - Status = NetLibCreateServiceChild ( - ControllerHandle, - This->DriverBindingHandle, - &gEfiTcp4ServiceBindingProtocolGuid, - &HttpService->TcpChildHandle - ); + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. - if (EFI_ERROR (Status)) { - goto ON_ERROR; - } + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. - Status = gBS->OpenProtocol ( - HttpService->TcpChildHandle, - &gEfiTcp4ProtocolGuid, - &Interface, - This->DriverBindingHandle, - ControllerHandle, - EFI_OPEN_PROTOCOL_BY_DRIVER - ); - - if (EFI_ERROR (Status)) { - goto ON_ERROR; - } + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return HttpDxeSupported ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_6 + ); - // - // Install the HttpServiceBinding Protocol onto Controller - // - Status = gBS->InstallMultipleProtocolInterfaces ( - &ControllerHandle, - &gEfiHttpServiceBindingProtocolGuid, - &HttpService->ServiceBinding, - NULL - ); +} - if (EFI_ERROR (Status)) { - goto ON_ERROR; - } +/** + Starts a device controller or a bus controller. - return EFI_SUCCESS; + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. -ON_ERROR: + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. - if (HttpService != NULL) { - HttpCleanService (HttpService); - FreePool (HttpService); - } - - return Status; + @retval EFI_SUCCESS The device was started. + @retval EFI_ALREADY_STARTED This device is already running on ControllerHandle. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return HttpDxeStart ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_6 + ); } /** @@ -456,78 +881,21 @@ ON_ERROR: **/ EFI_STATUS EFIAPI -HttpDxeDriverBindingStop ( +HttpDxeIp6DriverBindingStop ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer OPTIONAL ) { - EFI_HANDLE NicHandle; - EFI_STATUS Status; - EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; - HTTP_SERVICE *HttpService; - LIST_ENTRY *List; - HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context; - - // - // HTTP driver opens TCP child, So, Controller is a TCP - // child handle. Locate the Nic handle first. Then get the - // HTTP private data back. - // - NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiTcp4ProtocolGuid); - if (NicHandle == NULL) { - return EFI_SUCCESS; - } - - Status = gBS->OpenProtocol ( - NicHandle, - &gEfiHttpServiceBindingProtocolGuid, - (VOID **) &ServiceBinding, - This->DriverBindingHandle, - NicHandle, - EFI_OPEN_PROTOCOL_GET_PROTOCOL - ); - - if (EFI_ERROR (Status)) { - return EFI_DEVICE_ERROR; - } - - HttpService = HTTP_SERVICE_FROM_PROTOCOL (ServiceBinding); - - if (!IsListEmpty (&HttpService->ChildrenList)) { - // - // Destroy the HTTP child instance in ChildHandleBuffer. - // - List = &HttpService->ChildrenList; - Context.ServiceBinding = ServiceBinding; - Context.NumberOfChildren = NumberOfChildren; - Context.ChildHandleBuffer = ChildHandleBuffer; - Status = NetDestroyLinkList ( - List, - HttpDestroyChildEntryInHandleBuffer, - &Context, - NULL - ); - } - - if (NumberOfChildren == 0 && IsListEmpty (&HttpService->ChildrenList)) { - gBS->UninstallProtocolInterface ( - NicHandle, - &gEfiHttpServiceBindingProtocolGuid, - ServiceBinding + return HttpDxeStop ( + This, + ControllerHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_6 ); - - HttpCleanService (HttpService); - - FreePool (HttpService); - - Status = EFI_SUCCESS; - } - - return Status; } - /** Creates a child handle and installs a protocol. @@ -557,7 +925,6 @@ HttpServiceBindingCreateChild ( HTTP_SERVICE *HttpService; HTTP_PROTOCOL *HttpInstance; EFI_STATUS Status; - VOID *Interface; EFI_TPL OldTpl; if ((This == NULL) || (ChildHandle == NULL)) { @@ -569,6 +936,12 @@ HttpServiceBindingCreateChild ( if (HttpInstance == NULL) { return EFI_OUT_OF_RESOURCES; } + + HttpInstance->Signature = HTTP_PROTOCOL_SIGNATURE; + HttpInstance->Service = HttpService; + CopyMem (&HttpInstance->Http, &mEfiHttpTemplate, sizeof (HttpInstance->Http)); + NetMapInit (&HttpInstance->TxTokens); + NetMapInit (&HttpInstance->RxTokens); // // Install HTTP protocol onto ChildHandle @@ -584,27 +957,7 @@ HttpServiceBindingCreateChild ( goto ON_ERROR; } - HttpInstance->Handle = *ChildHandle; - - Status = HttpInitProtocol (HttpService, HttpInstance); - if (EFI_ERROR (Status)) { - goto ON_ERROR; - } - - // - // Open the default Tcp4 protocol by child. - // - Status = gBS->OpenProtocol ( - HttpService->TcpChildHandle, - &gEfiTcp4ProtocolGuid, - (VOID **) &Interface, - gHttpDxeDriverBinding.DriverBindingHandle, - HttpInstance->Handle, - EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER - ); - if (EFI_ERROR (Status)) { - goto ON_ERROR; - } + HttpInstance->Handle = *ChildHandle; // // Add it to the HTTP service's child list. @@ -619,8 +972,9 @@ HttpServiceBindingCreateChild ( return EFI_SUCCESS; ON_ERROR: - - HttpCleanProtocol (HttpInstance); + + NetMapClean (&HttpInstance->TxTokens); + NetMapClean (&HttpInstance->RxTokens); FreePool (HttpInstance); return Status; @@ -664,8 +1018,8 @@ HttpServiceBindingDestroyChild ( ChildHandle, &gEfiHttpProtocolGuid, (VOID **) &Http, - gHttpDxeDriverBinding.DriverBindingHandle, - ChildHandle, + NULL, + NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { @@ -681,16 +1035,6 @@ HttpServiceBindingDestroyChild ( return EFI_SUCCESS; } - // - // Close the Tcp4 protocol. - // - gBS->CloseProtocol ( - HttpService->TcpChildHandle, - &gEfiTcp4ProtocolGuid, - gHttpDxeDriverBinding.DriverBindingHandle, - ChildHandle - ); - HttpInstance->InDestroy = TRUE; // @@ -706,11 +1050,11 @@ HttpServiceBindingDestroyChild ( HttpInstance->InDestroy = FALSE; return Status; } - - OldTpl = gBS->RaiseTPL (TPL_CALLBACK); - + HttpCleanProtocol (HttpInstance); - + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + RemoveEntryList (&HttpInstance->Link); HttpService->ChildrenNumber--; diff --git a/NetworkPkg/HttpDxe/HttpDriver.h b/NetworkPkg/HttpDxe/HttpDriver.h index eea8d5169e..8fda6b2be4 100644 --- a/NetworkPkg/HttpDxe/HttpDriver.h +++ b/NetworkPkg/HttpDxe/HttpDriver.h @@ -43,8 +43,12 @@ // #include #include +#include #include +#include #include +#include + // // Produced Protocols @@ -59,7 +63,9 @@ // // Protocol instances // -extern EFI_DRIVER_BINDING_PROTOCOL gHttpDxeDriverBinding; +extern EFI_DRIVER_BINDING_PROTOCOL gHttpDxeIp4DriverBinding; +extern EFI_DRIVER_BINDING_PROTOCOL gHttpDxeIp6DriverBinding; + extern EFI_COMPONENT_NAME2_PROTOCOL gHttpDxeComponentName2; extern EFI_COMPONENT_NAME_PROTOCOL gHttpDxeComponentName; @@ -123,7 +129,7 @@ typedef struct { **/ EFI_STATUS EFIAPI -HttpDxeDriverBindingSupported ( +HttpDxeIp4DriverBindingSupported ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL @@ -166,7 +172,7 @@ HttpDxeDriverBindingSupported ( **/ EFI_STATUS EFIAPI -HttpDxeDriverBindingStart ( +HttpDxeIp4DriverBindingStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL @@ -200,13 +206,142 @@ HttpDxeDriverBindingStart ( **/ EFI_STATUS EFIAPI -HttpDxeDriverBindingStop ( +HttpDxeIp4DriverBindingStop ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer OPTIONAL ); +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_ALREADY_STARTED This device is already running on ControllerHandle. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + /** Creates a child handle and installs a protocol. diff --git a/NetworkPkg/HttpDxe/HttpDxe.inf b/NetworkPkg/HttpDxe/HttpDxe.inf index 0d3bd00cf7..bf2cbee5f7 100644 --- a/NetworkPkg/HttpDxe/HttpDxe.inf +++ b/NetworkPkg/HttpDxe/HttpDxe.inf @@ -56,9 +56,14 @@ gEfiHttpUtilitiesProtocolGuid ## CONSUMES gEfiTcp4ServiceBindingProtocolGuid ## TO_START gEfiTcp4ProtocolGuid ## TO_START + gEfiTcp6ServiceBindingProtocolGuid ## TO_START + gEfiTcp6ProtocolGuid ## TO_START gEfiDns4ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES gEfiDns4ProtocolGuid ## SOMETIMES_CONSUMES + gEfiDns6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiDns6ProtocolGuid ## SOMETIMES_CONSUMES gEfiIp4Config2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiIp6ConfigProtocolGuid ## SOMETIMES_CONSUMES [UserExtensions.TianoCore."ExtraFiles"] HttpDxeExtra.uni \ No newline at end of file diff --git a/NetworkPkg/HttpDxe/HttpDxe.uni b/NetworkPkg/HttpDxe/HttpDxe.uni index f9c2ac812e..d6792dd41e 100644 Binary files a/NetworkPkg/HttpDxe/HttpDxe.uni and b/NetworkPkg/HttpDxe/HttpDxe.uni differ diff --git a/NetworkPkg/HttpDxe/HttpImpl.c b/NetworkPkg/HttpDxe/HttpImpl.c index f1e3e53411..c527da0872 100644 --- a/NetworkPkg/HttpDxe/HttpImpl.c +++ b/NetworkPkg/HttpDxe/HttpImpl.c @@ -51,6 +51,8 @@ EfiHttpGetModeData ( ) { HTTP_PROTOCOL *HttpInstance; + EFI_HTTPv4_ACCESS_POINT *Http4AccessPoint; + EFI_HTTPv6_ACCESS_POINT *Http6AccessPoint; if ((This == NULL) || (HttpConfigData == NULL)) { return EFI_INVALID_PARAMETER; @@ -58,24 +60,32 @@ EfiHttpGetModeData ( HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); ASSERT (HttpInstance != NULL); - + if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) { return EFI_NOT_STARTED; } - if (HttpConfigData->AccessPoint.IPv4Node == NULL) { - return EFI_INVALID_PARAMETER; - } - HttpConfigData->HttpVersion = HttpInstance->HttpVersion; HttpConfigData->TimeOutMillisec = HttpInstance->TimeOutMillisec; HttpConfigData->LocalAddressIsIPv6 = HttpInstance->LocalAddressIsIPv6; - CopyMem ( - HttpConfigData->AccessPoint.IPv4Node, - &HttpInstance->IPv4Node, - sizeof (HttpInstance->IPv4Node) + if (HttpInstance->LocalAddressIsIPv6) { + Http6AccessPoint = AllocateZeroPool (sizeof (EFI_HTTPv6_ACCESS_POINT)); + CopyMem ( + Http6AccessPoint, + &HttpInstance->Ipv6Node, + sizeof (HttpInstance->Ipv6Node) ); + HttpConfigData->AccessPoint.IPv6Node = Http6AccessPoint; + } else { + Http4AccessPoint = AllocateZeroPool (sizeof (EFI_HTTPv4_ACCESS_POINT)); + CopyMem ( + Http4AccessPoint, + &HttpInstance->IPv4Node, + sizeof (HttpInstance->IPv4Node) + ); + HttpConfigData->AccessPoint.IPv4Node = Http4AccessPoint; + } return EFI_SUCCESS; } @@ -120,8 +130,13 @@ EfiHttpConfigure ( { HTTP_PROTOCOL *HttpInstance; EFI_STATUS Status; - - if (This == NULL) { + + // + // Check input parameters. + // + if (This == NULL || + (HttpConfigData != NULL && ((HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) || + (!HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)))) { return EFI_INVALID_PARAMETER; } @@ -129,18 +144,7 @@ EfiHttpConfigure ( ASSERT (HttpInstance != NULL && HttpInstance->Service != NULL); if (HttpConfigData != NULL) { - // - // Check input parameters. - // - if (HttpConfigData->LocalAddressIsIPv6) { - if (HttpConfigData->AccessPoint.IPv6Node == NULL) { - return EFI_INVALID_PARAMETER; - } - } else { - if (HttpConfigData->AccessPoint.IPv4Node == NULL) { - return EFI_INVALID_PARAMETER; - } - } + // // Now configure this HTTP instance. // @@ -151,33 +155,38 @@ EfiHttpConfigure ( HttpInstance->HttpVersion = HttpConfigData->HttpVersion; HttpInstance->TimeOutMillisec = HttpConfigData->TimeOutMillisec; HttpInstance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6; - - if (HttpConfigData->LocalAddressIsIPv6) { - return EFI_UNSUPPORTED; + + if (HttpConfigData->LocalAddressIsIPv6) { + CopyMem ( + &HttpInstance->Ipv6Node, + HttpConfigData->AccessPoint.IPv6Node, + sizeof (HttpInstance->Ipv6Node) + ); } else { CopyMem ( &HttpInstance->IPv4Node, HttpConfigData->AccessPoint.IPv4Node, sizeof (HttpInstance->IPv4Node) ); - - HttpInstance->State = HTTP_STATE_HTTP_CONFIGED; - return EFI_SUCCESS; } + // + // Creat Tcp child + // + Status = HttpInitProtocol (HttpInstance, HttpInstance->LocalAddressIsIPv6); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->State = HTTP_STATE_HTTP_CONFIGED; + return EFI_SUCCESS; } else { - if (HttpInstance->LocalAddressIsIPv6) { - return EFI_UNSUPPORTED; - } else { - HttpCleanProtocol (HttpInstance); - Status = HttpInitProtocol (HttpInstance->Service, HttpInstance); - if (EFI_ERROR (Status)) { - return Status; - } - - HttpInstance->State = HTTP_STATE_UNCONFIGED; - return EFI_SUCCESS; - } + // + // Reset all the resources related to HttpInsance. + // + HttpCleanProtocol (HttpInstance); + HttpInstance->State = HTTP_STATE_UNCONFIGED; + return EFI_SUCCESS; } } @@ -264,10 +273,6 @@ EfiHttpRequest ( return EFI_NOT_STARTED; } - if (HttpInstance->LocalAddressIsIPv6) { - return EFI_UNSUPPORTED; - } - // // Check whether the token already existed. // @@ -291,7 +296,8 @@ EfiHttpRequest ( } FreePool (HttpInstance->Url); HttpInstance->Url = Url; - } + } + UnicodeStrToAsciiStr (Request->Url, Url); UrlParser = NULL; @@ -340,7 +346,7 @@ EfiHttpRequest ( Wrap->HttpToken = Token; Wrap->HttpInstance = HttpInstance; - Status = HttpCreateTcp4TxEvent (Wrap); + Status = HttpCreateTcpTxEvent (Wrap); if (EFI_ERROR (Status)) { goto Error1; } @@ -379,24 +385,35 @@ EfiHttpRequest ( if (Configure) { // - // Parse Url for IPv4 address, if failed, perform DNS resolution. + // Parse Url for IPv4 or IPv6 address, if failed, perform DNS resolution. // - Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr); + if (!HttpInstance->LocalAddressIsIPv6) { + Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr); + } else { + Status = NetLibAsciiStrToIp6 (HostName, &HttpInstance->RemoteIpv6Addr); + } + if (EFI_ERROR (Status)) { - HostNameStr = AllocateZeroPool ((AsciiStrLen (HostName) + 1) * sizeof (UINT16)); + HostNameStr = AllocateZeroPool ((AsciiStrLen (HostName) + 1) * sizeof (CHAR16)); if (HostNameStr == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error1; } - + AsciiStrToUnicodeStr (HostName, HostNameStr); - Status = HttpDns4 (HttpInstance, HostNameStr, &HttpInstance->RemoteAddr); + if (!HttpInstance->LocalAddressIsIPv6) { + Status = HttpDns4 (HttpInstance, HostNameStr, &HttpInstance->RemoteAddr); + } else { + Status = HttpDns6 (HttpInstance, HostNameStr, &HttpInstance->RemoteIpv6Addr); + } + FreePool (HostNameStr); if (EFI_ERROR (Status)) { goto Error1; } } + // // Save the RemotePort and RemoteHost. // @@ -410,7 +427,7 @@ EfiHttpRequest ( // // The request URL is different from previous calls to Request(), close existing TCP instance. // - ASSERT (HttpInstance->Tcp4 != NULL); + ASSERT (HttpInstance->Tcp4 != NULL &&HttpInstance->Tcp6 != NULL); HttpCloseConnection (HttpInstance); EfiHttpCancel (This, NULL); } @@ -429,25 +446,16 @@ EfiHttpRequest ( Wrap->TcpWrap.Method = Request->Method; if (Configure) { - // - // Configure TCP instance. - // - Status = HttpConfigureTcp4 (HttpInstance, Wrap); - if (EFI_ERROR (Status)) { - goto Error1; - } - // - // Connect TCP. - // - Status = HttpConnectTcp4 (HttpInstance); + Status = HttpInitTcp (HttpInstance, Wrap); if (EFI_ERROR (Status)) { goto Error2; } + } else { // // For the new HTTP token, create TX TCP token events. // - Status = HttpCreateTcp4TxEvent (Wrap); + Status = HttpCreateTcpTxEvent (Wrap); if (EFI_ERROR (Status)) { goto Error1; } @@ -488,7 +496,7 @@ EfiHttpRequest ( // // Transmit the request message. // - Status = HttpTransmitTcp4 ( + Status = HttpTransmitTcp ( HttpInstance, Wrap, (UINT8*) RequestStr, @@ -499,11 +507,11 @@ EfiHttpRequest ( } DispatchDpc (); - + if (HostName != NULL) { FreePool (HostName); } - + return EFI_SUCCESS; Error5: @@ -517,15 +525,19 @@ Error4: Error3: HttpCloseConnection (HttpInstance); - Error2: - HttpCloseTcp4ConnCloseEvent (HttpInstance); - if (NULL != Wrap->TcpWrap.TxToken.CompletionToken.Event) { - gBS->CloseEvent (Wrap->TcpWrap.TxToken.CompletionToken.Event); - Wrap->TcpWrap.TxToken.CompletionToken.Event = NULL; + HttpCloseTcpConnCloseEvent (HttpInstance); + if (NULL != Wrap->TcpWrap.Tx4Token.CompletionToken.Event) { + gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event); + Wrap->TcpWrap.Tx4Token.CompletionToken.Event = NULL; + } + if (NULL != Wrap->TcpWrap.Tx6Token.CompletionToken.Event) { + gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event); + Wrap->TcpWrap.Tx6Token.CompletionToken.Event = NULL; } Error1: + if (HostName != NULL) { FreePool (HostName); } @@ -541,7 +553,7 @@ Error1: } /** - Cancel a TxToken or RxToken. + Cancel a user's Token. @param[in] Map The HTTP instance's token queue. @param[in] Item Object container for one HTTP token and token's wrap. @@ -562,6 +574,7 @@ HttpCancelTokens ( EFI_HTTP_TOKEN *Token; HTTP_TOKEN_WRAP *Wrap; + HTTP_PROTOCOL *HttpInstance; Token = (EFI_HTTP_TOKEN *) Context; @@ -575,24 +588,41 @@ HttpCancelTokens ( Wrap = (HTTP_TOKEN_WRAP *) Item->Value; ASSERT (Wrap != NULL); + HttpInstance = Wrap->HttpInstance; // // Free resources. // NetMapRemoveItem (Map, Item, NULL); - if (Wrap->TcpWrap.TxToken.CompletionToken.Event != NULL) { - gBS->CloseEvent (Wrap->TcpWrap.TxToken.CompletionToken.Event); - } + if (!HttpInstance->LocalAddressIsIPv6) { + if (Wrap->TcpWrap.Tx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event); + } + + if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); + } + + if (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer); + } - if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) { - gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event); - } + } else { + if (Wrap->TcpWrap.Tx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event); + } + + if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); + } - if (Wrap->TcpWrap.RxToken.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { - FreePool (Wrap->TcpWrap.RxToken.Packet.RxData->FragmentTable[0].FragmentBuffer); + if (Wrap->TcpWrap.Rx6Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Wrap->TcpWrap.Rx6Token.Packet.RxData->FragmentTable[0].FragmentBuffer); + } } + FreePool (Wrap); // @@ -747,7 +777,7 @@ HttpBodyParserCallback ( Wrap->HttpInstance->NextMsg = Data; // - // Free TxToken since already received corrsponding HTTP response. + // Free Tx4Token or Tx6Token since already received corrsponding HTTP response. // FreePool (Wrap); @@ -761,7 +791,7 @@ HttpBodyParserCallback ( @retval EFI_SUCCESS Allocation succeeded. @retval EFI_OUT_OF_RESOURCES Failed to complete the opration due to lack of resources. - @retval EFI_NOT_READY Can't find a corresponding TxToken or + @retval EFI_NOT_READY Can't find a corresponding Tx4Token/Tx6Token or the EFI_HTTP_UTILITIES_PROTOCOL is not available. **/ @@ -772,12 +802,9 @@ HttpResponseWorker ( { EFI_STATUS Status; EFI_HTTP_MESSAGE *HttpMsg; - EFI_TCP4_IO_TOKEN *RxToken; - EFI_TCP4_PROTOCOL *Tcp4; CHAR8 *EndofHeader; CHAR8 *HttpHeaders; UINTN SizeofHeaders; - CHAR8 *Buffer; UINTN BufferSize; UINTN StatusCode; CHAR8 *Tmp; @@ -796,23 +823,21 @@ HttpResponseWorker ( HttpInstance = Wrap->HttpInstance; Token = Wrap->HttpToken; - HttpMsg = Token->Message; - Tcp4 = HttpInstance->Tcp4; - ASSERT (Tcp4 != NULL); - HttpMsg->Headers = NULL; - HttpHeaders = NULL; - SizeofHeaders = 0; - Buffer = NULL; - BufferSize = 0; - EndofHeader = NULL; + HttpInstance->EndofHeader = NULL; + HttpInstance->HttpHeaders = NULL; + HttpMsg->Headers = NULL; + HttpHeaders = NULL; + SizeofHeaders = 0; + BufferSize = 0; + EndofHeader = NULL; if (HttpMsg->Data.Response != NULL) { // // Need receive the HTTP headers, prepare buffer. // - Status = HttpCreateTcp4RxEventForHeader (HttpInstance); + Status = HttpCreateTcpRxEventForHeader (HttpInstance); if (EFI_ERROR (Status)) { goto Error; } @@ -843,70 +868,15 @@ HttpResponseWorker ( // Check whether we cached the whole HTTP headers. // EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); - } - - RxToken = &HttpInstance->RxToken; - RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); - if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto Error; - } - - // - // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL. - // - while (EndofHeader == NULL) { - HttpInstance->IsRxDone = FALSE; - RxToken->Packet.RxData->DataLength = DEF_BUF_LEN; - RxToken->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; - Status = Tcp4->Receive (Tcp4, RxToken); - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); - goto Error; - } - - while (!HttpInstance->IsRxDone) { - Tcp4->Poll (Tcp4); - } - - Status = RxToken->CompletionToken.Status; - if (EFI_ERROR (Status)) { - goto Error; - } - - // - // Append the response string. - // - BufferSize = SizeofHeaders + RxToken->Packet.RxData->FragmentTable[0].FragmentLength; - Buffer = AllocateZeroPool (BufferSize); - if (Buffer == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto Error; - } - - if (HttpHeaders != NULL) { - CopyMem (Buffer, HttpHeaders, SizeofHeaders); - FreePool (HttpHeaders); - } - - CopyMem ( - Buffer + SizeofHeaders, - RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer, - RxToken->Packet.RxData->FragmentTable[0].FragmentLength - ); - HttpHeaders = Buffer; - SizeofHeaders = BufferSize; + } - // - // Check whether we received end of HTTP headers. - // - EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); - }; + HttpInstance->EndofHeader = &EndofHeader; + HttpInstance->HttpHeaders = &HttpHeaders; - // - // Skip the CRLF after the HTTP headers. - // - EndofHeader = EndofHeader + AsciiStrLen (HTTP_END_OF_HDR_STR); + Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize); + if (EFI_ERROR (Status)) { + goto Error; + } // // Cache the part of body. @@ -927,9 +897,6 @@ HttpResponseWorker ( HttpInstance->CacheLen = BodyLen; } - FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer); - RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; - // // Search for Status Code. // @@ -997,7 +964,7 @@ HttpResponseWorker ( } // - // The first TxToken not transmitted yet, insert back and return error. + // The first Tx Token not transmitted yet, insert back and return error. // if (!ValueInItem->TcpWrap.IsTxDone) { goto Error2; @@ -1108,16 +1075,8 @@ HttpResponseWorker ( // // We still need receive more data when there is no cache data and MsgParser is not NULL; // - RxToken = &Wrap->TcpWrap.RxToken; - - RxToken->Packet.RxData->DataLength = (UINT32) HttpMsg->BodyLength; - RxToken->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32) HttpMsg->BodyLength; - RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = (VOID *) HttpMsg->Body; - - RxToken->CompletionToken.Status = EFI_NOT_READY; - Status = Tcp4->Receive (Tcp4, RxToken); + Status = HttpTcpReceiveBody (Wrap, HttpMsg); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); goto Error; } @@ -1130,18 +1089,7 @@ Exit: } Token->Status = Status; gBS->SignalEvent (Token->Event); - - if (Wrap != NULL) { - if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) { - gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event); - } - } - - if (HttpInstance->RxToken.CompletionToken.Event != NULL) { - gBS->CloseEvent (HttpInstance->RxToken.CompletionToken.Event); - HttpInstance->RxToken.CompletionToken.Event = NULL; - } - + HttpCloseTcpRxEvent (Wrap); FreePool (Wrap); return Status; @@ -1149,28 +1097,7 @@ Error2: NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem); Error: - if (Wrap != NULL) { - if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) { - gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event); - } - RxToken = &Wrap->TcpWrap.RxToken; - if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { - FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer); - RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; - } - FreePool (Wrap); - } - - if (HttpInstance->RxToken.CompletionToken.Event != NULL) { - gBS->CloseEvent (HttpInstance->RxToken.CompletionToken.Event); - HttpInstance->RxToken.CompletionToken.Event = NULL; - } - - RxToken = &HttpInstance->RxToken; - if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { - FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer); - RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; - } + HttpTcpTokenCleanup (Wrap); if (HttpHeaders != NULL) { FreePool (HttpHeaders); @@ -1268,10 +1195,6 @@ EfiHttpResponse ( return EFI_NOT_STARTED; } - if (HttpInstance->LocalAddressIsIPv6) { - return EFI_UNSUPPORTED; - } - // // Check whether the token already existed. // @@ -1287,7 +1210,7 @@ EfiHttpResponse ( Wrap->HttpInstance = HttpInstance; Wrap->HttpToken = Token; - Status = HttpCreateTcp4RxEvent (Wrap); + Status = HttpCreateTcpRxEvent (Wrap); if (EFI_ERROR (Status)) { goto Error; } @@ -1308,8 +1231,12 @@ EfiHttpResponse ( Error: if (Wrap != NULL) { - if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) { - gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event); + if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); + } + + if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); } FreePool (Wrap); } @@ -1343,8 +1270,8 @@ EfiHttpPoll ( IN EFI_HTTP_PROTOCOL *This ) { - HTTP_PROTOCOL *HttpInstance; EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; if (This == NULL) { return EFI_INVALID_PARAMETER; @@ -1353,17 +1280,18 @@ EfiHttpPoll ( HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); ASSERT (HttpInstance != NULL); - if (HttpInstance->LocalAddressIsIPv6) { - return EFI_UNSUPPORTED; - } - - if (HttpInstance->Tcp4 == NULL || HttpInstance->State != HTTP_STATE_TCP_CONNECTED) { + if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED || (HttpInstance->Tcp4 == NULL && + HttpInstance->Tcp6 == NULL)) { return EFI_NOT_STARTED; } - - Status = HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); - + + if (HttpInstance->LocalAddressIsIPv6) { + Status = HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); + } else { + Status = HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } + DispatchDpc (); - + return Status; } diff --git a/NetworkPkg/HttpDxe/HttpProto.c b/NetworkPkg/HttpDxe/HttpProto.c index 57ea207389..39837a3c82 100644 --- a/NetworkPkg/HttpDxe/HttpProto.c +++ b/NetworkPkg/HttpDxe/HttpProto.c @@ -37,7 +37,7 @@ HttpCommonNotify ( } /** - The notify function associated with TxToken for Tcp4->Transmit(). + The notify function associated with Tx4Token for Tcp4->Transmit() or Tx6Token for Tcp6->Transmit(). @param[in] Context The context. @@ -49,25 +49,46 @@ HttpTcpTransmitNotifyDpc ( ) { HTTP_TOKEN_WRAP *Wrap; + HTTP_PROTOCOL *HttpInstance; if (Context == NULL) { return ; } + + Wrap = (HTTP_TOKEN_WRAP *) Context; + HttpInstance = Wrap->HttpInstance; + + if (!HttpInstance->LocalAddressIsIPv6) { + Wrap->HttpToken->Status = Wrap->TcpWrap.Tx4Token.CompletionToken.Status; + gBS->SignalEvent (Wrap->HttpToken->Event); + + // + // Free resources. + // + if (Wrap->TcpWrap.Tx4Token.Packet.TxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Wrap->TcpWrap.Tx4Token.Packet.TxData->FragmentTable[0].FragmentBuffer); + } - Wrap = (HTTP_TOKEN_WRAP *) Context; - Wrap->HttpToken->Status = Wrap->TcpWrap.TxToken.CompletionToken.Status; - gBS->SignalEvent (Wrap->HttpToken->Event); + if (Wrap->TcpWrap.Tx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event); + } + + } else { + Wrap->HttpToken->Status = Wrap->TcpWrap.Tx6Token.CompletionToken.Status; + gBS->SignalEvent (Wrap->HttpToken->Event); + + // + // Free resources. + // + if (Wrap->TcpWrap.Tx6Token.Packet.TxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Wrap->TcpWrap.Tx6Token.Packet.TxData->FragmentTable[0].FragmentBuffer); + } - // - // Free resources. - // - if (Wrap->TcpWrap.TxToken.Packet.TxData->FragmentTable[0].FragmentBuffer != NULL) { - FreePool (Wrap->TcpWrap.TxToken.Packet.TxData->FragmentTable[0].FragmentBuffer); + if (Wrap->TcpWrap.Tx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event); + } } - if (Wrap->TcpWrap.TxToken.CompletionToken.Event != NULL) { - gBS->CloseEvent (Wrap->TcpWrap.TxToken.CompletionToken.Event); - } Wrap->TcpWrap.IsTxDone = TRUE; @@ -98,9 +119,8 @@ HttpTcpTransmitNotify ( QueueDpc (TPL_CALLBACK, HttpTcpTransmitNotifyDpc, Context); } - /** - The notify function associated with RxToken for Tcp4->Receive (). + The notify function associated with Rx4Token for Tcp4->Receive () or Rx6Token for Tcp6->Receive(). @param[in] Context The context. @@ -116,25 +136,41 @@ HttpTcpReceiveNotifyDpc ( UINTN Length; EFI_STATUS Status; HTTP_PROTOCOL *HttpInstance; + BOOLEAN UsingIpv6; if (Context == NULL) { return ; } Wrap = (HTTP_TOKEN_WRAP *) Context; - gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event); - if (EFI_ERROR (Wrap->TcpWrap.RxToken.CompletionToken.Status)) { - return ; - } - HttpInstance = Wrap->HttpInstance; + UsingIpv6 = HttpInstance->LocalAddressIsIPv6; + + if (UsingIpv6) { + gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); + + if (EFI_ERROR (Wrap->TcpWrap.Rx6Token.CompletionToken.Status)) { + return ; + } + + } else { + gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); + + if (EFI_ERROR (Wrap->TcpWrap.Rx4Token.CompletionToken.Status)) { + return ; + } + } // // Check whether we receive a complete HTTP message. // ASSERT (HttpInstance->MsgParser != NULL); + if (UsingIpv6) { + Length = (UINTN) Wrap->TcpWrap.Rx6Data.FragmentTable[0].FragmentLength; + } else { + Length = (UINTN) Wrap->TcpWrap.Rx4Data.FragmentTable[0].FragmentLength; + } - Length = (UINTN) Wrap->TcpWrap.RxData.FragmentTable[0].FragmentLength; Status = HttpParseMessageBody ( HttpInstance->MsgParser, Length, @@ -179,7 +215,12 @@ HttpTcpReceiveNotifyDpc ( Wrap->TcpWrap.IsRxDone = TRUE; - Wrap->HttpToken->Status = Wrap->TcpWrap.RxToken.CompletionToken.Status; + if (UsingIpv6) { + Wrap->HttpToken->Status = Wrap->TcpWrap.Rx6Token.CompletionToken.Status; + } else { + Wrap->HttpToken->Status = Wrap->TcpWrap.Rx4Token.CompletionToken.Status; + } + gBS->SignalEvent (Wrap->HttpToken->Event); @@ -211,9 +252,8 @@ HttpTcpReceiveNotify ( QueueDpc (TPL_CALLBACK, HttpTcpReceiveNotifyDpc, Context); } - /** - Create events for the TCP4 connection token and TCP4 close token. + Create events for the TCP connection token and TCP close token. @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. @@ -222,11 +262,13 @@ HttpTcpReceiveNotify ( **/ EFI_STATUS -HttpCreateTcp4ConnCloseEvent ( +HttpCreateTcpConnCloseEvent ( IN HTTP_PROTOCOL *HttpInstance ) { EFI_STATUS Status; + + if (!HttpInstance->LocalAddressIsIPv6) { // // Create events for variuos asynchronous operations. // @@ -234,66 +276,109 @@ HttpCreateTcp4ConnCloseEvent ( EVT_NOTIFY_SIGNAL, TPL_NOTIFY, HttpCommonNotify, - &HttpInstance->IsConnDone, - &HttpInstance->ConnToken.CompletionToken.Event + &HttpInstance->IsTcp4ConnDone, + &HttpInstance->Tcp4ConnToken.CompletionToken.Event ); if (EFI_ERROR (Status)) { goto ERROR; } // - // Initialize CloseToken + // Initialize Tcp4CloseToken + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsTcp4CloseDone, + &HttpInstance->Tcp4CloseToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + } else { + // + // Create events for variuos asynchronous operations. // Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, TPL_NOTIFY, HttpCommonNotify, - &HttpInstance->IsCloseDone, - &HttpInstance->CloseToken.CompletionToken.Event + &HttpInstance->IsTcp6ConnDone, + &HttpInstance->Tcp6ConnToken.CompletionToken.Event ); if (EFI_ERROR (Status)) { goto ERROR; } - + // + // Initialize Tcp6CloseToken + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsTcp6CloseDone, + &HttpInstance->Tcp6CloseToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + } + return EFI_SUCCESS; ERROR: // // Error handling // - HttpCloseTcp4ConnCloseEvent (HttpInstance); + HttpCloseTcpConnCloseEvent (HttpInstance); return Status; } /** - Close events in the TCP4 connection token and TCP4 close token. + Close events in the TCP connection token and TCP close token. @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. **/ VOID -HttpCloseTcp4ConnCloseEvent ( +HttpCloseTcpConnCloseEvent ( IN HTTP_PROTOCOL *HttpInstance ) { ASSERT (HttpInstance != NULL); - if (NULL != HttpInstance->ConnToken.CompletionToken.Event) { - gBS->CloseEvent (HttpInstance->ConnToken.CompletionToken.Event); - HttpInstance->ConnToken.CompletionToken.Event = NULL; - } + if (HttpInstance->LocalAddressIsIPv6) { + if (NULL != HttpInstance->Tcp6ConnToken.CompletionToken.Event) { + gBS->CloseEvent (HttpInstance->Tcp6ConnToken.CompletionToken.Event); + HttpInstance->Tcp6ConnToken.CompletionToken.Event = NULL; + } - if (NULL != HttpInstance->CloseToken.CompletionToken.Event) { - gBS->CloseEvent(HttpInstance->CloseToken.CompletionToken.Event); - HttpInstance->CloseToken.CompletionToken.Event = NULL; - } + if (NULL != HttpInstance->Tcp6CloseToken.CompletionToken.Event) { + gBS->CloseEvent(HttpInstance->Tcp6CloseToken.CompletionToken.Event); + HttpInstance->Tcp6CloseToken.CompletionToken.Event = NULL; + } + + } else { + if (NULL != HttpInstance->Tcp4ConnToken.CompletionToken.Event) { + gBS->CloseEvent (HttpInstance->Tcp4ConnToken.CompletionToken.Event); + HttpInstance->Tcp4ConnToken.CompletionToken.Event = NULL; + } + + if (NULL != HttpInstance->Tcp4CloseToken.CompletionToken.Event) { + gBS->CloseEvent(HttpInstance->Tcp4CloseToken.CompletionToken.Event); + HttpInstance->Tcp4CloseToken.CompletionToken.Event = NULL; + } + } + } /** - Create event for the TCP4 transmit token. + Create event for the TCP transmit token. @param[in] Wrap Point to HTTP token's wrap data. @@ -302,37 +387,61 @@ HttpCloseTcp4ConnCloseEvent ( **/ EFI_STATUS -HttpCreateTcp4TxEvent ( +HttpCreateTcpTxEvent ( IN HTTP_TOKEN_WRAP *Wrap ) { EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; HTTP_TCP_TOKEN_WRAP *TcpWrap; + HttpInstance = Wrap->HttpInstance; TcpWrap = &Wrap->TcpWrap; - Status = gBS->CreateEvent ( - EVT_NOTIFY_SIGNAL, - TPL_NOTIFY, - HttpTcpTransmitNotify, - Wrap, - &TcpWrap->TxToken.CompletionToken.Event - ); - if (EFI_ERROR (Status)) { - return Status; - } + if (!HttpInstance->LocalAddressIsIPv6) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpTransmitNotify, + Wrap, + &TcpWrap->Tx4Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->Tx4Data.Push = TRUE; + TcpWrap->Tx4Data.Urgent = FALSE; + TcpWrap->Tx4Data.FragmentCount = 1; + TcpWrap->Tx4Token.Packet.TxData = &Wrap->TcpWrap.Tx4Data; + TcpWrap->Tx4Token.CompletionToken.Status = EFI_NOT_READY; - TcpWrap->TxData.Push = TRUE; - TcpWrap->TxData.Urgent = FALSE; - TcpWrap->TxData.FragmentCount = 1; - TcpWrap->TxToken.Packet.TxData = &Wrap->TcpWrap.TxData; - TcpWrap->TxToken.CompletionToken.Status = EFI_NOT_READY; + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpTransmitNotify, + Wrap, + &TcpWrap->Tx6Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + TcpWrap->Tx6Data.Push = TRUE; + TcpWrap->Tx6Data.Urgent = FALSE; + TcpWrap->Tx6Data.FragmentCount = 1; + TcpWrap->Tx6Token.Packet.TxData = &Wrap->TcpWrap.Tx6Data; + TcpWrap->Tx6Token.CompletionToken.Status =EFI_NOT_READY; + + + } + return EFI_SUCCESS; } /** - Create event for the TCP4 receive token which is used to receive HTTP header. + Create event for the TCP receive token which is used to receive HTTP header. @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. @@ -341,33 +450,52 @@ HttpCreateTcp4TxEvent ( **/ EFI_STATUS -HttpCreateTcp4RxEventForHeader ( +HttpCreateTcpRxEventForHeader ( IN HTTP_PROTOCOL *HttpInstance ) { EFI_STATUS Status; + if (!HttpInstance->LocalAddressIsIPv6) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsRxDone, + &HttpInstance->Rx4Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->Rx4Data.FragmentCount = 1; + HttpInstance->Rx4Token.Packet.RxData = &HttpInstance->Rx4Data; + HttpInstance->Rx4Token.CompletionToken.Status = EFI_NOT_READY; - Status = gBS->CreateEvent ( - EVT_NOTIFY_SIGNAL, - TPL_NOTIFY, - HttpCommonNotify, - &HttpInstance->IsRxDone, - &HttpInstance->RxToken.CompletionToken.Event - ); - if (EFI_ERROR (Status)) { - return Status; + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsRxDone, + &HttpInstance->Rx6Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->Rx6Data.FragmentCount =1; + HttpInstance->Rx6Token.Packet.RxData = &HttpInstance->Rx6Data; + HttpInstance->Rx6Token.CompletionToken.Status = EFI_NOT_READY; + } - HttpInstance->RxData.FragmentCount = 1; - HttpInstance->RxToken.Packet.RxData = &HttpInstance->RxData; - HttpInstance->RxToken.CompletionToken.Status = EFI_NOT_READY; return EFI_SUCCESS; } /** - Create event for the TCP4 receive token which is used to receive HTTP body. + Create event for the TCP receive token which is used to receive HTTP body. @param[in] Wrap Point to HTTP token's wrap data. @@ -376,38 +504,101 @@ HttpCreateTcp4RxEventForHeader ( **/ EFI_STATUS -HttpCreateTcp4RxEvent ( +HttpCreateTcpRxEvent ( IN HTTP_TOKEN_WRAP *Wrap ) { EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; HTTP_TCP_TOKEN_WRAP *TcpWrap; + HttpInstance = Wrap->HttpInstance; TcpWrap = &Wrap->TcpWrap; + if (!HttpInstance->LocalAddressIsIPv6) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpReceiveNotify, + Wrap, + &TcpWrap->Rx4Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->Rx4Data.FragmentCount = 1; + TcpWrap->Rx4Token.Packet.RxData = &Wrap->TcpWrap.Rx4Data; + TcpWrap->Rx4Token.CompletionToken.Status = EFI_NOT_READY; - Status = gBS->CreateEvent ( - EVT_NOTIFY_SIGNAL, - TPL_NOTIFY, - HttpTcpReceiveNotify, - Wrap, - &TcpWrap->RxToken.CompletionToken.Event - ); - if (EFI_ERROR (Status)) { - return Status; + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpReceiveNotify, + Wrap, + &TcpWrap->Rx6Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->Rx6Data.FragmentCount = 1; + TcpWrap->Rx6Token.Packet.RxData = &Wrap->TcpWrap.Rx6Data; + TcpWrap->Rx6Token.CompletionToken.Status = EFI_NOT_READY; } + + return EFI_SUCCESS; +} - TcpWrap->RxData.FragmentCount = 1; - TcpWrap->RxToken.Packet.RxData = &Wrap->TcpWrap.RxData; - TcpWrap->RxToken.CompletionToken.Status = EFI_NOT_READY; +/** + Close Events for Tcp Receive Tokens for HTTP body and HTTP header. - return EFI_SUCCESS; + @param[in] Wrap Pointer to HTTP token's wrap data. + +**/ +VOID +HttpCloseTcpRxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + HTTP_PROTOCOL *HttpInstance; + EFI_TCP4_IO_TOKEN *Rx4Token; + EFI_TCP6_IO_TOKEN *Rx6Token; + + HttpInstance = Wrap->HttpInstance; + Rx4Token = NULL; + Rx6Token = NULL; + + if (HttpInstance->LocalAddressIsIPv6) { + if (Wrap != NULL) { + if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); + } + } + + if (HttpInstance->Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (HttpInstance->Rx6Token.CompletionToken.Event); + HttpInstance->Rx6Token.CompletionToken.Event = NULL; + } + } else { + if (Wrap != NULL) { + if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); + } + } + + if (HttpInstance->Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (HttpInstance->Rx4Token.CompletionToken.Event); + HttpInstance->Rx4Token.CompletionToken.Event = NULL; + } + } } /** Intiialize the HTTP_PROTOCOL structure to the unconfigured state. - @param[in] HttpSb The HTTP service private instance. @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] IpVersion Indicate us TCP4 protocol or TCP6 protocol. @retval EFI_SUCCESS HTTP_PROTOCOL structure is initialized successfully. @retval Others Other error as indicated. @@ -415,95 +606,198 @@ HttpCreateTcp4RxEvent ( **/ EFI_STATUS HttpInitProtocol ( - IN HTTP_SERVICE *HttpSb, - IN OUT HTTP_PROTOCOL *HttpInstance + IN OUT HTTP_PROTOCOL *HttpInstance, + IN BOOLEAN IpVersion ) { EFI_STATUS Status; VOID *Interface; + BOOLEAN UsingIpv6; + + ASSERT (HttpInstance != NULL); + UsingIpv6 = IpVersion; + + if (!UsingIpv6) { + // + // Create TCP4 child. + // + Status = NetLibCreateServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->ImageHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + &HttpInstance->Tcp4ChildHandle + ); - ASSERT ((HttpSb != NULL) && (HttpInstance != NULL)); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } - HttpInstance->Signature = HTTP_PROTOCOL_SIGNATURE; - CopyMem (&HttpInstance->Http, &mEfiHttpTemplate, sizeof (HttpInstance->Http)); - HttpInstance->Service = HttpSb; + Status = gBS->OpenProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->ImageHandle, + HttpInstance->Service->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } - // - // Create TCP child. - // - Status = NetLibCreateServiceChild ( - HttpInstance->Service->ControllerHandle, - HttpInstance->Service->ImageHandle, - &gEfiTcp4ServiceBindingProtocolGuid, - &HttpInstance->TcpChildHandle - ); + Status = gBS->OpenProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &HttpInstance->Tcp4, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } - if (EFI_ERROR (Status)) { - goto ON_ERROR; - } + Status = gBS->OpenProtocol ( + HttpInstance->Service->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + } else { + // + // Create TCP6 Child. + // + Status = NetLibCreateServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->ImageHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + &HttpInstance->Tcp6ChildHandle + ); - Status = gBS->OpenProtocol ( - HttpInstance->TcpChildHandle, - &gEfiTcp4ProtocolGuid, - (VOID **) &Interface, - HttpInstance->Service->ImageHandle, - HttpInstance->Service->ControllerHandle, - EFI_OPEN_PROTOCOL_BY_DRIVER - ); - - if (EFI_ERROR (Status)) { - goto ON_ERROR; - } + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } - Status = gBS->OpenProtocol ( - HttpInstance->TcpChildHandle, - &gEfiTcp4ProtocolGuid, - (VOID **) &HttpInstance->Tcp4, - HttpInstance->Service->ImageHandle, - HttpInstance->Handle, - EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER - ); - if (EFI_ERROR(Status)) { - goto ON_ERROR; - } + Status = gBS->OpenProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->ImageHandle, + HttpInstance->Service->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + (VOID **) &HttpInstance->Tcp6, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Service->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + } + HttpInstance->Url = AllocateZeroPool (HTTP_URL_BUFFER_LEN); if (HttpInstance->Url == NULL) { Status = EFI_OUT_OF_RESOURCES; goto ON_ERROR; } - NetMapInit (&HttpInstance->TxTokens); - NetMapInit (&HttpInstance->RxTokens); - return EFI_SUCCESS; ON_ERROR: - if (HttpInstance->TcpChildHandle != NULL) { + if (HttpInstance->Tcp4ChildHandle != NULL) { gBS->CloseProtocol ( - HttpInstance->TcpChildHandle, + HttpInstance->Tcp4ChildHandle, &gEfiTcp4ProtocolGuid, HttpInstance->Service->ImageHandle, HttpInstance->Service->ControllerHandle ); gBS->CloseProtocol ( - HttpInstance->TcpChildHandle, + HttpInstance->Tcp4ChildHandle, &gEfiTcp4ProtocolGuid, HttpInstance->Service->ImageHandle, HttpInstance->Handle - ); + ); NetLibDestroyServiceChild ( HttpInstance->Service->ControllerHandle, HttpInstance->Service->ImageHandle, &gEfiTcp4ServiceBindingProtocolGuid, - HttpInstance->TcpChildHandle + HttpInstance->Tcp4ChildHandle ); } + + if (HttpInstance->Service->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + } + + if (HttpInstance->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Service->ControllerHandle + ); - return Status; + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->ImageHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + HttpInstance->Tcp6ChildHandle + ); + } + + if (HttpInstance->Service->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + } + + return EFI_UNSUPPORTED; } @@ -520,7 +814,7 @@ HttpCleanProtocol ( { HttpCloseConnection (HttpInstance); - HttpCloseTcp4ConnCloseEvent (HttpInstance); + HttpCloseTcpConnCloseEvent (HttpInstance); if (HttpInstance->CacheBody != NULL) { FreePool (HttpInstance->CacheBody); @@ -537,25 +831,25 @@ HttpCleanProtocol ( HttpFreeMsgParser (HttpInstance->MsgParser); HttpInstance->MsgParser = NULL; } - + if (HttpInstance->Url != NULL) { FreePool (HttpInstance->Url); HttpInstance->Url = NULL; } - + NetMapClean (&HttpInstance->TxTokens); NetMapClean (&HttpInstance->RxTokens); - if (HttpInstance->TcpChildHandle != NULL) { + if (HttpInstance->Tcp4ChildHandle != NULL) { gBS->CloseProtocol ( - HttpInstance->TcpChildHandle, + HttpInstance->Tcp4ChildHandle, &gEfiTcp4ProtocolGuid, HttpInstance->Service->ImageHandle, HttpInstance->Service->ControllerHandle ); gBS->CloseProtocol ( - HttpInstance->TcpChildHandle, + HttpInstance->Tcp4ChildHandle, &gEfiTcp4ProtocolGuid, HttpInstance->Service->ImageHandle, HttpInstance->Handle @@ -565,9 +859,51 @@ HttpCleanProtocol ( HttpInstance->Service->ControllerHandle, HttpInstance->Service->ImageHandle, &gEfiTcp4ServiceBindingProtocolGuid, - HttpInstance->TcpChildHandle + HttpInstance->Tcp4ChildHandle ); } + + if (HttpInstance->Service->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + } + + if (HttpInstance->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Service->ControllerHandle + ); + + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->ImageHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + HttpInstance->Tcp6ChildHandle + ); + } + + if (HttpInstance->Service->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + } + } /** @@ -586,27 +922,40 @@ HttpCreateConnection ( { EFI_STATUS Status; - // - // Create events for variuos asynchronous operations. - // - HttpInstance->IsConnDone = FALSE; - // // Connect to Http server // - HttpInstance->ConnToken.CompletionToken.Status = EFI_NOT_READY; - Status = HttpInstance->Tcp4->Connect (HttpInstance->Tcp4, &HttpInstance->ConnToken); - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "HttpCreateConnection: Tcp4->Connect() = %r\n", Status)); - return Status; - } - - while (!HttpInstance->IsConnDone) { - HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); - } + if (!HttpInstance->LocalAddressIsIPv6) { + HttpInstance->IsTcp4ConnDone = FALSE; + HttpInstance->Tcp4ConnToken.CompletionToken.Status = EFI_NOT_READY; + Status = HttpInstance->Tcp4->Connect (HttpInstance->Tcp4, &HttpInstance->Tcp4ConnToken); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpCreateConnection: Tcp4->Connect() = %r\n", Status)); + return Status; + } + + while (!HttpInstance->IsTcp4ConnDone) { + HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } + + Status = HttpInstance->Tcp4ConnToken.CompletionToken.Status; + + } else { + HttpInstance->IsTcp6ConnDone = FALSE; + HttpInstance->Tcp6ConnToken.CompletionToken.Status = EFI_NOT_READY; + Status = HttpInstance->Tcp6->Connect (HttpInstance->Tcp6, &HttpInstance->Tcp6ConnToken); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpCreateConnection: Tcp6->Connect() = %r\n", Status)); + return Status; + } - Status = HttpInstance->ConnToken.CompletionToken.Status; + while(!HttpInstance->IsTcp6ConnDone) { + HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); + } + Status = HttpInstance->Tcp6ConnToken.CompletionToken.Status; + } + if (!EFI_ERROR (Status)) { HttpInstance->State = HTTP_STATE_TCP_CONNECTED; } @@ -631,17 +980,32 @@ HttpCloseConnection ( EFI_STATUS Status; if (HttpInstance->State == HTTP_STATE_TCP_CONNECTED) { - HttpInstance->CloseToken.AbortOnClose = TRUE; - HttpInstance->IsCloseDone = FALSE; - - Status = HttpInstance->Tcp4->Close (HttpInstance->Tcp4, &HttpInstance->CloseToken); - if (EFI_ERROR (Status)) { - return Status; - } - while (!HttpInstance->IsCloseDone) { - HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + if (HttpInstance->LocalAddressIsIPv6) { + HttpInstance->Tcp6CloseToken.AbortOnClose = TRUE; + HttpInstance->IsTcp6CloseDone = FALSE; + Status = HttpInstance->Tcp6->Close (HttpInstance->Tcp6, &HttpInstance->Tcp6CloseToken); + if (EFI_ERROR (Status)) { + return Status; + } + + while (!HttpInstance->IsTcp6CloseDone) { + HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); + } + + } else { + HttpInstance->Tcp4CloseToken.AbortOnClose = TRUE; + HttpInstance->IsTcp4CloseDone = FALSE; + Status = HttpInstance->Tcp4->Close (HttpInstance->Tcp4, &HttpInstance->Tcp4CloseToken); + if (EFI_ERROR (Status)) { + return Status; + } + + while (!HttpInstance->IsTcp4CloseDone) { + HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } } + } HttpInstance->State = HTTP_STATE_TCP_CLOSED; @@ -710,12 +1074,82 @@ HttpConfigureTcp4 ( return Status; } - Status = HttpCreateTcp4ConnCloseEvent (HttpInstance); + Status = HttpCreateTcpConnCloseEvent (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = HttpCreateTcpTxEvent (Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->State = HTTP_STATE_TCP_CONFIGED; + + return EFI_SUCCESS; +} + +/** + Configure TCP6 protocol child. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + + @retval EFI_SUCCESS The TCP6 protocol child is configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConfigureTcp6 ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + EFI_TCP6_CONFIG_DATA *Tcp6CfgData; + EFI_TCP6_ACCESS_POINT *Tcp6Ap; + EFI_TCP6_OPTION *Tcp6Option; + + ASSERT (HttpInstance != NULL); + + Tcp6CfgData = &HttpInstance->Tcp6CfgData; + ZeroMem (Tcp6CfgData, sizeof (EFI_TCP6_CONFIG_DATA)); + + Tcp6CfgData->TrafficClass = 0; + Tcp6CfgData->HopLimit = 255; + Tcp6CfgData->ControlOption = &HttpInstance->Tcp6Option; + + Tcp6Ap = &Tcp6CfgData->AccessPoint; + Tcp6Ap->ActiveFlag = TRUE; + Tcp6Ap->StationPort = HttpInstance->Ipv6Node.LocalPort; + Tcp6Ap->RemotePort = HttpInstance->RemotePort; + IP6_COPY_ADDRESS (&Tcp6Ap->StationAddress, &HttpInstance->Ipv6Node.LocalAddress); + IP6_COPY_ADDRESS (&Tcp6Ap->RemoteAddress , &HttpInstance->RemoteIpv6Addr); + + Tcp6Option = Tcp6CfgData->ControlOption; + Tcp6Option->ReceiveBufferSize = HTTP_BUFFER_SIZE_DEAULT; + Tcp6Option->SendBufferSize = HTTP_BUFFER_SIZE_DEAULT; + Tcp6Option->MaxSynBackLog = HTTP_MAX_SYN_BACK_LOG; + Tcp6Option->ConnectionTimeout = HTTP_CONNECTION_TIMEOUT; + Tcp6Option->DataRetries = HTTP_DATA_RETRIES; + Tcp6Option->FinTimeout = HTTP_FIN_TIMEOUT; + Tcp6Option->KeepAliveProbes = HTTP_KEEP_ALIVE_PROBES; + Tcp6Option->KeepAliveTime = HTTP_KEEP_ALIVE_TIME; + Tcp6Option->KeepAliveInterval = HTTP_KEEP_ALIVE_INTERVAL; + Tcp6Option->EnableNagle = TRUE; + + Status = HttpInstance->Tcp6->Configure (HttpInstance->Tcp6, Tcp6CfgData); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpConfigureTcp6 - %r\n", Status)); + return Status; + } + + Status = HttpCreateTcpConnCloseEvent (HttpInstance); if (EFI_ERROR (Status)) { return Status; } - Status = HttpCreateTcp4TxEvent (Wrap); + Status = HttpCreateTcpTxEvent (Wrap); if (EFI_ERROR (Status)) { return Status; } @@ -723,10 +1157,11 @@ HttpConfigureTcp4 ( HttpInstance->State = HTTP_STATE_TCP_CONFIGED; return EFI_SUCCESS; + } /** - Check existing TCP connection, if in error state, receover TCP4 connection. + Check existing TCP connection, if in error state, recover TCP4 connection. @param[in] HttpInstance The HTTP instance private data. @@ -769,7 +1204,105 @@ HttpConnectTcp4 ( } /** - Send the HTTP message through TCP4. + Check existing TCP connection, if in error state, recover TCP6 connection. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval EFI_NOT_READY TCP6 protocol child is not created or configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConnectTcp6 ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + EFI_TCP6_CONNECTION_STATE Tcp6State; + + if (HttpInstance->State != HTTP_STATE_TCP_CONFIGED || HttpInstance->Tcp6 == NULL) { + return EFI_NOT_READY; + } + + Status = HttpInstance->Tcp6->GetModeData ( + HttpInstance->Tcp6, + &Tcp6State, + NULL, + NULL, + NULL, + NULL + ); + + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "Tcp6 GetModeData fail - %x\n", Status)); + return Status; + } + + if (Tcp6State > Tcp6StateEstablished) { + HttpCloseConnection (HttpInstance); + } + + return HttpCreateConnection (HttpInstance); +} + +/** + Initialize TCP related data. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + + @retval EFI_SUCCESS The initialization of TCP instance is done. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpInitTcp ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + ASSERT (HttpInstance != NULL); + + if (!HttpInstance->LocalAddressIsIPv6) { + // + // Configure TCP instance. + // + Status = HttpConfigureTcp4 (HttpInstance, Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Connect TCP. + // + Status = HttpConnectTcp4 (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + // + // Configure TCP instance. + // + Status = HttpConfigureTcp6 (HttpInstance, Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Connect TCP. + // + Status = HttpConnectTcp6 (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; + +} + +/** + Send the HTTP message through TCP4 or TCP6. @param[in] HttpInstance The HTTP instance private data. @param[in] Wrap The HTTP token's wrap data. @@ -781,7 +1314,7 @@ HttpConnectTcp4 ( **/ EFI_STATUS -HttpTransmitTcp4 ( +HttpTransmitTcp ( IN HTTP_PROTOCOL *HttpInstance, IN HTTP_TOKEN_WRAP *Wrap, IN UINT8 *TxString, @@ -789,23 +1322,44 @@ HttpTransmitTcp4 ( ) { EFI_STATUS Status; - EFI_TCP4_IO_TOKEN *TxToken; + EFI_TCP4_IO_TOKEN *Tx4Token; EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP6_IO_TOKEN *Tx6Token; + EFI_TCP6_PROTOCOL *Tcp6; - Tcp4 = HttpInstance->Tcp4; - TxToken = &Wrap->TcpWrap.TxToken; + if (!HttpInstance->LocalAddressIsIPv6) { + Tcp4 = HttpInstance->Tcp4; + Tx4Token = &Wrap->TcpWrap.Tx4Token; + + Tx4Token->Packet.TxData->DataLength = (UINT32) TxStringLen; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentLength = (UINT32) TxStringLen; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TxString; + Tx4Token->CompletionToken.Status = EFI_NOT_READY; + + Wrap->TcpWrap.IsTxDone = FALSE; + Status = Tcp4->Transmit (Tcp4, Tx4Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Transmit failed: %r\n", Status)); + return Status; + } - TxToken->Packet.TxData->DataLength = (UINT32) TxStringLen; - TxToken->Packet.TxData->FragmentTable[0].FragmentLength = (UINT32) TxStringLen; - TxToken->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TxString; - TxToken->CompletionToken.Status = EFI_NOT_READY; + } else { + Tcp6 = HttpInstance->Tcp6; + Tx6Token = &Wrap->TcpWrap.Tx6Token; - Wrap->TcpWrap.IsTxDone = FALSE; - Status = Tcp4->Transmit (Tcp4, TxToken); - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "Transmit failed: %r\n", Status)); - return Status; + Tx6Token->Packet.TxData->DataLength = (UINT32) TxStringLen; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentLength = (UINT32) TxStringLen; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TxString; + Tx6Token->CompletionToken.Status = EFI_NOT_READY; + + Wrap->TcpWrap.IsTxDone = FALSE; + Status = Tcp6->Transmit (Tcp6, Tx6Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Transmit failed: %r\n", Status)); + return Status; + } } + return Status; } @@ -913,7 +1467,7 @@ HttpMappingToStatusCode ( /** Check whether the user's token or event has already - been enqueue on HTTP TxToken or RxToken list. + been enqueue on HTTP Tx or Rx Token list. @param[in] Map The container of either user's transmit or receive token. @@ -947,9 +1501,9 @@ HttpTokenExist ( } /** - Check whether the HTTP message associated with TxToken is already sent out. + Check whether the HTTP message associated with Tx4Token or Tx6Token is already sent out. - @param[in] Map The container of TxToken. + @param[in] Map The container of Tx4Token or Tx6Token. @param[in] Item Current item to check against. @param[in] Context The Token to check againist. @@ -979,7 +1533,7 @@ HttpTcpNotReady ( /** Transmit the HTTP mssage by processing the associated HTTP token. - @param[in] Map The container of TxToken. + @param[in] Map The container of Tx4Token or Tx6Token. @param[in] Item Current item to check against. @param[in] Context The Token to check againist. @@ -1032,7 +1586,7 @@ HttpTcpTransmit ( // // Transmit the request message. // - Status = HttpTransmitTcp4 ( + Status = HttpTransmitTcp ( ValueInItem->HttpInstance, ValueInItem, (UINT8*) RequestStr, @@ -1045,7 +1599,7 @@ HttpTcpTransmit ( /** Receive the HTTP response by processing the associated HTTP token. - @param[in] Map The container of RxToken. + @param[in] Map The container of Rx4Token or Rx6Token. @param[in] Item Current item to check against. @param[in] Context The Token to check againist. @@ -1068,6 +1622,319 @@ HttpTcpReceive ( return HttpResponseWorker ((HTTP_TOKEN_WRAP *) Item->Value); } +/** + Receive the HTTP header by processing the associated HTTP token. + + @param[in] HttpInstance The HTTP instance private data. + @param[in, out] SizeofHeaders The HTTP header length. + @param[in, out] BufferSize The size of buffer to cacahe the header message. + + @retval EFI_SUCCESS The HTTP header is received. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpTcpReceiveHeader ( + IN HTTP_PROTOCOL *HttpInstance, + IN OUT UINTN *SizeofHeaders, + IN OUT UINTN *BufferSize + ) +{ + EFI_STATUS Status; + EFI_TCP4_IO_TOKEN *Rx4Token; + EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP6_IO_TOKEN *Rx6Token; + EFI_TCP6_PROTOCOL *Tcp6; + CHAR8 **EndofHeader; + CHAR8 **HttpHeaders; + CHAR8 *Buffer; + + ASSERT (HttpInstance != NULL); + + EndofHeader = HttpInstance->EndofHeader; + HttpHeaders = HttpInstance->HttpHeaders; + Tcp4 = HttpInstance->Tcp4; + Tcp6 = HttpInstance->Tcp6; + Buffer = NULL; + Rx4Token = NULL; + Rx6Token = NULL; + + if (HttpInstance->LocalAddressIsIPv6) { + ASSERT (Tcp6 != NULL); + } else { + ASSERT (Tcp4 != NULL); + } + + if (!HttpInstance->LocalAddressIsIPv6) { + Rx4Token = &HttpInstance->Rx4Token; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); + if (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + // + // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL. + // + while (*EndofHeader == NULL) { + HttpInstance->IsRxDone = FALSE; + Rx4Token->Packet.RxData->DataLength = DEF_BUF_LEN; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; + Status = Tcp4->Receive (Tcp4, Rx4Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); + return Status; + } + + while (!HttpInstance->IsRxDone) { + Tcp4->Poll (Tcp4); + } + + Status = Rx4Token->CompletionToken.Status; + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Append the response string. + // + *BufferSize = (*SizeofHeaders) + Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength; + Buffer = AllocateZeroPool (*BufferSize); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + if (*HttpHeaders != NULL) { + CopyMem (Buffer, *HttpHeaders, (*SizeofHeaders)); + FreePool (*HttpHeaders); + } + + CopyMem ( + Buffer + (*SizeofHeaders), + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer, + Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength + ); + *HttpHeaders = Buffer; + *SizeofHeaders = *BufferSize; + + // + // Check whether we received end of HTTP headers. + // + *EndofHeader = AsciiStrStr (*HttpHeaders, HTTP_END_OF_HDR_STR); + } + FreePool (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + + } else { + Rx6Token = &HttpInstance->Rx6Token; + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); + if (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + // + // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL. + // + while (*EndofHeader == NULL) { + HttpInstance->IsRxDone = FALSE; + Rx6Token->Packet.RxData->DataLength = DEF_BUF_LEN; + Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; + Status = Tcp6->Receive (Tcp6, Rx6Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status)); + return Status; + } + + while (!HttpInstance->IsRxDone) { + Tcp6->Poll (Tcp6); + } + + Status = Rx6Token->CompletionToken.Status; + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Append the response string. + // + *BufferSize = (*SizeofHeaders) + Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength; + Buffer = AllocateZeroPool (*BufferSize); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + if (*HttpHeaders != NULL) { + CopyMem (Buffer, *HttpHeaders, (*SizeofHeaders)); + FreePool (*HttpHeaders); + } + + CopyMem ( + Buffer + (*SizeofHeaders), + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer, + Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength + ); + *HttpHeaders = Buffer; + *SizeofHeaders = *BufferSize; + + // + // Check whether we received end of HTTP headers. + // + *EndofHeader = AsciiStrStr (*HttpHeaders, HTTP_END_OF_HDR_STR); + + } + FreePool (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + + // + // Skip the CRLF after the HTTP headers. + // + *EndofHeader = *EndofHeader + AsciiStrLen (HTTP_END_OF_HDR_STR); + + return EFI_SUCCESS; +} + +/** + Receive the HTTP body by processing the associated HTTP token. + + @param[in] Wrap The HTTP token's wrap data. + @param[in] HttpMsg The HTTP message data. + + @retval EFI_SUCCESS The HTTP body is received. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpTcpReceiveBody ( + IN HTTP_TOKEN_WRAP *Wrap, + IN EFI_HTTP_MESSAGE *HttpMsg + ) +{ + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + EFI_TCP6_PROTOCOL *Tcp6; + EFI_TCP6_IO_TOKEN *Rx6Token; + EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP4_IO_TOKEN *Rx4Token; + + HttpInstance = Wrap->HttpInstance; + Tcp4 = HttpInstance->Tcp4; + Tcp6 = HttpInstance->Tcp6; + Rx4Token = NULL; + Rx6Token = NULL; + + + if (HttpInstance->LocalAddressIsIPv6) { + ASSERT (Tcp6 != NULL); + } else { + ASSERT (Tcp4 != NULL); + } + + if (HttpInstance->LocalAddressIsIPv6) { + Rx6Token = &Wrap->TcpWrap.Rx6Token; + Rx6Token ->Packet.RxData->DataLength = (UINT32) HttpMsg->BodyLength; + Rx6Token ->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32) HttpMsg->BodyLength; + Rx6Token ->Packet.RxData->FragmentTable[0].FragmentBuffer = (VOID *) HttpMsg->Body; + Rx6Token->CompletionToken.Status = EFI_NOT_READY; + + Status = Tcp6->Receive (Tcp6, Rx6Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status)); + return Status; + } + + } else { + Rx4Token = &Wrap->TcpWrap.Rx4Token; + Rx4Token->Packet.RxData->DataLength = (UINT32) HttpMsg->BodyLength; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32) HttpMsg->BodyLength; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = (VOID *) HttpMsg->Body; + + Rx4Token->CompletionToken.Status = EFI_NOT_READY; + Status = Tcp4->Receive (Tcp4, Rx4Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); + return Status; + } + } + + return EFI_SUCCESS; + +} + +/** + Clean up Tcp Tokens while the Tcp transmission error occurs. + + @param[in] Wrap Pointer to HTTP token's wrap data. + +**/ +VOID +HttpTcpTokenCleanup ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + HTTP_PROTOCOL *HttpInstance; + EFI_TCP4_IO_TOKEN *Rx4Token; + EFI_TCP6_IO_TOKEN *Rx6Token; + + HttpInstance = Wrap->HttpInstance; + Rx4Token = NULL; + Rx6Token = NULL; + + if (HttpInstance->LocalAddressIsIPv6) { + if (Wrap != NULL) { + if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); + } + + Rx6Token = &Wrap->TcpWrap.Rx6Token; + if (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + FreePool (Wrap); + } + + if (HttpInstance->Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (HttpInstance->Rx6Token.CompletionToken.Event); + HttpInstance->Rx6Token.CompletionToken.Event = NULL; + } + + Rx6Token = &HttpInstance->Rx6Token; + if (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + + } else { + if (Wrap != NULL) { + if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); + } + Rx4Token = &Wrap->TcpWrap.Rx4Token; + if (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + FreePool (Wrap); + } + + if (HttpInstance->Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (HttpInstance->Rx4Token.CompletionToken.Event); + HttpInstance->Rx4Token.CompletionToken.Event = NULL; + } + + Rx4Token = &HttpInstance->Rx4Token; + if (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + } + +} + /** Generate HTTP request string. diff --git a/NetworkPkg/HttpDxe/HttpProto.h b/NetworkPkg/HttpDxe/HttpProto.h index c37b80c8ec..a15e0a87be 100644 --- a/NetworkPkg/HttpDxe/HttpProto.h +++ b/NetworkPkg/HttpDxe/HttpProto.h @@ -27,6 +27,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. HTTP_SERVICE_SIGNATURE \ ) + // // The state of HTTP protocol. It starts from UNCONFIGED. // @@ -58,18 +59,23 @@ typedef struct _HTTP_SERVICE { EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; EFI_HANDLE ImageHandle; EFI_HANDLE ControllerHandle; + EFI_HANDLE Tcp4ChildHandle; + EFI_HANDLE Tcp6ChildHandle; LIST_ENTRY ChildrenList; UINTN ChildrenNumber; - EFI_HANDLE TcpChildHandle; INTN State; } HTTP_SERVICE; typedef struct { - EFI_TCP4_IO_TOKEN TxToken; - EFI_TCP4_TRANSMIT_DATA TxData; + EFI_TCP4_IO_TOKEN Tx4Token; + EFI_TCP4_TRANSMIT_DATA Tx4Data; + EFI_TCP6_IO_TOKEN Tx6Token; + EFI_TCP6_TRANSMIT_DATA Tx6Data; + EFI_TCP4_IO_TOKEN Rx4Token; + EFI_TCP4_RECEIVE_DATA Rx4Data; + EFI_TCP6_IO_TOKEN Rx6Token; + EFI_TCP6_RECEIVE_DATA Rx6Data; BOOLEAN IsTxDone; - EFI_TCP4_IO_TOKEN RxToken; - EFI_TCP4_RECEIVE_DATA RxData; BOOLEAN IsRxDone; UINTN BodyLen; EFI_HTTP_METHOD Method; @@ -84,26 +90,43 @@ typedef struct _HTTP_PROTOCOL { BOOLEAN InDestroy; INTN State; - EFI_HANDLE TcpChildHandle; + EFI_HANDLE Tcp4ChildHandle; EFI_TCP4_PROTOCOL *Tcp4; EFI_TCP4_CONFIG_DATA Tcp4CfgData; EFI_TCP4_OPTION Tcp4Option; - EFI_TCP4_CONNECTION_TOKEN ConnToken; - BOOLEAN IsConnDone; - EFI_TCP4_CLOSE_TOKEN CloseToken; - BOOLEAN IsCloseDone; - + EFI_TCP4_CONNECTION_TOKEN Tcp4ConnToken; + BOOLEAN IsTcp4ConnDone; + EFI_TCP4_CLOSE_TOKEN Tcp4CloseToken; + BOOLEAN IsTcp4CloseDone; CHAR8 *RemoteHost; UINT16 RemotePort; EFI_IPv4_ADDRESS RemoteAddr; + + EFI_HANDLE Tcp6ChildHandle; + EFI_TCP6_PROTOCOL *Tcp6; + EFI_TCP6_CONFIG_DATA Tcp6CfgData; + EFI_TCP6_OPTION Tcp6Option; + + EFI_TCP6_CONNECTION_TOKEN Tcp6ConnToken; + BOOLEAN IsTcp6ConnDone; + EFI_TCP6_CLOSE_TOKEN Tcp6CloseToken; + BOOLEAN IsTcp6CloseDone; + EFI_IPv6_ADDRESS RemoteIpv6Addr; + + + // - // RxToken used for receiving HTTP header. + // Rx4Token or Rx6Token used for receiving HTTP header. // - EFI_TCP4_IO_TOKEN RxToken; - EFI_TCP4_RECEIVE_DATA RxData; + EFI_TCP4_IO_TOKEN Rx4Token; + EFI_TCP4_RECEIVE_DATA Rx4Data; + EFI_TCP6_IO_TOKEN Rx6Token; + EFI_TCP6_RECEIVE_DATA Rx6Data; BOOLEAN IsRxDone; + CHAR8 **EndofHeader; + CHAR8 **HttpHeaders; CHAR8 *CacheBody; CHAR8 *NextMsg; UINTN CacheLen; @@ -119,6 +142,7 @@ typedef struct _HTTP_PROTOCOL { BOOLEAN LocalAddressIsIPv6; EFI_HTTPv4_ACCESS_POINT IPv4Node; + EFI_HTTPv6_ACCESS_POINT Ipv6Node; NET_MAP TxTokens; NET_MAP RxTokens; @@ -158,7 +182,7 @@ HttpCommonNotify ( ); /** - Create events for the TCP4 connection token and TCP4 close token. + Create events for the TCP connection token and TCP close token. @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. @@ -167,23 +191,23 @@ HttpCommonNotify ( **/ EFI_STATUS -HttpCreateTcp4ConnCloseEvent ( +HttpCreateTcpConnCloseEvent ( IN HTTP_PROTOCOL *HttpInstance ); /** - Close events in the TCP4 connection token and TCP4 close token. + Close events in the TCP connection token and TCP close token. @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. **/ VOID -HttpCloseTcp4ConnCloseEvent ( +HttpCloseTcpConnCloseEvent ( IN HTTP_PROTOCOL *HttpInstance ); /** - Create event for the TCP4 transmit token. + Create event for the TCP transmit token. @param[in] Wrap Point to HTTP token's wrap data. @@ -192,12 +216,12 @@ HttpCloseTcp4ConnCloseEvent ( **/ EFI_STATUS -HttpCreateTcp4TxEvent ( +HttpCreateTcpTxEvent ( IN HTTP_TOKEN_WRAP *Wrap ); /** - Create event for the TCP4 receive token which is used to receive HTTP header. + Create event for the TCP receive token which is used to receive HTTP header. @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. @@ -206,12 +230,12 @@ HttpCreateTcp4TxEvent ( **/ EFI_STATUS -HttpCreateTcp4RxEventForHeader ( +HttpCreateTcpRxEventForHeader ( IN HTTP_PROTOCOL *HttpInstance ); /** - Create event for the TCP4 receive token which is used to receive HTTP body. + Create event for the TCP receive token which is used to receive HTTP body. @param[in] Wrap Point to HTTP token's wrap data. @@ -220,15 +244,26 @@ HttpCreateTcp4RxEventForHeader ( **/ EFI_STATUS -HttpCreateTcp4RxEvent ( +HttpCreateTcpRxEvent ( IN HTTP_TOKEN_WRAP *Wrap ); +/** + Close Events for Tcp Receive Tokens for HTTP body and HTTP header. + + @param[in] Wrap Pointer to HTTP token's wrap data. + +**/ +VOID +HttpCloseTcpRxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ); + /** Intiialize the HTTP_PROTOCOL structure to the unconfigured state. - @param[in] HttpSb The HTTP service private instance. @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] IpVersion Indicate us TCP4 protocol or TCP6 protocol. @retval EFI_SUCCESS HTTP_PROTOCOL structure is initialized successfully. @retval Others Other error as indicated. @@ -236,8 +271,8 @@ HttpCreateTcp4RxEvent ( **/ EFI_STATUS HttpInitProtocol ( - IN HTTP_SERVICE *HttpSb, - IN OUT HTTP_PROTOCOL *HttpInstance + IN OUT HTTP_PROTOCOL *HttpInstance, + IN BOOLEAN IpVersion ); /** @@ -295,6 +330,22 @@ HttpConfigureTcp4 ( IN HTTP_TOKEN_WRAP *Wrap ); +/** + Configure TCP6 protocol child. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + + @retval EFI_SUCCESS The TCP6 protocol child is configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConfigureTcp6 ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap + ); + /** Check existing TCP connection, if in error state, receover TCP4 connection. @@ -311,7 +362,22 @@ HttpConnectTcp4 ( ); /** - Send the HTTP message through TCP4. + Check existing TCP connection, if in error state, recover TCP6 connection. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval EFI_NOT_READY TCP6 protocol child is not created or configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConnectTcp6 ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Send the HTTP message through TCP4 or TCP6. @param[in] HttpInstance The HTTP instance private data. @param[in] Wrap The HTTP token's wrap data. @@ -323,7 +389,7 @@ HttpConnectTcp4 ( **/ EFI_STATUS -HttpTransmitTcp4 ( +HttpTransmitTcp ( IN HTTP_PROTOCOL *HttpInstance, IN HTTP_TOKEN_WRAP *Wrap, IN UINT8 *TxString, @@ -346,7 +412,7 @@ HttpMappingToStatusCode ( /** Check whether the user's token or event has already - been enqueue on HTTP TxToken or RxToken list. + been enqueue on HTTP Tx or Rx Token list. @param[in] Map The container of either user's transmit or receive token. @@ -367,7 +433,7 @@ HttpTokenExist ( ); /** - Check whether the HTTP message associated with TxToken is already sent out. + Check whether the HTTP message associated with TxToken or Tx6Token is already sent out. @param[in] Map The container of TxToken. @param[in] Item Current item to check against. @@ -385,10 +451,26 @@ HttpTcpNotReady ( IN VOID *Context ); +/** + Initialize TCP related data. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + + @retval EFI_SUCCESS The initialization of TCP instance is done. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpInitTcp ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap + ); + /** Transmit the HTTP mssage by processing the associated HTTP token. - @param[in] Map The container of TxToken. + @param[in] Map The container of TxToken or Tx6Token. @param[in] Item Current item to check against. @param[in] Context The Token to check againist. @@ -408,7 +490,7 @@ HttpTcpTransmit ( /** Receive the HTTP response by processing the associated HTTP token. - @param[in] Map The container of RxToken. + @param[in] Map The container of Rx4Token or Rx6Token. @param[in] Item Current item to check against. @param[in] Context The Token to check againist. @@ -425,6 +507,51 @@ HttpTcpReceive ( IN VOID *Context ); +/** + Receive the HTTP header by processing the associated HTTP token. + + @param[in] HttpInstance The HTTP instance private data. + @param[in, out] SizeofHeaders The HTTP header length. + @param[in, out] BufferSize The size of buffer to cacahe the header message. + + @retval EFI_SUCCESS The HTTP header is received. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpTcpReceiveHeader ( + IN HTTP_PROTOCOL *HttpInstance, + IN OUT UINTN *SizeofHeaders, + IN OUT UINTN *BufferSize + ); + +/** + Receive the HTTP body by processing the associated HTTP token. + + @param[in] Wrap The HTTP token's wrap data. + @param[in] HttpMsg The HTTP message data. + + @retval EFI_SUCCESS The HTTP body is received. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpTcpReceiveBody ( + IN HTTP_TOKEN_WRAP *Wrap, + IN EFI_HTTP_MESSAGE *HttpMsg + ); + +/** + Clean up Tcp Tokens while the Tcp transmission error occurs. + + @param[in] Wrap Pointer to HTTP token's wrap data. + +**/ +VOID +HttpTcpTokenCleanup ( + IN HTTP_TOKEN_WRAP *Wrap + ); + /** Generate HTTP request string.