diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe')
22 files changed, 8765 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootClient.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootClient.c new file mode 100644 index 00000000..b59af6d5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootClient.c @@ -0,0 +1,1306 @@ +/** @file + Implementation of the boot file download function. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HttpBootDxe.h" + +/** + Update the device path node to include the boot resource information. + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Device patch successfully updated. + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. + @retval Others Unexpected error happened. + +**/ +EFI_STATUS +HttpBootUpdateDevicePath ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_DEV_PATH *Node; + EFI_DEVICE_PATH_PROTOCOL *TmpIpDevicePath; + EFI_DEVICE_PATH_PROTOCOL *TmpDnsDevicePath; + EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; + UINTN Length; + EFI_STATUS Status; + + TmpIpDevicePath = NULL; + TmpDnsDevicePath = NULL; + + // + // Update the IP node with DHCP assigned information. + // + if (!Private->UsingIpv6) { + Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH)); + if (Node == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH; + Node->Ipv4.Header.SubType = MSG_IPv4_DP; + SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH)); + CopyMem (&Node->Ipv4.LocalIpAddress, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS)); + Node->Ipv4.RemotePort = Private->Port; + Node->Ipv4.Protocol = EFI_IP_PROTO_TCP; + Node->Ipv4.StaticIpAddress = FALSE; + CopyMem (&Node->Ipv4.GatewayIpAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Node->Ipv4.SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + } else { + Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH)); + if (Node == NULL) { + return EFI_OUT_OF_RESOURCES; + } + 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)); + } + + TmpIpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + if (TmpIpDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Update the DNS node with DNS server IP list if existed. + // + if (Private->DnsServerIp != NULL) { + Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + sizeof (Node->Dns.IsIPv6) + Private->DnsServerCount * sizeof (EFI_IP_ADDRESS); + Node = AllocatePool (Length); + if (Node == NULL) { + FreePool (TmpIpDevicePath); + return EFI_OUT_OF_RESOURCES; + } + Node->DevPath.Type = MESSAGING_DEVICE_PATH; + Node->DevPath.SubType = MSG_DNS_DP; + SetDevicePathNodeLength (Node, Length); + Node->Dns.IsIPv6 = Private->UsingIpv6 ? 0x01 : 0x00; + CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL) + sizeof (Node->Dns.IsIPv6), Private->DnsServerIp, Private->DnsServerCount * sizeof (EFI_IP_ADDRESS)); + + TmpDnsDevicePath = AppendDevicePathNode (TmpIpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + FreePool (TmpIpDevicePath); + TmpIpDevicePath = NULL; + if (TmpDnsDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + // + // Update the URI node with the boot file URI. + // + Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (Private->BootFileUri); + Node = AllocatePool (Length); + if (Node == NULL) { + if (TmpIpDevicePath != NULL) { + FreePool (TmpIpDevicePath); + } + if (TmpDnsDevicePath != NULL) { + FreePool (TmpDnsDevicePath); + } + return EFI_OUT_OF_RESOURCES; + } + Node->DevPath.Type = MESSAGING_DEVICE_PATH; + Node->DevPath.SubType = MSG_URI_DP; + SetDevicePathNodeLength (Node, Length); + CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Private->BootFileUri, AsciiStrSize (Private->BootFileUri)); + + if (TmpDnsDevicePath != NULL) { + NewDevicePath = AppendDevicePathNode (TmpDnsDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (TmpDnsDevicePath); + } else { + ASSERT (TmpIpDevicePath != NULL); + NewDevicePath = AppendDevicePathNode (TmpIpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (TmpIpDevicePath); + } + FreePool (Node); + if (NewDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + 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; + } + + return EFI_SUCCESS; +} + +/** + Parse the boot file URI information from the selected Dhcp4 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 +HttpBootDhcp4ExtractUriInfo ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + HTTP_BOOT_DHCP4_PACKET_CACHE *SelectOffer; + HTTP_BOOT_DHCP4_PACKET_CACHE *HttpOffer; + UINT32 SelectIndex; + UINT32 ProxyIndex; + UINT32 DnsServerIndex; + EFI_DHCP4_PACKET_OPTION *Option; + EFI_STATUS Status; + + ASSERT (Private != NULL); + ASSERT (Private->SelectIndex != 0); + SelectIndex = Private->SelectIndex - 1; + ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM); + + DnsServerIndex = 0; + + Status = EFI_SUCCESS; + + // + // SelectOffer contains the IP address configuration and name server configuration. + // HttpOffer contains the boot file URL. + // + SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp4; + if (Private->FilePathUri == NULL) { + // + // In Corporate environment, we need a HttpOffer. + // + if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || + (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns) || + (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) { + HttpOffer = SelectOffer; + } else { + ASSERT (Private->SelectProxyType != HttpOfferTypeMax); + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; + HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4; + } + Private->BootFileUriParser = HttpOffer->UriParser; + Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data; + } else { + // + // In Home environment the BootFileUri comes from the FilePath. + // + Private->BootFileUriParser = Private->FilePathUriParser; + Private->BootFileUri = Private->FilePathUri; + } + + // + // Check the URI scheme. + // + Status = HttpBootCheckUriScheme (Private->BootFileUri); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpBootDhcp4ExtractUriInfo: %r.\n", Status)); + if (Status == EFI_INVALID_PARAMETER) { + AsciiPrint ("\n Error: Invalid URI address.\n"); + } else if (Status == EFI_ACCESS_DENIED) { + AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n"); + } + return Status; + } + + if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || + (SelectOffer->OfferType == HttpOfferTypeDhcpDns) || + (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns)) { + Option = SelectOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER]; + ASSERT (Option != NULL); + + // + // Record the Dns Server address list. + // + Private->DnsServerCount = (Option->Length) / sizeof (EFI_IPv4_ADDRESS); + + Private->DnsServerIp = AllocateZeroPool (Private->DnsServerCount * sizeof (EFI_IP_ADDRESS)); + if (Private->DnsServerIp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (DnsServerIndex = 0; DnsServerIndex < Private->DnsServerCount; DnsServerIndex++) { + CopyMem (&(Private->DnsServerIp[DnsServerIndex].v4), &(((EFI_IPv4_ADDRESS *) Option->Data)[DnsServerIndex]), sizeof (EFI_IPv4_ADDRESS)); + } + + // + // Configure the default DNS server if server assigned. + // + Status = HttpBootRegisterIp4Dns ( + Private, + Option->Length, + Option->Data + ); + if (EFI_ERROR (Status)) { + FreePool (Private->DnsServerIp); + Private->DnsServerIp = NULL; + return Status; + } + } + + // + // Extract the port from URL, and use default HTTP port 80 if not provided. + // + Status = HttpUrlGetPort ( + Private->BootFileUri, + Private->BootFileUriParser, + &Private->Port + ); + if (EFI_ERROR (Status) || Private->Port == 0) { + Private->Port = 80; + } + + // + // All boot informations are valid here. + // + + // + // Update the device path to include the boot resource information. + // + Status = HttpBootUpdateDevicePath (Private); + if (EFI_ERROR (Status) && Private->DnsServerIp != NULL) { + FreePool (Private->DnsServerIp); + Private->DnsServerIp = NULL; + } + + 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; + UINT32 DnsServerIndex; + EFI_DHCP6_PACKET_OPTION *Option; + EFI_IPv6_ADDRESS IpAddr; + CHAR8 *HostName; + UINTN HostNameSize; + CHAR16 *HostNameStr; + EFI_STATUS Status; + + ASSERT (Private != NULL); + ASSERT (Private->SelectIndex != 0); + SelectIndex = Private->SelectIndex - 1; + ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM); + + DnsServerIndex = 0; + + 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 (Private->FilePathUri == NULL) { + // + // In Corporate environment, we need a HttpOffer. + // + if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || + (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns) || + (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) { + HttpOffer = SelectOffer; + } else { + ASSERT (Private->SelectProxyType != HttpOfferTypeMax); + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; + HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp6; + } + Private->BootFileUriParser = HttpOffer->UriParser; + Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data; + } else { + // + // In Home environment the BootFileUri comes from the FilePath. + // + Private->BootFileUriParser = Private->FilePathUriParser; + Private->BootFileUri = Private->FilePathUri; + } + + // + // Check the URI scheme. + // + Status = HttpBootCheckUriScheme (Private->BootFileUri); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpBootDhcp6ExtractUriInfo: %r.\n", Status)); + if (Status == EFI_INVALID_PARAMETER) { + AsciiPrint ("\n Error: Invalid URI address.\n"); + } else if (Status == EFI_ACCESS_DENIED) { + AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n"); + } + return Status; + } + + // + // Set the Local station address to IP layer. + // + Status = HttpBootSetIp6Address (Private); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Register the IPv6 gateway address to the network device. + // + Status = HttpBootSetIp6Gateway (Private); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || + (SelectOffer->OfferType == HttpOfferTypeDhcpDns) || + (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns)) { + Option = SelectOffer->OptList[HTTP_BOOT_DHCP6_IDX_DNS_SERVER]; + ASSERT (Option != NULL); + + // + // Record the Dns Server address list. + // + Private->DnsServerCount = HTONS (Option->OpLen) / sizeof (EFI_IPv6_ADDRESS); + + Private->DnsServerIp = AllocateZeroPool (Private->DnsServerCount * sizeof (EFI_IP_ADDRESS)); + if (Private->DnsServerIp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (DnsServerIndex = 0; DnsServerIndex < Private->DnsServerCount; DnsServerIndex++) { + CopyMem (&(Private->DnsServerIp[DnsServerIndex].v6), &(((EFI_IPv6_ADDRESS *) Option->Data)[DnsServerIndex]), sizeof (EFI_IPv6_ADDRESS)); + } + + // + // Configure the default DNS server if server assigned. + // + Status = HttpBootSetIp6Dns ( + Private, + HTONS (Option->OpLen), + Option->Data + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } + + // + // Extract the HTTP server Ip from URL. This is used to Check route table + // whether can send message to HTTP Server Ip through the GateWay. + // + Status = HttpUrlGetIp6 ( + Private->BootFileUri, + Private->BootFileUriParser, + &IpAddr + ); + + if (EFI_ERROR (Status)) { + // + // The Http server address is expressed by Name Ip, so perform DNS resolution + // + Status = HttpUrlGetHostName ( + Private->BootFileUri, + Private->BootFileUriParser, + &HostName + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + HostNameSize = AsciiStrSize (HostName); + HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16)); + if (HostNameStr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize); + + if (HostName != NULL) { + FreePool (HostName); + } + + Status = HttpBootDns (Private, HostNameStr, &IpAddr); + FreePool (HostNameStr); + if (EFI_ERROR (Status)) { + AsciiPrint ("\n Error: Could not retrieve the host address from DNS server.\n"); + goto Error; + } + } + + CopyMem (&Private->ServerIp.v6, &IpAddr, sizeof (EFI_IPv6_ADDRESS)); + + // + // Extract the port from URL, and use default HTTP port 80 if not provided. + // + Status = HttpUrlGetPort ( + Private->BootFileUri, + Private->BootFileUriParser, + &Private->Port + ); + if (EFI_ERROR (Status) || Private->Port == 0) { + Private->Port = 80; + } + + // + // All boot informations are valid here. + // + + // + // Update the device path to include the boot resource information. + // + Status = HttpBootUpdateDevicePath (Private); + if (EFI_ERROR (Status)) { + goto Error; + } + + return Status; + +Error: + if (Private->DnsServerIp != NULL) { + FreePool (Private->DnsServerIp); + Private->DnsServerIp = NULL; + } + + return Status; +} + + +/** + Discover all the boot information for boot file. + + @param[in, out] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Successfully obtained all the boot information . + @retval Others Failed to retrieve the boot information. + +**/ +EFI_STATUS +HttpBootDiscoverBootInfo ( + IN OUT HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + + // + // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and + // other Http boot information. + // + Status = HttpBootDhcp (Private); + if (EFI_ERROR (Status)) { + return Status; + } + + if (!Private->UsingIpv6) { + Status = HttpBootDhcp4ExtractUriInfo (Private); + } else { + Status = HttpBootDhcp6ExtractUriInfo (Private); + } + + return Status; +} + +/** + HttpIo Callback function which will be invoked when specified HTTP_IO_CALLBACK_EVENT happened. + + @param[in] EventType Indicate the Event type that occurs in the current callback. + @param[in] Message HTTP message which will be send to, or just received from HTTP server. + @param[in] Context The Callback Context pointer. + + @retval EFI_SUCCESS Tells the HttpIo to continue the HTTP process. + @retval Others Tells the HttpIo to abort the current HTTP process. +**/ +EFI_STATUS +EFIAPI +HttpBootHttpIoCallback ( + IN HTTP_IO_CALLBACK_EVENT EventType, + IN EFI_HTTP_MESSAGE *Message, + IN VOID *Context + ) +{ + HTTP_BOOT_PRIVATE_DATA *Private; + EFI_STATUS Status; + Private = (HTTP_BOOT_PRIVATE_DATA *) Context; + if (Private->HttpBootCallback != NULL) { + Status = Private->HttpBootCallback->Callback ( + Private->HttpBootCallback, + EventType == HttpIoRequest ? HttpBootHttpRequest : HttpBootHttpResponse, + EventType == HttpIoRequest ? FALSE : TRUE, + sizeof (EFI_HTTP_MESSAGE), + (VOID *) Message + ); + return Status; + } + return EFI_SUCCESS; +} + +/** + Create a HttpIo instance for the file download. + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Successfully created. + @retval Others Failed to create HttpIo. + +**/ +EFI_STATUS +HttpBootCreateHttpIo ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + HTTP_IO_CONFIG_DATA ConfigData; + EFI_STATUS Status; + EFI_HANDLE ImageHandle; + + ASSERT (Private != NULL); + + ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA)); + if (!Private->UsingIpv6) { + 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); + ImageHandle = Private->Ip4Nic->ImageHandle; + } else { + ConfigData.Config6.HttpVersion = HttpVersion11; + ConfigData.Config6.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT; + IP6_COPY_ADDRESS (&ConfigData.Config6.LocalIp, &Private->StationIp.v6); + ImageHandle = Private->Ip6Nic->ImageHandle; + } + + Status = HttpIoCreateIo ( + ImageHandle, + Private->Controller, + Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4, + &ConfigData, + HttpBootHttpIoCallback, + (VOID *) Private, + &Private->HttpIo + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Private->HttpCreated = TRUE; + return EFI_SUCCESS; +} + +/** + Release all the resource of a cache item. + + @param[in] Cache The pointer to the cache item. + +**/ +VOID +HttpBootFreeCache ( + IN HTTP_BOOT_CACHE_CONTENT *Cache + ) +{ + UINTN Index; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + HTTP_BOOT_ENTITY_DATA *EntityData; + + if (Cache != NULL) { + // + // Free the request data + // + if (Cache->RequestData != NULL) { + if (Cache->RequestData->Url != NULL) { + FreePool (Cache->RequestData->Url); + } + FreePool (Cache->RequestData); + } + + // + // Free the response header + // + if (Cache->ResponseData != NULL) { + if (Cache->ResponseData->Headers != NULL) { + for (Index = 0; Index < Cache->ResponseData->HeaderCount; Index++) { + FreePool (Cache->ResponseData->Headers[Index].FieldName); + FreePool (Cache->ResponseData->Headers[Index].FieldValue); + } + FreePool (Cache->ResponseData->Headers); + } + } + + // + // Free the response body + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Cache->EntityDataList) { + EntityData = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_ENTITY_DATA, Link); + if (EntityData->Block != NULL) { + FreePool (EntityData->Block); + } + RemoveEntryList (&EntityData->Link); + FreePool (EntityData); + } + + FreePool (Cache); + } +} + +/** + Clean up all cached data. + + @param[in] Private The pointer to the driver's private data. + +**/ +VOID +HttpBootFreeCacheList ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + HTTP_BOOT_CACHE_CONTENT *Cache; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->CacheList) { + Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link); + RemoveEntryList (&Cache->Link); + HttpBootFreeCache (Cache); + } +} + +/** + Get the file content from cached data. + + @param[in] Private The pointer to the driver's private data. + @param[in] Uri Uri of the file to be retrieved from cache. + @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL, + then the size of the requested file is returned in + BufferSize. + @param[out] ImageType The image type of the downloaded file. + + @retval EFI_SUCCESS Successfully created. + @retval Others Failed to create HttpIo. + +**/ +EFI_STATUS +HttpBootGetFileFromCache ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN CHAR16 *Uri, + IN OUT UINTN *BufferSize, + OUT UINT8 *Buffer, + OUT HTTP_BOOT_IMAGE_TYPE *ImageType + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + HTTP_BOOT_CACHE_CONTENT *Cache; + HTTP_BOOT_ENTITY_DATA *EntityData; + UINTN CopyedSize; + + if (Uri == NULL || BufferSize == NULL || Buffer == NULL || ImageType == NULL) { + return EFI_INVALID_PARAMETER; + } + + NET_LIST_FOR_EACH (Entry, &Private->CacheList) { + Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link); + // + // Compare the URI to see whether we already have a cache for this file. + // + if ((Cache->RequestData != NULL) && + (Cache->RequestData->Url != NULL) && + (StrCmp (Uri, Cache->RequestData->Url) == 0)) { + // + // Hit in cache, record image type. + // + *ImageType = Cache->ImageType; + + // + // Check buffer size. + // + if (*BufferSize < Cache->EntityLength) { + *BufferSize = Cache->EntityLength; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Fill data to buffer. + // + CopyedSize = 0; + NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) { + EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link); + if (*BufferSize > CopyedSize) { + CopyMem ( + Buffer + CopyedSize, + EntityData->DataStart, + MIN (EntityData->DataLength, *BufferSize - CopyedSize) + ); + CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize); + } + } + *BufferSize = CopyedSize; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + A callback function to intercept events during message parser. + + This function will be invoked during HttpParseMessageBody() with various events type. An error + return status of the callback function will cause the HttpParseMessageBody() aborted. + + @param[in] EventType Event type of this callback call. + @param[in] Data A pointer to data buffer. + @param[in] Length Length in bytes of the Data. + @param[in] Context Callback context set by HttpInitMsgParser(). + + @retval EFI_SUCCESS Continue to parser the message body. + @retval Others Abort the parse. + +**/ +EFI_STATUS +EFIAPI +HttpBootGetBootFileCallback ( + IN HTTP_BODY_PARSE_EVENT EventType, + IN CHAR8 *Data, + IN UINTN Length, + IN VOID *Context + ) +{ + HTTP_BOOT_CALLBACK_DATA *CallbackData; + HTTP_BOOT_ENTITY_DATA *NewEntityData; + EFI_STATUS Status; + EFI_HTTP_BOOT_CALLBACK_PROTOCOL *HttpBootCallback; + + // + // We only care about the entity data. + // + if (EventType != BodyParseEventOnData) { + return EFI_SUCCESS; + } + + CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context; + HttpBootCallback = CallbackData->Private->HttpBootCallback; + if (HttpBootCallback != NULL) { + Status = HttpBootCallback->Callback ( + HttpBootCallback, + HttpBootHttpEntityBody, + TRUE, + (UINT32)Length, + Data + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + // + // Copy data if caller has provided a buffer. + // + if (CallbackData->BufferSize > CallbackData->CopyedSize) { + CopyMem ( + CallbackData->Buffer + CallbackData->CopyedSize, + Data, + MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize) + ); + CallbackData->CopyedSize += MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize); + } + + // + // The caller doesn't provide a buffer, save the block into cache list. + // + if (CallbackData->Cache != NULL) { + NewEntityData = AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA)); + if (NewEntityData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + if (CallbackData->NewBlock) { + NewEntityData->Block = CallbackData->Block; + CallbackData->Block = NULL; + } + NewEntityData->DataLength = Length; + NewEntityData->DataStart = (UINT8*) Data; + InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link); + } + return EFI_SUCCESS; +} + +/** + This function download the boot file by using UEFI HTTP protocol. + + @param[in] Private The pointer to the driver's private data. + @param[in] HeaderOnly Only request the response header, it could save a lot of time if + the caller only want to know the size of the requested file. + @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL, + then the size of the requested file is returned in + BufferSize. + @param[out] ImageType The image type of the downloaded file. + + @retval EFI_SUCCESS The file was loaded. + @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. + BufferSize has been updated with the size needed to complete + the request. + @retval Others Unexpected error happened. + +**/ +EFI_STATUS +HttpBootGetBootFile ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN BOOLEAN HeaderOnly, + IN OUT UINTN *BufferSize, + OUT UINT8 *Buffer, + OUT HTTP_BOOT_IMAGE_TYPE *ImageType + ) +{ + EFI_STATUS Status; + EFI_HTTP_STATUS_CODE StatusCode; + CHAR8 *HostName; + EFI_HTTP_REQUEST_DATA *RequestData; + HTTP_IO_RESPONSE_DATA *ResponseData; + HTTP_IO_RESPONSE_DATA ResponseBody; + HTTP_IO *HttpIo; + HTTP_IO_HEADER *HttpIoHeader; + VOID *Parser; + HTTP_BOOT_CALLBACK_DATA Context; + UINTN ContentLength; + HTTP_BOOT_CACHE_CONTENT *Cache; + UINT8 *Block; + UINTN UrlSize; + CHAR16 *Url; + BOOLEAN IdentityMode; + UINTN ReceivedSize; + + ASSERT (Private != NULL); + ASSERT (Private->HttpCreated); + + if (BufferSize == NULL || ImageType == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (*BufferSize != 0 && Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // First, check whether we already cached the requested Uri. + // + UrlSize = AsciiStrSize (Private->BootFileUri); + Url = AllocatePool (UrlSize * sizeof (CHAR16)); + if (Url == NULL) { + return EFI_OUT_OF_RESOURCES; + } + AsciiStrToUnicodeStrS (Private->BootFileUri, Url, UrlSize); + if (!HeaderOnly && Buffer != NULL) { + Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer, ImageType); + if (Status != EFI_NOT_FOUND) { + FreePool (Url); + return Status; + } + } + + // + // Not found in cache, try to download it through HTTP. + // + + // + // 1. Create a temp cache item for the requested URI if caller doesn't provide buffer. + // + Cache = NULL; + if ((!HeaderOnly) && (*BufferSize == 0)) { + Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT)); + if (Cache == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ERROR_1; + } + Cache->ImageType = ImageTypeMax; + InitializeListHead (&Cache->EntityDataList); + } + + // + // 2. Send HTTP request message. + // + + // + // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file: + // Host + // Accept + // User-Agent + // + HttpIoHeader = HttpIoCreateHeader (3); + if (HttpIoHeader == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ERROR_2; + } + + // + // Add HTTP header field 1: Host + // + HostName = NULL; + Status = HttpUrlGetHostName ( + Private->BootFileUri, + Private->BootFileUriParser, + &HostName + ); + if (EFI_ERROR (Status)) { + goto ERROR_3; + } + Status = HttpIoSetHeader ( + HttpIoHeader, + HTTP_HEADER_HOST, + HostName + ); + FreePool (HostName); + if (EFI_ERROR (Status)) { + goto ERROR_3; + } + + // + // Add HTTP header field 2: Accept + // + Status = HttpIoSetHeader ( + HttpIoHeader, + HTTP_HEADER_ACCEPT, + "*/*" + ); + if (EFI_ERROR (Status)) { + goto ERROR_3; + } + + // + // Add HTTP header field 3: User-Agent + // + Status = HttpIoSetHeader ( + HttpIoHeader, + HTTP_HEADER_USER_AGENT, + HTTP_USER_AGENT_EFI_HTTP_BOOT + ); + if (EFI_ERROR (Status)) { + goto ERROR_3; + } + + // + // 2.2 Build the rest of HTTP request info. + // + RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA)); + if (RequestData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ERROR_3; + } + RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet; + RequestData->Url = Url; + + // + // 2.3 Record the request info in a temp cache item. + // + if (Cache != NULL) { + Cache->RequestData = RequestData; + } + + // + // 2.4 Send out the request to HTTP server. + // + HttpIo = &Private->HttpIo; + Status = HttpIoSendRequest ( + HttpIo, + RequestData, + HttpIoHeader->HeaderCount, + HttpIoHeader->Headers, + 0, + NULL + ); + if (EFI_ERROR (Status)) { + goto ERROR_4; + } + + // + // 3. Receive HTTP response message. + // + + // + // 3.1 First step, use zero BodyLength to only receive the response headers. + // + ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESPONSE_DATA)); + if (ResponseData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ERROR_4; + } + Status = HttpIoRecvResponse ( + &Private->HttpIo, + TRUE, + ResponseData + ); + if (EFI_ERROR (Status) || EFI_ERROR (ResponseData->Status)) { + if (EFI_ERROR (ResponseData->Status)) { + StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode; + HttpBootPrintErrorMessage (StatusCode); + Status = ResponseData->Status; + } + goto ERROR_5; + } + + // + // Check the image type according to server's response. + // + Status = HttpBootCheckImageType ( + Private->BootFileUri, + Private->BootFileUriParser, + ResponseData->HeaderCount, + ResponseData->Headers, + ImageType + ); + if (EFI_ERROR (Status)) { + goto ERROR_5; + } + + // + // 3.2 Cache the response header. + // + if (Cache != NULL) { + Cache->ResponseData = ResponseData; + Cache->ImageType = *ImageType; + } + + // + // 3.3 Init a message-body parser from the header information. + // + Parser = NULL; + Context.NewBlock = FALSE; + Context.Block = NULL; + Context.CopyedSize = 0; + Context.Buffer = Buffer; + Context.BufferSize = *BufferSize; + Context.Cache = Cache; + Context.Private = Private; + Status = HttpInitMsgParser ( + HeaderOnly ? HttpMethodHead : HttpMethodGet, + ResponseData->Response.StatusCode, + ResponseData->HeaderCount, + ResponseData->Headers, + HttpBootGetBootFileCallback, + (VOID*) &Context, + &Parser + ); + if (EFI_ERROR (Status)) { + goto ERROR_6; + } + + // + // 3.4 Continue to receive and parse message-body if needed. + // + Block = NULL; + if (!HeaderOnly) { + // + // 3.4.1, check whether we are in identity transfer-coding. + // + ContentLength = 0; + Status = HttpGetEntityLength (Parser, &ContentLength); + if (!EFI_ERROR (Status)) { + IdentityMode = TRUE; + } else { + IdentityMode = FALSE; + } + + // + // 3.4.2, start the message-body download, the identity and chunked transfer-coding + // is handled in different path here. + // + ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESPONSE_DATA)); + if (IdentityMode) { + // + // In identity transfer-coding there is no need to parse the message body, + // just download the message body to the user provided buffer directly. + // + ReceivedSize = 0; + while (ReceivedSize < ContentLength) { + ResponseBody.Body = (CHAR8*) Buffer + ReceivedSize; + ResponseBody.BodyLength = *BufferSize - ReceivedSize; + Status = HttpIoRecvResponse ( + &Private->HttpIo, + FALSE, + &ResponseBody + ); + if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) { + if (EFI_ERROR (ResponseBody.Status)) { + Status = ResponseBody.Status; + } + goto ERROR_6; + } + ReceivedSize += ResponseBody.BodyLength; + if (Private->HttpBootCallback != NULL) { + Status = Private->HttpBootCallback->Callback ( + Private->HttpBootCallback, + HttpBootHttpEntityBody, + TRUE, + (UINT32)ResponseBody.BodyLength, + ResponseBody.Body + ); + if (EFI_ERROR (Status)) { + goto ERROR_6; + } + } + } + } else { + // + // In "chunked" transfer-coding mode, so we need to parse the received + // data to get the real entity content. + // + Block = NULL; + while (!HttpIsMessageComplete (Parser)) { + // + // Allocate a buffer in Block to hold the message-body. + // If caller provides a buffer, this Block will be reused in every HttpIoRecvResponse(). + // Otherwise a buffer, the buffer in Block will be cached and we should allocate a new before + // every HttpIoRecvResponse(). + // + if (Block == NULL || Context.BufferSize == 0) { + Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE); + if (Block == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ERROR_6; + } + Context.NewBlock = TRUE; + Context.Block = Block; + } else { + Context.NewBlock = FALSE; + } + + ResponseBody.Body = (CHAR8*) Block; + ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE; + Status = HttpIoRecvResponse ( + &Private->HttpIo, + FALSE, + &ResponseBody + ); + if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) { + if (EFI_ERROR (ResponseBody.Status)) { + Status = ResponseBody.Status; + } + goto ERROR_6; + } + + // + // Parse the new received block of the message-body, the block will be saved in cache. + // + Status = HttpParseMessageBody ( + Parser, + ResponseBody.BodyLength, + ResponseBody.Body + ); + if (EFI_ERROR (Status)) { + goto ERROR_6; + } + } + } + } + + // + // 3.5 Message-body receive & parse is completed, we should be able to get the file size now. + // + Status = HttpGetEntityLength (Parser, &ContentLength); + if (EFI_ERROR (Status)) { + goto ERROR_6; + } + + if (*BufferSize < ContentLength) { + Status = EFI_BUFFER_TOO_SMALL; + } else { + Status = EFI_SUCCESS; + } + *BufferSize = ContentLength; + + // + // 4. Save the cache item to driver's cache list and return. + // + if (Cache != NULL) { + Cache->EntityLength = ContentLength; + InsertTailList (&Private->CacheList, &Cache->Link); + } + + if (Parser != NULL) { + HttpFreeMsgParser (Parser); + } + + return Status; + +ERROR_6: + if (Parser != NULL) { + HttpFreeMsgParser (Parser); + } + if (Context.Block != NULL) { + FreePool (Context.Block); + } + HttpBootFreeCache (Cache); + +ERROR_5: + if (ResponseData != NULL) { + FreePool (ResponseData); + } +ERROR_4: + if (RequestData != NULL) { + FreePool (RequestData); + } +ERROR_3: + HttpIoFreeHeader (HttpIoHeader); +ERROR_2: + if (Cache != NULL) { + FreePool (Cache); + } +ERROR_1: + if (Url != NULL) { + FreePool (Url); + } + + return Status; +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootClient.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootClient.h new file mode 100644 index 00000000..b0ca80e9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootClient.h @@ -0,0 +1,137 @@ +/** @file + Declaration of the boot file download function. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_HTTP_BOOT_HTTP_H__ +#define __EFI_HTTP_BOOT_HTTP_H__ + +#define HTTP_BOOT_REQUEST_TIMEOUT 5000 // 5 seconds in uints of millisecond. +#define HTTP_BOOT_RESPONSE_TIMEOUT 5000 // 5 seconds in uints of millisecond. +#define HTTP_BOOT_BLOCK_SIZE 1500 + + + +#define HTTP_USER_AGENT_EFI_HTTP_BOOT "UefiHttpBoot/1.0" + +// +// Record the data length and start address of a data block. +// +typedef struct { + LIST_ENTRY Link; // Link to the EntityDataList in HTTP_BOOT_CACHE_CONTENT + UINT8 *Block; // If NULL, the data is in previous data block. + UINT8 *DataStart; // Point to somewhere in the Block + UINTN DataLength; +} HTTP_BOOT_ENTITY_DATA; + +// +// Structure for a cache item +// +typedef struct { + LIST_ENTRY Link; // Link to the CacheList in driver's private data. + EFI_HTTP_REQUEST_DATA *RequestData; + HTTP_IO_RESPONSE_DATA *ResponseData; // Not include any message-body data. + HTTP_BOOT_IMAGE_TYPE ImageType; + UINTN EntityLength; + LIST_ENTRY EntityDataList; // Entity data (message-body) +} HTTP_BOOT_CACHE_CONTENT; + +// +// Callback data for HTTP_BODY_PARSER_CALLBACK() +// +typedef struct { + EFI_STATUS Status; + // + // Cache info. + // + HTTP_BOOT_CACHE_CONTENT *Cache; + BOOLEAN NewBlock; + UINT8 *Block; + + // + // Caller provided buffer to load the file in. + // + UINTN CopyedSize; + UINTN BufferSize; + UINT8 *Buffer; + + HTTP_BOOT_PRIVATE_DATA *Private; +} HTTP_BOOT_CALLBACK_DATA; + +/** + Discover all the boot information for boot file. + + @param[in, out] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Successfully obtained all the boot information . + @retval Others Failed to retrieve the boot information. + +**/ +EFI_STATUS +HttpBootDiscoverBootInfo ( + IN OUT HTTP_BOOT_PRIVATE_DATA *Private + ); + +/** + Create a HttpIo instance for the file download. + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Successfully created. + @retval Others Failed to create HttpIo. + +**/ +EFI_STATUS +HttpBootCreateHttpIo ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +/** + This function download the boot file by using UEFI HTTP protocol. + + @param[in] Private The pointer to the driver's private data. + @param[in] HeaderOnly Only request the response header, it could save a lot of time if + the caller only want to know the size of the requested file. + @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL, + then the size of the requested file is returned in + BufferSize. + @param[out] ImageType The image type of the downloaded file. + + @retval EFI_SUCCESS The file was loaded. + @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. + BufferSize has been updated with the size needed to complete + the request. + @retval Others Unexpected error happened. + +**/ +EFI_STATUS +HttpBootGetBootFile ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN BOOLEAN HeaderOnly, + IN OUT UINTN *BufferSize, + OUT UINT8 *Buffer, + OUT HTTP_BOOT_IMAGE_TYPE *ImageType + ); + +/** + Clean up all cached data. + + @param[in] Private The pointer to the driver's private data. + +**/ +VOID +HttpBootFreeCacheList ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootComponentName.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootComponentName.c new file mode 100644 index 00000000..7eaec8ea --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootComponentName.c @@ -0,0 +1,177 @@ +/** @file + Implementation of EFI_COMPONENT_NAME_PROTOCOL and EFI_COMPONENT_NAME2_PROTOCOL protocol. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HttpBootDxe.h" + +/// +/// Component Name Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_COMPONENT_NAME_PROTOCOL gHttpBootDxeComponentName = { + (EFI_COMPONENT_NAME_GET_DRIVER_NAME) HttpBootDxeComponentNameGetDriverName, + (EFI_COMPONENT_NAME_GET_CONTROLLER_NAME)HttpBootDxeComponentNameGetControllerName, + "eng" +}; + +/// +/// Component Name 2 Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_COMPONENT_NAME2_PROTOCOL gHttpBootDxeComponentName2 = { + HttpBootDxeComponentNameGetDriverName, + HttpBootDxeComponentNameGetControllerName, + "en" +}; + +/// +/// Table of driver names +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_UNICODE_STRING_TABLE mHttpBootDxeDriverNameTable[] = { + { "eng;en", (CHAR16 *)L"UEFI HTTP Boot Driver" }, + { NULL, NULL } +}; + +/// +/// Table of controller names +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_UNICODE_STRING_TABLE mHttpBootDxeControllerNameTable[] = { + { "eng;en", (CHAR16 *)L"UEFI Http Boot Controller" }, + { NULL, NULL } +}; + +/** + Retrieves a Unicode string that is the user-readable name of the EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a three-character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + @param DriverName A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpBootDxeComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mHttpBootDxeDriverNameTable, + DriverName, + (BOOLEAN) (This != &gHttpBootDxeComponentName2) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + @param Language A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + @param ControllerName A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language, from the point of view of the driver specified + by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpBootDxeComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_HANDLE NicHandle; + UINT32 *Id; + + if (ControllerHandle == NULL || ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + NicHandle = HttpBootGetNicByIp4Children (ControllerHandle); + if (NicHandle == NULL) { + NicHandle = HttpBootGetNicByIp6Children(ControllerHandle); + if (NicHandle == NULL) { + return EFI_UNSUPPORTED; + } + } + + // + // Try to retrieve the private data by caller ID GUID. + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mHttpBootDxeControllerNameTable, + ControllerName, + (BOOLEAN)(This != &gHttpBootDxeComponentName2) + ); + +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootComponentName.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootComponentName.h new file mode 100644 index 00000000..a02fc147 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootComponentName.h @@ -0,0 +1,93 @@ +/** @file + Declaration of HTTP boot driver's EFI_COMPONENT_NAME_PROTOCOL and + EFI_COMPONENT_NAME2_PROTOCOL function. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_UEFI_HTTP_BOOT_COM_NAME_H__ +#define __EFI_UEFI_HTTP_BOOT_COM_NAME_H__ + +/** + Retrieves a Unicode string that is the user-readable name of the EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a three-character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + @param DriverName A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpBootDxeComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + @param Language A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + @param ControllerName A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language, from the point of view of the driver specified + by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpBootDxeComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.c new file mode 100644 index 00000000..3814d196 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.c @@ -0,0 +1,700 @@ +/** @file + Helper functions for configuring or getting the parameters relating to HTTP Boot. + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HttpBootDxe.h" +#include <Library/UefiBootManagerLib.h> + +CHAR16 mHttpBootConfigStorageName[] = L"HTTP_BOOT_CONFIG_IFR_NVDATA"; + +/** + Add new boot option for HTTP boot. + + @param[in] Private Pointer to the driver private data. + @param[in] UsingIpv6 Set to TRUE if creating boot option for IPv6. + @param[in] Description The description text of the boot option. + @param[in] Uri The URI string of the boot file. + + @retval EFI_SUCCESS The boot option is created successfully. + @retval Others Failed to create new boot option. + +**/ +EFI_STATUS +HttpBootAddBootOption ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN BOOLEAN UsingIpv6, + IN CHAR16 *Description, + IN CHAR16 *Uri + ) +{ + EFI_DEV_PATH *Node; + EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath; + EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; + UINTN Length; + CHAR8 AsciiUri[URI_STR_MAX_SIZE]; + EFI_STATUS Status; + UINTN Index; + EFI_BOOT_MANAGER_LOAD_OPTION NewOption; + + NewDevicePath = NULL; + Node = NULL; + TmpDevicePath = NULL; + + if (StrLen (Description) == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Convert the scheme to all lower case. + // + for (Index = 0; Index < StrLen (Uri); Index++) { + if (Uri[Index] == L':') { + break; + } + if (Uri[Index] >= L'A' && Uri[Index] <= L'Z') { + Uri[Index] -= (CHAR16)(L'A' - L'a'); + } + } + + // + // Only accept empty URI, or http and https URI. + // + if ((StrLen (Uri) != 0) && (StrnCmp (Uri, L"http://", 7) != 0) && (StrnCmp (Uri, L"https://", 8) != 0)) { + return EFI_INVALID_PARAMETER; + } + + // + // Create a new device path by appending the IP node and URI node to + // the driver's parent device path + // + if (!UsingIpv6) { + Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH)); + if (Node == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH; + Node->Ipv4.Header.SubType = MSG_IPv4_DP; + SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH)); + } else { + Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH)); + if (Node == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + Node->Ipv6.Header.Type = MESSAGING_DEVICE_PATH; + Node->Ipv6.Header.SubType = MSG_IPv6_DP; + SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH)); + } + TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + if (TmpDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Update the URI node with the input boot file URI. + // + UnicodeStrToAsciiStrS (Uri, AsciiUri, sizeof (AsciiUri)); + Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (AsciiUri); + Node = AllocatePool (Length); + if (Node == NULL) { + Status = EFI_OUT_OF_RESOURCES; + FreePool (TmpDevicePath); + goto ON_EXIT; + } + Node->DevPath.Type = MESSAGING_DEVICE_PATH; + Node->DevPath.SubType = MSG_URI_DP; + SetDevicePathNodeLength (Node, Length); + CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), AsciiUri, AsciiStrSize (AsciiUri)); + NewDevicePath = AppendDevicePathNode (TmpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + FreePool (TmpDevicePath); + if (NewDevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Add a new load option. + // + Status = EfiBootManagerInitializeLoadOption ( + &NewOption, + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + LOAD_OPTION_ACTIVE, + Description, + NewDevicePath, + NULL, + 0 + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = EfiBootManagerAddLoadOptionVariable (&NewOption, (UINTN) -1); + EfiBootManagerFreeLoadOption (&NewOption); + +ON_EXIT: + + if (NewDevicePath != NULL) { + FreePool (NewDevicePath); + } + + return Status; +} + +/** + + This function allows the caller to request the current + configuration for one or more named elements. The resulting + string is in <ConfigAltResp> format. Also, any and all alternative + configuration strings shall be appended to the end of the + current configuration string. If they are, they must appear + after the current configuration. They must contain the same + routing (GUID, NAME, PATH) as the current configuration string. + They must have an additional description indicating the type of + alternative configuration the string represents, + "ALTCFG=<StringToken>". That <StringToken> (when + converted from Hex UNICODE to binary) is a reference to a + string in the associated string pack. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + + @param[in] Request A null-terminated Unicode string in + <ConfigRequest> format. Note that this + includes the routing information as well as + the configurable name / value pairs. It is + invalid for this string to be in + <MultiConfigRequest> format. + + @param[out] Progress On return, points to a character in the + Request string. Points to the string's null + terminator if request was successful. Points + to the most recent "&" before the first + failing name / value pair (or the beginning + of the string if the failure is in the first + name / value pair) if the request was not successful. + + @param[out] Results A null-terminated Unicode string in + <ConfigAltResp> format which has all values + filled in for the names in the Request string. + String to be allocated by the called function. + + @retval EFI_SUCCESS The Results string is filled with the + values corresponding to all requested + names. + + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the + parts of the results that must be + stored awaiting possible future + protocols. + + @retval EFI_INVALID_PARAMETER For example, passing in a NULL + for the Request parameter + would result in this type of + error. In this case, the + Progress parameter would be + set to NULL. + + @retval EFI_NOT_FOUND Routing data doesn't match any + known driver. Progress set to the + first character in the routing header. + Note: There is no requirement that the + driver validate the routing data. It + must skip the <ConfigHdr> in order to + process the names. + + @retval EFI_INVALID_PARAMETER Illegal syntax. Progress set + to most recent "&" before the + error or the beginning of the + string. + + @retval EFI_INVALID_PARAMETER Unknown name. Progress points + to the & before the name in + question. + +**/ +EFI_STATUS +EFIAPI +HttpBootFormExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo; + EFI_STRING ConfigRequestHdr; + EFI_STRING ConfigRequest; + BOOLEAN AllocatedRequest; + UINTN Size; + + if (Progress == NULL || Results == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Progress = Request; + if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &gHttpBootConfigGuid, mHttpBootConfigStorageName)) { + return EFI_NOT_FOUND; + } + + ConfigRequestHdr = NULL; + ConfigRequest = NULL; + AllocatedRequest = FALSE; + Size = 0; + + CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This); + // + // Convert buffer data to <ConfigResp> by helper function BlockToConfig() + // + BufferSize = sizeof (HTTP_BOOT_CONFIG_IFR_NVDATA); + ZeroMem (&CallbackInfo->HttpBootNvData, BufferSize); + StrCpyS (CallbackInfo->HttpBootNvData.Description, DESCRIPTION_STR_MAX_SIZE / sizeof (CHAR16), HTTP_BOOT_DEFAULT_DESCRIPTION_STR); + + ConfigRequest = Request; + if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) { + // + // Request has no request element, construct full request string. + // Allocate and fill a buffer large enough to hold the <ConfigHdr> template + // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator + // + ConfigRequestHdr = HiiConstructConfigHdr (&gHttpBootConfigGuid, mHttpBootConfigStorageName, CallbackInfo->ChildHandle); + Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16); + ConfigRequest = AllocateZeroPool (Size); + if (ConfigRequest == NULL) { + return EFI_OUT_OF_RESOURCES; + } + AllocatedRequest = TRUE; + UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize); + FreePool (ConfigRequestHdr); + } + + Status = gHiiConfigRouting->BlockToConfig ( + gHiiConfigRouting, + ConfigRequest, + (UINT8 *) &CallbackInfo->HttpBootNvData, + BufferSize, + Results, + Progress + ); + + // + // Free the allocated config request string. + // + if (AllocatedRequest) { + FreePool (ConfigRequest); + ConfigRequest = NULL; + } + // + // Set Progress string to the original request string. + // + if (Request == NULL) { + *Progress = NULL; + } else if (StrStr (Request, L"OFFSET") == NULL) { + *Progress = Request + StrLen (Request); + } + + return Status; +} + +/** + + This function applies changes in a driver's configuration. + Input is a Configuration, which has the routing data for this + driver followed by name / value configuration pairs. The driver + must apply those pairs to its configurable storage. If the + driver's configuration is stored in a linear block of data + and the driver's name / value pairs are in <BlockConfig> + format, it may use the ConfigToBlock helper function (above) to + simplify the job. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + + @param[in] Configuration A null-terminated Unicode string in + <ConfigString> format. + + @param[out] Progress A pointer to a string filled in with the + offset of the most recent '&' before the + first failing name / value pair (or the + beginning of the string if the failure + is in the first name / value pair) or + the terminating NULL if all was + successful. + + @retval EFI_SUCCESS The results have been distributed or are + awaiting distribution. + + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the + parts of the results that must be + stored awaiting possible future + protocols. + + @retval EFI_INVALID_PARAMETERS Passing in a NULL for the + Results parameter would result + in this type of error. + + @retval EFI_NOT_FOUND Target for the specified routing data + was not found. + +**/ +EFI_STATUS +EFIAPI +HttpBootFormRouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo; + HTTP_BOOT_PRIVATE_DATA *Private; + + if (Progress == NULL) { + return EFI_INVALID_PARAMETER; + } + *Progress = Configuration; + + if (Configuration == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check routing data in <ConfigHdr>. + // Note: there is no name for Name/Value storage, only GUID will be checked + // + if (!HiiIsConfigHdrMatch (Configuration, &gHttpBootConfigGuid, mHttpBootConfigStorageName)) { + return EFI_NOT_FOUND; + } + + CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This); + Private = HTTP_BOOT_PRIVATE_DATA_FROM_CALLBACK_INFO (CallbackInfo); + + BufferSize = sizeof (HTTP_BOOT_CONFIG_IFR_NVDATA); + ZeroMem (&CallbackInfo->HttpBootNvData, BufferSize); + + Status = gHiiConfigRouting->ConfigToBlock ( + gHiiConfigRouting, + Configuration, + (UINT8 *) &CallbackInfo->HttpBootNvData, + &BufferSize, + Progress + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Create a new boot option according to the configuration data. + // + HttpBootAddBootOption ( + Private, + (CallbackInfo->HttpBootNvData.IpVersion == HTTP_BOOT_IP_VERSION_6) ? TRUE : FALSE, + CallbackInfo->HttpBootNvData.Description, + CallbackInfo->HttpBootNvData.Uri + ); + + return EFI_SUCCESS; +} + +/** + + This function is called to provide results data to the driver. + This data consists of a unique key that is used to identify + which data is either being passed back or being asked for. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Action Specifies the type of action taken by the browser. + @param[in] QuestionId A unique value which is sent to the original + exporting driver so that it can identify the type + of data to expect. The format of the data tends to + vary based on the opcode that generated the callback. + @param[in] Type The type of value for the question. + @param[in, out] Value A pointer to the data being sent to the original + exporting driver. + @param[out] ActionRequest On return, points to the action requested by the + callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the + variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be saved. + @retval EFI_UNSUPPORTED The specified Action is not supported by the + callback. +**/ +EFI_STATUS +EFIAPI +HttpBootFormCallback ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN EFI_BROWSER_ACTION Action, + IN EFI_QUESTION_ID QuestionId, + IN UINT8 Type, + IN OUT EFI_IFR_TYPE_VALUE *Value, + OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest + ) +{ + EFI_INPUT_KEY Key; + CHAR16 *Uri; + UINTN UriLen; + CHAR8 *AsciiUri; + HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo; + EFI_STATUS Status; + + Uri = NULL; + UriLen = 0; + AsciiUri = NULL; + Status = EFI_SUCCESS; + + if (This == NULL || Value == NULL) { + return EFI_INVALID_PARAMETER; + } + + CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This); + + if (Action != EFI_BROWSER_ACTION_CHANGING) { + return EFI_UNSUPPORTED; + } + + switch (QuestionId) { + case KEY_INITIATOR_URI: + // + // Get user input URI string + // + Uri = HiiGetString (CallbackInfo->RegisteredHandle, Value->string, NULL); + if(Uri == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // The URI should be either an empty string (for corporate environment) ,or http(s) for home environment. + // Pop up a message box for the unsupported URI. + // + if (StrLen (Uri) != 0) { + UriLen = StrLen (Uri) + 1; + AsciiUri = AllocateZeroPool (UriLen); + if (AsciiUri == NULL) { + FreePool (Uri); + return EFI_OUT_OF_RESOURCES; + } + + UnicodeStrToAsciiStrS (Uri, AsciiUri, UriLen); + + Status = HttpBootCheckUriScheme (AsciiUri); + + if (Status == EFI_INVALID_PARAMETER) { + + DEBUG ((EFI_D_ERROR, "HttpBootFormCallback: %r.\n", Status)); + + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"ERROR: Unsupported URI!", + L"Only supports HTTP and HTTPS", + NULL + ); + } else if (Status == EFI_ACCESS_DENIED) { + + DEBUG ((EFI_D_ERROR, "HttpBootFormCallback: %r.\n", Status)); + + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"ERROR: Unsupported URI!", + L"HTTP is disabled", + NULL + ); + } + } + + if (Uri != NULL) { + FreePool (Uri); + } + + if (AsciiUri != NULL) { + FreePool (AsciiUri); + } + + break; + + default: + break; + } + + return Status; +} + +/** + Initialize the configuration form. + + @param[in] Private Pointer to the driver private data. + + @retval EFI_SUCCESS The configuration form is initialized. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + +**/ +EFI_STATUS +HttpBootConfigFormInit ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo; + VENDOR_DEVICE_PATH VendorDeviceNode; + CHAR16 *MacString; + CHAR16 *OldMenuString; + CHAR16 MenuString[128]; + + CallbackInfo = &Private->CallbackInfo; + + if (CallbackInfo->Initialized) { + return EFI_SUCCESS; + } + + CallbackInfo->Signature = HTTP_BOOT_FORM_CALLBACK_INFO_SIGNATURE; + + // + // Construct device path node for EFI HII Config Access protocol, + // which consists of controller physical device path and one hardware + // vendor guid node. + // + ZeroMem (&VendorDeviceNode, sizeof (VENDOR_DEVICE_PATH)); + VendorDeviceNode.Header.Type = HARDWARE_DEVICE_PATH; + VendorDeviceNode.Header.SubType = HW_VENDOR_DP; + CopyGuid (&VendorDeviceNode.Guid, &gEfiCallerIdGuid); + SetDevicePathNodeLength (&VendorDeviceNode.Header, sizeof (VENDOR_DEVICE_PATH)); + CallbackInfo->HiiVendorDevicePath = AppendDevicePathNode ( + Private->ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &VendorDeviceNode + ); + if (CallbackInfo->HiiVendorDevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + CallbackInfo->ConfigAccess.ExtractConfig = HttpBootFormExtractConfig; + CallbackInfo->ConfigAccess.RouteConfig = HttpBootFormRouteConfig; + CallbackInfo->ConfigAccess.Callback = HttpBootFormCallback; + + // + // Install Device Path Protocol and Config Access protocol to driver handle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &CallbackInfo->ChildHandle, + &gEfiDevicePathProtocolGuid, + CallbackInfo->HiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &CallbackInfo->ConfigAccess, + NULL + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Publish our HII data. + // + CallbackInfo->RegisteredHandle = HiiAddPackages ( + &gHttpBootConfigGuid, + CallbackInfo->ChildHandle, + HttpBootDxeStrings, + HttpBootConfigVfrBin, + NULL + ); + if (CallbackInfo->RegisteredHandle == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + // + // Append MAC string in the menu help string + // + Status = NetLibGetMacString (Private->Controller, NULL, &MacString); + if (!EFI_ERROR (Status)) { + OldMenuString = HiiGetString ( + CallbackInfo->RegisteredHandle, + STRING_TOKEN (STR_HTTP_BOOT_CONFIG_FORM_HELP), + NULL + ); + UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString); + HiiSetString ( + CallbackInfo->RegisteredHandle, + STRING_TOKEN (STR_HTTP_BOOT_CONFIG_FORM_HELP), + MenuString, + NULL + ); + + FreePool (MacString); + FreePool (OldMenuString); + + CallbackInfo->Initialized = TRUE; + return EFI_SUCCESS; + } + +Error: + + HttpBootConfigFormUnload (Private); + return Status; +} + +/** + Unload the configuration form, this includes: delete all the configuration + entries, uninstall the form callback protocol, and free the resources used. + The form will only be unload completely when both IP4 and IP6 stack are stopped. + + @param[in] Private Pointer to the driver private data. + + @retval EFI_SUCCESS The configuration form is unloaded. + @retval Others Failed to unload the form. + +**/ +EFI_STATUS +HttpBootConfigFormUnload ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo; + + if (Private->Ip4Nic != NULL || Private->Ip6Nic != NULL) { + // + // Only unload the configuration form when both IP4 and IP6 stack are stopped. + // + return EFI_SUCCESS; + } + + CallbackInfo = &Private->CallbackInfo; + if (CallbackInfo->ChildHandle != NULL) { + // + // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL + // + gBS->UninstallMultipleProtocolInterfaces ( + CallbackInfo->ChildHandle, + &gEfiDevicePathProtocolGuid, + CallbackInfo->HiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &CallbackInfo->ConfigAccess, + NULL + ); + CallbackInfo->ChildHandle = NULL; + } + + if (CallbackInfo->HiiVendorDevicePath != NULL) { + FreePool (CallbackInfo->HiiVendorDevicePath); + CallbackInfo->HiiVendorDevicePath = NULL; + } + + if (CallbackInfo->RegisteredHandle != NULL) { + // + // Remove HII package list + // + HiiRemovePackages (CallbackInfo->RegisteredHandle); + CallbackInfo->RegisteredHandle = NULL; + } + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.h new file mode 100644 index 00000000..c94ae8c0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.h @@ -0,0 +1,73 @@ +/** @file + The header file of functions for configuring or getting the parameters + relating to HTTP Boot. + +Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _HTTP_BOOT_CONFIG_H_ +#define _HTTP_BOOT_CONFIG_H_ + + +#include "HttpBootConfigNVDataStruc.h" + +typedef struct _HTTP_BOOT_FORM_CALLBACK_INFO HTTP_BOOT_FORM_CALLBACK_INFO; + +extern UINT8 HttpBootDxeStrings[]; +extern UINT8 HttpBootConfigVfrBin[]; + +#pragma pack() + +#define HTTP_BOOT_FORM_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('H', 'B', 'f', 'c') + +#define HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(Callback) \ + CR ( \ + Callback, \ + HTTP_BOOT_FORM_CALLBACK_INFO, \ + ConfigAccess, \ + HTTP_BOOT_FORM_CALLBACK_INFO_SIGNATURE \ + ) + +struct _HTTP_BOOT_FORM_CALLBACK_INFO { + UINT32 Signature; + BOOLEAN Initialized; + EFI_HANDLE ChildHandle; + EFI_DEVICE_PATH_PROTOCOL *HiiVendorDevicePath; + EFI_HII_HANDLE RegisteredHandle; + EFI_HII_CONFIG_ACCESS_PROTOCOL ConfigAccess; + HTTP_BOOT_CONFIG_IFR_NVDATA HttpBootNvData; +}; + +/** + Initialize the configuration form. + + @param[in] Private Pointer to the driver private data. + + @retval EFI_SUCCESS The configuration form is initialized. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + +**/ +EFI_STATUS +HttpBootConfigFormInit ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +/** + Unload the configuration form, this includes: delete all the configuration + entries, uninstall the form callback protocol, and free the resources used. + The form will only be unload completely when both IP4 and IP6 stack are stopped. + + @param[in] Private Pointer to the driver private data. + + @retval EFI_SUCCESS The configuration form is unloaded. + @retval Others Failed to unload the form. + +**/ +EFI_STATUS +HttpBootConfigFormUnload ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigNVDataStruc.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigNVDataStruc.h new file mode 100644 index 00000000..19d7e452 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigNVDataStruc.h @@ -0,0 +1,44 @@ +/** @file + Define NVData structures used by the HTTP Boot configuration component. + +Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _HTTP_BOOT_NVDATA_STRUC_H_ +#define _HTTP_BOOT_NVDATA_STRUC_H_ + +#include <Guid/HttpBootConfigHii.h> + +#define HTTP_BOOT_IP_VERSION_4 0 +#define HTTP_BOOT_IP_VERSION_6 1 + +// +// Macros used for an IPv4 or an IPv6 address. +// +#define URI_STR_MIN_SIZE 0 +#define URI_STR_MAX_SIZE 255 + +#define DESCRIPTION_STR_MIN_SIZE 6 +#define DESCRIPTION_STR_MAX_SIZE 75 + +#define CONFIGURATION_VARSTORE_ID 0x1234 + +#define FORMID_MAIN_FORM 1 + +#define KEY_INITIATOR_URI 0x101 + +#define HTTP_BOOT_DEFAULT_DESCRIPTION_STR L"UEFI HTTP" + +#pragma pack(1) +typedef struct _HTTP_BOOT_CONFIG_IFR_NVDATA { + UINT8 IpVersion; + UINT8 Padding; + CHAR16 Description[DESCRIPTION_STR_MAX_SIZE]; + CHAR16 Uri[URI_STR_MAX_SIZE]; +} HTTP_BOOT_CONFIG_IFR_NVDATA; +#pragma pack() + + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigStrings.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigStrings.uni new file mode 100644 index 00000000..40abb13d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigStrings.uni @@ -0,0 +1,21 @@ +/** @file
+ String definitions for HTTP Boot configuration.
+
+ Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#langdef en-US "English"
+
+#string STR_HTTP_BOOT_CONFIG_FORM_TITLE #language en-US "HTTP Boot Configuration"
+#string STR_HTTP_BOOT_CONFIG_FORM_HELP #language en-US "Configure HTTP Boot parameters."
+#string STR_HTTP_BOOT_IP_VERSION_PROMPT #language en-US "Internet Protocol"
+#string STR_HTTP_BOOT_IP_VERSION_HELP #language en-US "Select the version of Internet Protocol."
+#string STR_HTTP_BOOT_IP_VERSION_4 #language en-US "IP4"
+#string STR_HTTP_BOOT_IP_VERSION_6 #language en-US "IP6"
+#string STR_BOOT_URI_PROMPT #language en-US "Boot URI"
+#string STR_BOOT_URI_HELP #language en-US "A new Boot Option will be created according to this Boot URI."
+#string STR_BOOT_DESCRIPTION_PROMPT #language en-US "Input the description"
+#string STR_NULL_STRING #language en-US ""
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigVfr.vfr b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigVfr.vfr new file mode 100644 index 00000000..0a59287c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigVfr.vfr @@ -0,0 +1,49 @@ +/** @file + VFR file used by the HTTP Boot configuration component. + + Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HttpBootConfigNVDataStruc.h" + + +formset + guid = HTTP_BOOT_CONFIG_GUID, + title = STRING_TOKEN(STR_HTTP_BOOT_CONFIG_FORM_TITLE), + help = STRING_TOKEN(STR_HTTP_BOOT_CONFIG_FORM_HELP), + + varstore HTTP_BOOT_CONFIG_IFR_NVDATA, + name = HTTP_BOOT_CONFIG_IFR_NVDATA, + guid = HTTP_BOOT_CONFIG_GUID; + + form formid = FORMID_MAIN_FORM, + title = STRING_TOKEN(STR_HTTP_BOOT_CONFIG_FORM_TITLE); + + string varid = HTTP_BOOT_CONFIG_IFR_NVDATA.Description, + prompt = STRING_TOKEN(STR_BOOT_DESCRIPTION_PROMPT), + help = STRING_TOKEN(STR_NULL_STRING), + minsize = DESCRIPTION_STR_MIN_SIZE, + maxsize = DESCRIPTION_STR_MAX_SIZE, + endstring; + + oneof varid = HTTP_BOOT_CONFIG_IFR_NVDATA.IpVersion, + prompt = STRING_TOKEN(STR_HTTP_BOOT_IP_VERSION_PROMPT), + help = STRING_TOKEN(STR_HTTP_BOOT_IP_VERSION_HELP), + option text = STRING_TOKEN(STR_HTTP_BOOT_IP_VERSION_4), value = HTTP_BOOT_IP_VERSION_4, flags = DEFAULT; + option text = STRING_TOKEN(STR_HTTP_BOOT_IP_VERSION_6), value = HTTP_BOOT_IP_VERSION_6, flags = 0; + endoneof; + + string varid = HTTP_BOOT_CONFIG_IFR_NVDATA.Uri, + prompt = STRING_TOKEN(STR_BOOT_URI_PROMPT), + help = STRING_TOKEN(STR_BOOT_URI_HELP), + flags = INTERACTIVE, + key = KEY_INITIATOR_URI, + minsize = URI_STR_MIN_SIZE, + maxsize = URI_STR_MAX_SIZE, + endstring; + endform; + +endformset; diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c new file mode 100644 index 00000000..1d275ae0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c @@ -0,0 +1,912 @@ +/** @file + Functions implementation related with DHCPv4 for HTTP boot driver. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HttpBootDxe.h" + +// +// This is a map from the interested DHCP4 option tags' index to the tag value. +// +UINT8 mInterestedDhcp4Tags[HTTP_BOOT_DHCP4_TAG_INDEX_MAX] = { + DHCP4_TAG_BOOTFILE_LEN, + DHCP4_TAG_OVERLOAD, + DHCP4_TAG_MSG_TYPE, + DHCP4_TAG_SERVER_ID, + DHCP4_TAG_VENDOR_CLASS_ID, + DHCP4_TAG_BOOTFILE, + DHCP4_TAG_DNS_SERVER +}; + +// +// There are 4 times retries with the value of 4, 8, 16 and 32, refers to UEFI 2.5 spec. +// +UINT32 mHttpDhcpTimeout[4] = {4, 8, 16, 32}; + +/** + Build the options buffer for the DHCPv4 request packet. + + @param[in] Private Pointer to HTTP boot driver private data. + @param[out] OptList Pointer to the option pointer array. + @param[in] Buffer Pointer to the buffer to contain the option list. + + @return Index The count of the built-in options. + +**/ +UINT32 +HttpBootBuildDhcp4Options ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + OUT EFI_DHCP4_PACKET_OPTION **OptList, + IN UINT8 *Buffer + ) +{ + HTTP_BOOT_DHCP4_OPTION_ENTRY OptEnt; + UINT16 Value; + UINT32 Index; + + Index = 0; + OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer; + + // + // Append parameter request list option. + // + OptList[Index]->OpCode = DHCP4_TAG_PARA_LIST; + OptList[Index]->Length = 27; + OptEnt.Para = (HTTP_BOOT_DHCP4_OPTION_PARA *) OptList[Index]->Data; + OptEnt.Para->ParaList[0] = DHCP4_TAG_NETMASK; + OptEnt.Para->ParaList[1] = DHCP4_TAG_TIME_OFFSET; + OptEnt.Para->ParaList[2] = DHCP4_TAG_ROUTER; + OptEnt.Para->ParaList[3] = DHCP4_TAG_TIME_SERVER; + OptEnt.Para->ParaList[4] = DHCP4_TAG_NAME_SERVER; + OptEnt.Para->ParaList[5] = DHCP4_TAG_DNS_SERVER; + OptEnt.Para->ParaList[6] = DHCP4_TAG_HOSTNAME; + OptEnt.Para->ParaList[7] = DHCP4_TAG_BOOTFILE_LEN; + OptEnt.Para->ParaList[8] = DHCP4_TAG_DOMAINNAME; + OptEnt.Para->ParaList[9] = DHCP4_TAG_ROOTPATH; + OptEnt.Para->ParaList[10] = DHCP4_TAG_EXTEND_PATH; + OptEnt.Para->ParaList[11] = DHCP4_TAG_EMTU; + OptEnt.Para->ParaList[12] = DHCP4_TAG_TTL; + OptEnt.Para->ParaList[13] = DHCP4_TAG_BROADCAST; + OptEnt.Para->ParaList[14] = DHCP4_TAG_NIS_DOMAIN; + OptEnt.Para->ParaList[15] = DHCP4_TAG_NIS_SERVER; + OptEnt.Para->ParaList[16] = DHCP4_TAG_NTP_SERVER; + OptEnt.Para->ParaList[17] = DHCP4_TAG_VENDOR; + OptEnt.Para->ParaList[18] = DHCP4_TAG_REQUEST_IP; + OptEnt.Para->ParaList[19] = DHCP4_TAG_LEASE; + OptEnt.Para->ParaList[20] = DHCP4_TAG_SERVER_ID; + OptEnt.Para->ParaList[21] = DHCP4_TAG_T1; + OptEnt.Para->ParaList[22] = DHCP4_TAG_T2; + OptEnt.Para->ParaList[23] = DHCP4_TAG_VENDOR_CLASS_ID; + OptEnt.Para->ParaList[25] = DHCP4_TAG_BOOTFILE; + OptEnt.Para->ParaList[26] = DHCP4_TAG_UUID; + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append UUID/Guid-based client identifier option + // + OptList[Index]->OpCode = DHCP4_TAG_UUID; + OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_UUID); + OptEnt.Uuid = (HTTP_BOOT_DHCP4_OPTION_UUID *) OptList[Index]->Data; + OptEnt.Uuid->Type = 0; + if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) { + // + // Zero the Guid to indicate NOT programmable if failed to get system Guid. + // + ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID)); + } + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append client network device interface option + // + OptList[Index]->OpCode = DHCP4_TAG_UNDI; + OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_UNDI); + OptEnt.Undi = (HTTP_BOOT_DHCP4_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_DHCP_OPTION (OptList[Index - 1]); + + // + // Append client system architecture option + // + OptList[Index]->OpCode = DHCP4_TAG_ARCH; + OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_ARCH); + OptEnt.Arch = (HTTP_BOOT_DHCP4_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_DHCP_OPTION (OptList[Index - 1]); + + // + // Append vendor class identify option + // + OptList[Index]->OpCode = DHCP4_TAG_VENDOR_CLASS_ID; + OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_CLID); + OptEnt.Clid = (HTTP_BOOT_DHCP4_OPTION_CLID *) OptList[Index]->Data; + CopyMem ( + OptEnt.Clid, + DEFAULT_CLASS_ID_DATA, + sizeof (HTTP_BOOT_DHCP4_OPTION_CLID) + ); + HttpBootUintnToAscDecWithFormat ( + EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE, + OptEnt.Clid->ArchitectureType, + sizeof (OptEnt.Clid->ArchitectureType) + ); + + if (Private->Nii != NULL) { + CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName)); + HttpBootUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor)); + HttpBootUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor)); + } + + Index++; + + return Index; +} + +/** + Parse a certain dhcp4 option by OptTag in Buffer, and return with start pointer. + + @param[in] Buffer Pointer to the option buffer. + @param[in] Length Length of the option buffer. + @param[in] OptTag Tag of the required option. + + @retval NULL Failed to find the required option. + @retval Others The position of the required option. + +**/ +EFI_DHCP4_PACKET_OPTION * +HttpBootParseDhcp4Options ( + IN UINT8 *Buffer, + IN UINT32 Length, + IN UINT8 OptTag + ) +{ + EFI_DHCP4_PACKET_OPTION *Option; + UINT32 Offset; + + Option = (EFI_DHCP4_PACKET_OPTION *) Buffer; + Offset = 0; + + while (Offset < Length && Option->OpCode != DHCP4_TAG_EOP) { + + if (Option->OpCode == OptTag) { + // + // Found the required option. + // + return Option; + } + + // + // Skip the current option to the next. + // + if (Option->OpCode == DHCP4_TAG_PAD) { + Offset++; + } else { + Offset += Option->Length + 2; + } + + Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset); + } + + return NULL; +} + +/** + Cache the DHCPv4 packet. + + @param[in] Dst Pointer to the cache buffer for DHCPv4 packet. + @param[in] Src Pointer to the DHCPv4 packet to be cached. + + @retval EFI_SUCCESS Packet is copied. + @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet. + +**/ +EFI_STATUS +HttpBootCacheDhcp4Packet ( + IN EFI_DHCP4_PACKET *Dst, + IN EFI_DHCP4_PACKET *Src + ) +{ + if (Dst->Size < Src->Length) { + return EFI_BUFFER_TOO_SMALL; + } + + CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length); + Dst->Length = Src->Length; + + return EFI_SUCCESS; +} + +/** + Parse the cached DHCPv4 packet, including all the options. + + @param[in] Cache4 Pointer to cached DHCPv4 packet. + + @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse an invalid packet. + +**/ +EFI_STATUS +HttpBootParseDhcp4Packet ( + IN HTTP_BOOT_DHCP4_PACKET_CACHE *Cache4 + ) +{ + EFI_DHCP4_PACKET *Offer; + EFI_DHCP4_PACKET_OPTION **Options; + UINTN Index; + EFI_DHCP4_PACKET_OPTION *Option; + BOOLEAN IsProxyOffer; + BOOLEAN IsHttpOffer; + BOOLEAN IsDnsOffer; + BOOLEAN IpExpressedUri; + UINT8 *Ptr8; + EFI_STATUS Status; + HTTP_BOOT_OFFER_TYPE OfferType; + EFI_IPv4_ADDRESS IpAddr; + BOOLEAN FileFieldOverloaded; + + IsDnsOffer = FALSE; + IpExpressedUri = FALSE; + IsProxyOffer = FALSE; + IsHttpOffer = FALSE; + FileFieldOverloaded = FALSE; + + ZeroMem (Cache4->OptList, sizeof (Cache4->OptList)); + + Offer = &Cache4->Packet.Offer; + Options = Cache4->OptList; + + // + // Parse DHCPv4 options in this offer, and store the pointers. + // First, try to parse DHCPv4 options from the DHCP optional parameters field. + // + for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) { + Options[Index] = HttpBootParseDhcp4Options ( + Offer->Dhcp4.Option, + GET_OPTION_BUFFER_LEN (Offer), + mInterestedDhcp4Tags[Index] + ); + } + // + // Second, Check if bootfilename and serverhostname is overloaded to carry DHCP options refers to rfc-2132. + // If yes, try to parse options from the BootFileName field, then ServerName field. + // + Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_OVERLOAD]; + if (Option != NULL) { + if ((Option->Data[0] & HTTP_BOOT_DHCP4_OVERLOAD_FILE) != 0) { + FileFieldOverloaded = TRUE; + for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) { + if (Options[Index] == NULL) { + Options[Index] = HttpBootParseDhcp4Options ( + (UINT8 *) Offer->Dhcp4.Header.BootFileName, + sizeof (Offer->Dhcp4.Header.BootFileName), + mInterestedDhcp4Tags[Index] + ); + } + } + } + if ((Option->Data[0] & HTTP_BOOT_DHCP4_OVERLOAD_SERVER_NAME) != 0) { + for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) { + if (Options[Index] == NULL) { + Options[Index] = HttpBootParseDhcp4Options ( + (UINT8 *) Offer->Dhcp4.Header.ServerName, + sizeof (Offer->Dhcp4.Header.ServerName), + mInterestedDhcp4Tags[Index] + ); + } + } + } + } + + // + // The offer with "yiaddr" is a proxy offer. + // + if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) { + IsProxyOffer = TRUE; + } + + // + // The offer with "HTTPClient" is a Http offer. + // + Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_CLASS_ID]; + if ((Option != NULL) && (Option->Length >= 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_DHCP4_TAG_INDEX_DNS_SERVER]; + if (Option != NULL) { + IsDnsOffer = TRUE; + } + + // + // Parse boot file name: + // Boot URI information is provided thru 'file' field in DHCP Header or option 67. + // According to RFC 2132, boot file name should be read from DHCP option 67 (bootfile name) if present. + // Otherwise, read from boot file field in DHCP header. + // + if (Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { + // + // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null + // terminated string. So force to append null terminated character at the end of string. + // + Ptr8 = (UINT8*)&Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data[0]; + Ptr8 += Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Length; + if (*(Ptr8 - 1) != '\0') { + *Ptr8 = '\0'; + } + } else if (!FileFieldOverloaded && Offer->Dhcp4.Header.BootFileName[0] != 0) { + // + // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it. + // Do not count dhcp option header here, or else will destroy the serverhostname. + // + Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *) + (&Offer->Dhcp4.Header.BootFileName[0] - + OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0])); + } + + // + // Http offer must have a boot URI. + // + if (IsHttpOffer && Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // Try to retrieve the IP of HTTP server from URI. + // + if (IsHttpOffer) { + Status = HttpParseUrl ( + (CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data, + (UINT32) AsciiStrLen ((CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data), + FALSE, + &Cache4->UriParser + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Status = HttpUrlGetIp4 ( + (CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data, + Cache4->UriParser, + &IpAddr + ); + IpExpressedUri = !EFI_ERROR (Status); + } + + // + // Determine offer type of the DHCPv4 packet. + // + if (IsHttpOffer) { + if (IpExpressedUri) { + if (IsProxyOffer) { + OfferType = HttpOfferTypeProxyIpUri; + } else { + OfferType = IsDnsOffer ? HttpOfferTypeDhcpIpUriDns : HttpOfferTypeDhcpIpUri; + } + } else { + if (!IsProxyOffer) { + OfferType = IsDnsOffer ? HttpOfferTypeDhcpNameUriDns : HttpOfferTypeDhcpNameUri; + } else { + OfferType = HttpOfferTypeProxyNameUri; + } + } + + } else { + if (!IsProxyOffer) { + OfferType = IsDnsOffer ? HttpOfferTypeDhcpDns : HttpOfferTypeDhcpOnly; + } else { + if (Cache4->UriParser != NULL) { + FreePool (Cache4->UriParser); + } + return EFI_DEVICE_ERROR; + } + } + + Cache4->OfferType = OfferType; + return EFI_SUCCESS; +} + +/** + Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount. + + @param[in] Private Pointer to HTTP boot driver private data. + @param[in] RcvdOffer Pointer to the received offer packet. + + @retval EFI_SUCCESS Cache and parse the packet successfully. + @retval Others Operation failed. +**/ +EFI_STATUS +HttpBootCacheDhcp4Offer ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN EFI_DHCP4_PACKET *RcvdOffer + ) +{ + HTTP_BOOT_DHCP4_PACKET_CACHE *Cache4; + EFI_DHCP4_PACKET *Offer; + HTTP_BOOT_OFFER_TYPE OfferType; + EFI_STATUS Status; + + ASSERT (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM); + Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4; + Offer = &Cache4->Packet.Offer; + + // + // Cache the content of DHCPv4 packet firstly. + // + Status = HttpBootCacheDhcp4Packet (Offer, RcvdOffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Validate the DHCPv4 packet, and parse the options and offer type. + // + if (EFI_ERROR (HttpBootParseDhcp4Packet (Cache4))) { + return EFI_ABORTED; + } + + // + // Determine whether cache the current offer by type, and record OfferIndex and OfferCount. + // + OfferType = Cache4->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++; + + return EFI_SUCCESS; +} + +/** + 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 + ) +{ + Private->SelectIndex = 0; + Private->SelectProxyType = HttpOfferTypeMax; + + if (Private->FilePathUri != NULL) { + // + // We are in home environment, the URI is already specified. + // Just need to choose a DHCP offer. + // The offer with DNS server address takes priority here. + // + if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1; + + } else if (Private->OfferCount[HttpOfferTypeDhcpIpUriDns] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUriDns][0] + 1; + + } else if (Private->OfferCount[HttpOfferTypeDhcpNameUriDns] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpNameUriDns][0] + 1; + + } else if (Private->OfferCount[HttpOfferTypeDhcpOnly] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpOnly][0] + 1; + + } else if (Private->OfferCount[HttpOfferTypeDhcpIpUri] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUri][0] + 1; + } + + } else { + // + // We are in corporate environment. + // + // Priority1: HttpOfferTypeDhcpIpUri or HttpOfferTypeDhcpIpUriDns + // Priority2: HttpOfferTypeDhcpNameUriDns + // Priority3: HttpOfferTypeDhcpOnly + HttpOfferTypeProxyIpUri + // Priority4: HttpOfferTypeDhcpDns + HttpOfferTypeProxyIpUri + // Priority5: HttpOfferTypeDhcpDns + HttpOfferTypeProxyNameUri + // Priority6: HttpOfferTypeDhcpDns + HttpOfferTypeDhcpNameUri + // + if (Private->OfferCount[HttpOfferTypeDhcpIpUri] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUri][0] + 1; + + } else if (Private->OfferCount[HttpOfferTypeDhcpIpUriDns] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUriDns][0] + 1; + + }else if (Private->OfferCount[HttpOfferTypeDhcpNameUriDns] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpNameUriDns][0] + 1; + + } else if (Private->OfferCount[HttpOfferTypeDhcpOnly] > 0 && + Private->OfferCount[HttpOfferTypeProxyIpUri] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = HttpOfferTypeProxyIpUri; + + } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 && + Private->OfferCount[HttpOfferTypeProxyIpUri] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1; + Private->SelectProxyType = HttpOfferTypeProxyIpUri; + + } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 && + Private->OfferCount[HttpOfferTypeProxyNameUri] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1; + Private->SelectProxyType = HttpOfferTypeProxyNameUri; + + } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 && + Private->OfferCount[HttpOfferTypeDhcpNameUri] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1; + Private->SelectProxyType = HttpOfferTypeDhcpNameUri; + } + } +} + + +/** + EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver + to intercept events that occurred in the configuration process. + + @param[in] This Pointer to the EFI DHCPv4 Protocol. + @param[in] Context Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure(). + @param[in] CurrentState The current operational state of the EFI DHCPv4 Protocol driver. + @param[in] Dhcp4Event The event that occurs in the current state, which usually means a + state transition. + @param[in] Packet The DHCPv4 packet that is going to be sent or already received. + @param[out] NewPacket The packet that is used to replace the above Packet. + + @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process. + @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol + driver will continue to wait for more DHCPOFFER packets until the + retry timeout expires. + @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process + and return to the Dhcp4Init or Dhcp4InitReboot state. + +**/ +EFI_STATUS +EFIAPI +HttpBootDhcp4CallBack ( + IN EFI_DHCP4_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP4_STATE CurrentState, + IN EFI_DHCP4_EVENT Dhcp4Event, + IN EFI_DHCP4_PACKET *Packet OPTIONAL, + OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL + ) +{ + HTTP_BOOT_PRIVATE_DATA *Private; + EFI_DHCP4_PACKET_OPTION *MaxMsgSize; + UINT16 Value; + EFI_STATUS Status; + BOOLEAN Received; + + if ((Dhcp4Event != Dhcp4SendDiscover) && + (Dhcp4Event != Dhcp4RcvdOffer) && + (Dhcp4Event != Dhcp4SendRequest) && + (Dhcp4Event != Dhcp4RcvdAck) && + (Dhcp4Event != Dhcp4SelectOffer)) { + return EFI_SUCCESS; + } + + Private = (HTTP_BOOT_PRIVATE_DATA *) Context; + + // + // Override the Maximum DHCP Message Size. + // + MaxMsgSize = HttpBootParseDhcp4Options ( + Packet->Dhcp4.Option, + GET_OPTION_BUFFER_LEN (Packet), + DHCP4_TAG_MAXMSG + ); + if (MaxMsgSize != NULL) { + Value = HTONS (HTTP_BOOT_DHCP4_PACKET_MAX_SIZE); + CopyMem (MaxMsgSize->Data, &Value, sizeof (Value)); + } + + // + // Callback to user if any packets sent or received. + // + if (Private->HttpBootCallback != NULL && Dhcp4Event != Dhcp4SelectOffer) { + Received = (BOOLEAN) (Dhcp4Event == Dhcp4RcvdOffer || Dhcp4Event == Dhcp4RcvdAck); + Status = Private->HttpBootCallback->Callback ( + Private->HttpBootCallback, + HttpBootDhcp4, + Received, + Packet->Length, + &Packet->Dhcp4 + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + } + + Status = EFI_SUCCESS; + switch (Dhcp4Event) { + case Dhcp4RcvdOffer: + Status = EFI_NOT_READY; + if (Packet->Length > HTTP_BOOT_DHCP4_PACKET_MAX_SIZE) { + // + // Ignore the incoming packets which exceed the maximum length. + // + break; + } + if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) { + // + // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record + // the OfferIndex and OfferCount. + // If error happens, just ignore this packet and continue to wait more offer. + // + HttpBootCacheDhcp4Offer (Private, Packet); + } + break; + + case Dhcp4SelectOffer: + // + // Select offer according to the priority in UEFI spec, and record the SelectIndex + // and SelectProxyType. + // + HttpBootSelectDhcpOffer (Private); + + if (Private->SelectIndex == 0) { + Status = EFI_ABORTED; + } else { + *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer; + } + break; + + default: + break; + } + + return Status; +} + +/** + This function will register the IPv4 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 +HttpBootRegisterIp4Gateway ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + + ASSERT (!Private->UsingIpv6); + + Ip4Config2 = Private->Ip4Config2; + + // + // Configure the gateway if valid. + // + if (!EFI_IP4_EQUAL (&Private->GatewayIp, &mZeroIp4Addr)) { + Status = Ip4Config2->SetData ( + Ip4Config2, + Ip4Config2DataTypeGateway, + sizeof (EFI_IPv4_ADDRESS), + &Private->GatewayIp + ); + 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_IPv4_ADDRESS instances. + + @retval EFI_SUCCESS The DNS configuration has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootRegisterIp4Dns ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN UINTN DataLength, + IN VOID *DnsServerData + ) +{ + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + + ASSERT (!Private->UsingIpv6); + + Ip4Config2 = Private->Ip4Config2; + + return Ip4Config2->SetData ( + Ip4Config2, + Ip4Config2DataTypeDnsServer, + DataLength, + DnsServerData + ); +} + + +/** + This function will switch the IP4 configuration policy to Static. + + @param[in] Private Pointer to HTTP boot driver private data. + + @retval EFI_SUCCESS The policy is already configured to static. + @retval Others Other error as indicated.. + +**/ +EFI_STATUS +HttpBootSetIp4Policy ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_IP4_CONFIG2_POLICY Policy; + EFI_STATUS Status; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + UINTN DataSize; + + Ip4Config2 = Private->Ip4Config2; + + DataSize = sizeof (EFI_IP4_CONFIG2_POLICY); + Status = Ip4Config2->GetData ( + Ip4Config2, + Ip4Config2DataTypePolicy, + &DataSize, + &Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Policy != Ip4Config2PolicyStatic) { + Policy = Ip4Config2PolicyStatic; + Status= Ip4Config2->SetData ( + Ip4Config2, + Ip4Config2DataTypePolicy, + sizeof (EFI_IP4_CONFIG2_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other Http boot information. + + @param[in] Private Pointer to HTTP boot driver private data. + + @retval EFI_SUCCESS The D.O.R.A process successfully finished. + @retval Others Failed to finish the D.O.R.A process. + +**/ +EFI_STATUS +HttpBootDhcp4Dora ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_DHCP4_PROTOCOL *Dhcp4; + UINT32 OptCount; + EFI_DHCP4_PACKET_OPTION *OptList[HTTP_BOOT_DHCP4_OPTION_MAX_NUM]; + UINT8 Buffer[HTTP_BOOT_DHCP4_OPTION_MAX_SIZE]; + EFI_DHCP4_CONFIG_DATA Config; + EFI_STATUS Status; + EFI_DHCP4_MODE_DATA Mode; + + Dhcp4 = Private->Dhcp4; + ASSERT (Dhcp4 != NULL); + + Status = HttpBootSetIp4Policy (Private); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Build option list for the request packet. + // + OptCount = HttpBootBuildDhcp4Options (Private, OptList, Buffer); + ASSERT (OptCount > 0); + + ZeroMem (&Config, sizeof(Config)); + Config.OptionCount = OptCount; + Config.OptionList = OptList; + Config.Dhcp4Callback = HttpBootDhcp4CallBack; + Config.CallbackContext = Private; + Config.DiscoverTryCount = HTTP_BOOT_DHCP_RETRIES; + Config.DiscoverTimeout = mHttpDhcpTimeout; + + // + // Configure the DHCPv4 instance for HTTP boot. + // + Status = Dhcp4->Configure (Dhcp4, &Config); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Initialize the record fields for DHCPv4 offer in private data. + // + Private->OfferNum = 0; + ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); + ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); + + // + // Start DHCPv4 D.O.R.A. process to acquire IPv4 address. + // + Status = Dhcp4->Start (Dhcp4, NULL); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Get the acquired IPv4 address and store them. + // + Status = Dhcp4->GetModeData (Dhcp4, &Mode); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + ASSERT (Mode.State == Dhcp4Bound); + CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS)); + + Status = HttpBootRegisterIp4Gateway (Private); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + AsciiPrint ("\n Station IP address is "); + HttpBootShowIp4Addr (&Private->StationIp.v4); + AsciiPrint ("\n"); + +ON_EXIT: + if (EFI_ERROR (Status)) { + Dhcp4->Stop (Dhcp4); + Dhcp4->Configure (Dhcp4, NULL); + } else { + ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA)); + Dhcp4->Configure (Dhcp4, &Config); + } + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h new file mode 100644 index 00000000..9f670ad2 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h @@ -0,0 +1,253 @@ +/** @file + Functions declaration related with DHCPv4 for HTTP boot driver. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2020, Hewlett Packard Enterprise Development LP. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_UEFI_HTTP_BOOT_DHCP4_H__ +#define __EFI_UEFI_HTTP_BOOT_DHCP4_H__ + +#define HTTP_BOOT_DHCP4_OPTION_MAX_NUM 16 +#define HTTP_BOOT_DHCP4_OPTION_MAX_SIZE 312 +#define HTTP_BOOT_DHCP4_PACKET_MAX_SIZE 1472 + +#define HTTP_BOOT_DHCP4_OPCODE_REQUEST 1 +#define HTTP_BOOT_DHCP4_OPCODE_REPLY 2 +#define HTTP_BOOT_DHCP4_MSG_TYPE_REQUEST 3 +#define HTTP_BOOT_DHCP4_MAGIC 0x63538263 // network byte order + +#define HTTP_BOOT_DHCP4_OVERLOAD_FILE 1 +#define HTTP_BOOT_DHCP4_OVERLOAD_SERVER_NAME 2 + +/// +/// HTTP Tag definition that identifies the processor +/// and programming environment of the client system. +/// These identifiers are defined by IETF: +/// http://www.ietf.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml +/// +#if defined (MDE_CPU_IA32) +#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_IA32 +#elif defined (MDE_CPU_X64) +#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_X64 +#elif defined (MDE_CPU_ARM) +#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_ARM +#elif defined (MDE_CPU_AARCH64) +#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_AARCH64 +#elif defined (MDE_CPU_RISCV64) +#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_RISCV64 +#elif defined (MDE_CPU_EBC) +#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_EBC +#endif + +/// DHCP offer types among HTTP boot. +/// Dhcp4 and Dhcp6 share this definition, and corresponding +/// relationship is as follows: +/// Dhcp4Discover <> Dhcp6Solicit +/// Dhcp4Offer <> Dhcp6Advertise +/// Dhcp4Request <> Dhcp6Request +/// Dhcp4Ack <> DHcp6Reply +/// +typedef enum { + // + // <IP address, IP expressed URI> + // + HttpOfferTypeDhcpIpUri, + // + // <IP address, IP expressed URI, Name-server> + // + HttpOfferTypeDhcpIpUriDns, + // + // <IP address, Domain-name expressed URI, Name-server> + // + HttpOfferTypeDhcpNameUriDns, + // + // <IP address, Name-server> + // + HttpOfferTypeDhcpDns, + // + // <IP address> + // + HttpOfferTypeDhcpOnly, + // + // <Domain-name expressed URI> or + // <Domain-name expressed URI, Name-server (will be ignored)> + // + HttpOfferTypeProxyNameUri, + // + // <IP expressed URI> or + // <IP expressed URI, Name-server (will be ignored)> + // + HttpOfferTypeProxyIpUri, + // + // <IP address, Domain-name expressed URI> + // + HttpOfferTypeDhcpNameUri, + HttpOfferTypeMax +} HTTP_BOOT_OFFER_TYPE; + +#define HTTP_BOOT_DHCP_RETRIES 4 +#define HTTP_BOOT_OFFER_MAX_NUM 16 + +// The array index of the DHCP4 option tag interested +// +#define HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE_LEN 0 +#define HTTP_BOOT_DHCP4_TAG_INDEX_OVERLOAD 1 +#define HTTP_BOOT_DHCP4_TAG_INDEX_MSG_TYPE 2 +#define HTTP_BOOT_DHCP4_TAG_INDEX_SERVER_ID 3 +#define HTTP_BOOT_DHCP4_TAG_INDEX_CLASS_ID 4 +#define HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE 5 +#define HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER 6 +#define HTTP_BOOT_DHCP4_TAG_INDEX_MAX 7 + +#pragma pack(1) + +typedef struct { + UINT8 ParaList[135]; +} HTTP_BOOT_DHCP4_OPTION_PARA; + +typedef struct { + UINT16 Size; +} HTTP_BOOT_DHCP4_OPTION_MAX_MESG_SIZE; + +typedef struct { + UINT8 Type; + UINT8 MajorVer; + UINT8 MinorVer; +} HTTP_BOOT_DHCP4_OPTION_UNDI; + +typedef struct { + UINT8 Type; +} HTTP_BOOT_DHCP4_OPTION_MESG; + +typedef struct { + UINT16 Type; +} HTTP_BOOT_DHCP4_OPTION_ARCH; + +typedef struct { + UINT8 ClassIdentifier[11]; + UINT8 ArchitecturePrefix[5]; + UINT8 ArchitectureType[5]; + UINT8 Lit3[1]; + UINT8 InterfaceName[4]; + UINT8 Lit4[1]; + UINT8 UndiMajor[3]; + UINT8 UndiMinor[3]; +} HTTP_BOOT_DHCP4_OPTION_CLID; + +typedef struct { + UINT8 Type; + UINT8 Guid[16]; +} HTTP_BOOT_DHCP4_OPTION_UUID; + +typedef struct { + UINT16 Type; + UINT16 Layer; +} HTTP_BOOT_OPTION_BOOT_ITEM; + +#pragma pack() + +typedef union { + HTTP_BOOT_DHCP4_OPTION_PARA *Para; + HTTP_BOOT_DHCP4_OPTION_UNDI *Undi; + HTTP_BOOT_DHCP4_OPTION_ARCH *Arch; + HTTP_BOOT_DHCP4_OPTION_CLID *Clid; + HTTP_BOOT_DHCP4_OPTION_UUID *Uuid; + HTTP_BOOT_DHCP4_OPTION_MESG *Mesg; + HTTP_BOOT_DHCP4_OPTION_MAX_MESG_SIZE *MaxMesgSize; +} HTTP_BOOT_DHCP4_OPTION_ENTRY; + +#define GET_NEXT_DHCP_OPTION(Opt) \ + (EFI_DHCP4_PACKET_OPTION *) ((UINT8 *) (Opt) + \ + sizeof (EFI_DHCP4_PACKET_OPTION) + (Opt)->Length - 1) + +#define GET_OPTION_BUFFER_LEN(Pkt) \ + ((Pkt)->Length - sizeof (EFI_DHCP4_HEADER) - 4) + +#define DEFAULT_CLASS_ID_DATA "HTTPClient:Arch:xxxxx:UNDI:003000" +#define DEFAULT_UNDI_TYPE 1 +#define DEFAULT_UNDI_MAJOR 3 +#define DEFAULT_UNDI_MINOR 0 + +typedef struct { + UINT32 Reserved; +} HTTP_BOOT_VENDOR_OPTION; + +#define HTTP_CACHED_DHCP4_PACKET_MAX_SIZE (OFFSET_OF (EFI_DHCP4_PACKET, Dhcp4) + HTTP_BOOT_DHCP4_PACKET_MAX_SIZE) + +typedef union { + EFI_DHCP4_PACKET Offer; + EFI_DHCP4_PACKET Ack; + UINT8 Buffer[HTTP_CACHED_DHCP4_PACKET_MAX_SIZE]; +} HTTP_BOOT_DHCP4_PACKET; + +typedef struct { + // + // URI component + // + CHAR8 *Scheme; + CHAR8 *Authority; + CHAR8 *Path; + CHAR8 *Query; + CHAR8 *Fragment; /// TODO: may not required in HTTP URL + + CHAR8 *RegName; /// Point to somewhere in Authority + BOOLEAN AddrIsOk; + EFI_IP_ADDRESS Address; + UINT16 Port; +} HTTP_BOOT_URI_CONTENT; + +typedef struct { + HTTP_BOOT_DHCP4_PACKET Packet; + HTTP_BOOT_OFFER_TYPE OfferType; + VOID *UriParser; + 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. + + @param[in] Private Pointer to HTTP_BOOT private data. + + @retval EFI_SUCCESS The D.O.R.A process successfully finished. + @retval Others Failed to finish the D.O.R.A process. + +**/ +EFI_STATUS +HttpBootDhcp4Dora ( + 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_IPv4_ADDRESS instances. + + @retval EFI_SUCCESS The DNS configuration has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootRegisterIp4Dns ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN UINTN DataLength, + IN VOID *DnsServerData + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c new file mode 100644 index 00000000..b5e8ce7c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c @@ -0,0 +1,1032 @@ +/** @file + Functions implementation related with DHCPv6 for HTTP boot driver. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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 (DHCP6_OPT_ORO); + OptList[Index]->OpLen = HTONS (8); + OptEnt.Oro = (HTTP_BOOT_DHCP6_OPTION_ORO *) OptList[Index]->Data; + OptEnt.Oro->OpCode[0] = HTONS(DHCP6_OPT_BOOT_FILE_URL); + OptEnt.Oro->OpCode[1] = HTONS(DHCP6_OPT_BOOT_FILE_PARAM); + OptEnt.Oro->OpCode[2] = HTONS(DHCP6_OPT_DNS_SERVERS); + OptEnt.Oro->OpCode[3] = HTONS(DHCP6_OPT_VENDOR_CLASS); + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append client network device interface option + // + OptList[Index]->OpCode = HTONS (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 (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 (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 position 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) == DHCP6_OPT_IA_NA) { + Options[HTTP_BOOT_DHCP6_IDX_IA_NA] = Option; + } else if (NTOHS (Option->OpCode) == 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) == DHCP6_OPT_BOOT_FILE_PARAM) { + Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_PARAM] = Option; + } else if (NTOHS (Option->OpCode) == DHCP6_OPT_VENDOR_CLASS) { + Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS] = Option; + } else if (NTOHS (Option->OpCode) == 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, embedded 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), + 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) >= 16 && + CompareMem ((Option->Data + 6), 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) { + if (IsProxyOffer) { + OfferType = HttpOfferTypeProxyIpUri; + } else { + OfferType = IsDnsOffer ? HttpOfferTypeDhcpIpUriDns : 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. + + @retval EFI_SUCCESS Packet is copied. + @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet. + +**/ +EFI_STATUS +HttpBootCacheDhcp6Packet ( + IN EFI_DHCP6_PACKET *Dst, + IN EFI_DHCP6_PACKET *Src + ) +{ + if (Dst->Size < Src->Length) { + return EFI_BUFFER_TOO_SMALL; + } + + CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length); + Dst->Length = Src->Length; + + return EFI_SUCCESS; +} + +/** + 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. + + @retval EFI_SUCCESS Cache and parse the packet successfully. + @retval Others Operation failed. + +**/ +EFI_STATUS +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; + EFI_STATUS Status; + + Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6; + Offer = &Cache6->Packet.Offer; + + // + // Cache the content of DHCPv6 packet firstly. + // + Status = HttpBootCacheDhcp6Packet(Offer, RcvdOffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Validate the DHCPv6 packet, and parse the options and offer type. + // + if (EFI_ERROR (HttpBootParseDhcp6Packet (Cache6))) { + return EFI_ABORTED; + } + + // + // 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++; + + return EFI_SUCCESS; +} + +/** + 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. + @retval EFI_OUT_OF_RESOURCES There are not enough resources. + +**/ +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; + BOOLEAN Received; + + if ((Dhcp6Event != Dhcp6SendSolicit) && + (Dhcp6Event != Dhcp6RcvdAdvertise) && + (Dhcp6Event != Dhcp6SendRequest) && + (Dhcp6Event != Dhcp6RcvdReply) && + (Dhcp6Event != Dhcp6SelectAdvertise)) { + return EFI_SUCCESS; + } + + ASSERT (Packet != NULL); + + Private = (HTTP_BOOT_PRIVATE_DATA *) Context; + Status = EFI_SUCCESS; + if (Private->HttpBootCallback != NULL && Dhcp6Event != Dhcp6SelectAdvertise) { + Received = (BOOLEAN) (Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply); + Status = Private->HttpBootCallback->Callback ( + Private->HttpBootCallback, + HttpBootDhcp6, + Received, + Packet->Length, + &Packet->Dhcp6 + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + } + switch (Dhcp6Event) { + + case Dhcp6RcvdAdvertise: + Status = EFI_NOT_READY; + if (Packet->Length > HTTP_BOOT_DHCP6_PACKET_MAX_SIZE) { + // + // Ignore the incoming packets which exceed the maximum length. + // + break; + } + if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) { + // + // Cache the dhcp offers to OfferBuffer[] for select later, and record + // the OfferIndex and OfferCount. + // If error happens, just ignore this packet and continue to wait more offer. + // + 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); + if (*NewPacket == NULL) { + return EFI_OUT_OF_RESOURCES; + } + 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 Unexpected 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 successfully. + @retval Others Unexpected 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)); + Dhcp6->Configure (Dhcp6, &Config); + if (Mode.ClientId != NULL) { + FreePool (Mode.ClientId); + } + if (Mode.Ia != NULL) { + FreePool (Mode.Ia); + } + } + + return Status; + +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h new file mode 100644 index 00000000..2543fced --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h @@ -0,0 +1,169 @@ +/** @file + Functions declaration related with DHCPv6 for HTTP boot driver. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#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_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[11]; + 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; + +#define HTTP_CACHED_DHCP6_PACKET_MAX_SIZE (OFFSET_OF (EFI_DHCP6_PACKET, Dhcp6) + HTTP_BOOT_DHCP6_PACKET_MAX_SIZE) + +typedef union { + EFI_DHCP6_PACKET Offer; + EFI_DHCP6_PACKET Ack; + UINT8 Buffer[HTTP_CACHED_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 successfully. + @retval Others Unexpected 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/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.c new file mode 100644 index 00000000..fb6b5f62 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.c @@ -0,0 +1,1332 @@ +/** @file + Driver Binding functions implementation for UEFI HTTP boot. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HttpBootDxe.h" + +/// +/// Driver Binding Protocol instance +/// +EFI_DRIVER_BINDING_PROTOCOL gHttpBootIp4DxeDriverBinding = { + HttpBootIp4DxeDriverBindingSupported, + HttpBootIp4DxeDriverBindingStart, + HttpBootIp4DxeDriverBindingStop, + HTTP_BOOT_DXE_VERSION, + NULL, + NULL +}; + +EFI_DRIVER_BINDING_PROTOCOL gHttpBootIp6DxeDriverBinding = { + HttpBootIp6DxeDriverBindingSupported, + HttpBootIp6DxeDriverBindingStart, + HttpBootIp6DxeDriverBindingStop, + HTTP_BOOT_DXE_VERSION, + NULL, + NULL +}; + + + +/** + Check whether UNDI protocol supports IPv6. + + @param[in] Private Pointer to HTTP_BOOT_PRIVATE_DATA. + @param[out] Ipv6Support TRUE if UNDI supports IPv6. + + @retval EFI_SUCCESS Get the result whether UNDI supports IPv6 by NII or AIP protocol successfully. + @retval EFI_NOT_FOUND Don't know whether UNDI supports IPv6 since NII or AIP is not available. + +**/ +EFI_STATUS +HttpBootCheckIpv6Support ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + OUT BOOLEAN *Ipv6Support + ) +{ + EFI_HANDLE Handle; + EFI_ADAPTER_INFORMATION_PROTOCOL *Aip; + EFI_STATUS Status; + EFI_GUID *InfoTypesBuffer; + UINTN InfoTypeBufferCount; + UINTN TypeIndex; + BOOLEAN Supported; + VOID *InfoBlock; + UINTN InfoBlockSize; + + ASSERT (Private != NULL && Ipv6Support != NULL); + + // + // Check whether the UNDI supports IPv6 by NII protocol. + // + if (Private->Nii != NULL) { + *Ipv6Support = Private->Nii->Ipv6Supported; + return EFI_SUCCESS; + } + + // + // Get the NIC handle by SNP protocol. + // + Handle = NetLibGetSnpHandle (Private->Controller, NULL); + if (Handle == NULL) { + return EFI_NOT_FOUND; + } + + Aip = NULL; + Status = gBS->HandleProtocol ( + Handle, + &gEfiAdapterInformationProtocolGuid, + (VOID *) &Aip + ); + if (EFI_ERROR (Status) || Aip == NULL) { + return EFI_NOT_FOUND; + } + + InfoTypesBuffer = NULL; + InfoTypeBufferCount = 0; + Status = Aip->GetSupportedTypes (Aip, &InfoTypesBuffer, &InfoTypeBufferCount); + if (EFI_ERROR (Status) || InfoTypesBuffer == NULL) { + FreePool (InfoTypesBuffer); + return EFI_NOT_FOUND; + } + + Supported = FALSE; + for (TypeIndex = 0; TypeIndex < InfoTypeBufferCount; TypeIndex++) { + if (CompareGuid (&InfoTypesBuffer[TypeIndex], &gEfiAdapterInfoUndiIpv6SupportGuid)) { + Supported = TRUE; + break; + } + } + + FreePool (InfoTypesBuffer); + if (!Supported) { + return EFI_NOT_FOUND; + } + + // + // We now have adapter information block. + // + InfoBlock = NULL; + InfoBlockSize = 0; + Status = Aip->GetInformation (Aip, &gEfiAdapterInfoUndiIpv6SupportGuid, &InfoBlock, &InfoBlockSize); + if (EFI_ERROR (Status) || InfoBlock == NULL) { + FreePool (InfoBlock); + return EFI_NOT_FOUND; + } + + *Ipv6Support = ((EFI_ADAPTER_INFO_UNDI_IPV6_SUPPORT *) InfoBlock)->Ipv6Support; + FreePool (InfoBlock); + + return EFI_SUCCESS; +} + +/** + Destroy the HTTP child based on IPv4 stack. + + @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL. + @param[in] Private Pointer to HTTP_BOOT_PRIVATE_DATA. + +**/ +VOID +HttpBootDestroyIp4Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + ASSERT (This != NULL); + ASSERT (Private != NULL); + + if (Private->Dhcp4Child != NULL) { + gBS->CloseProtocol ( + Private->Dhcp4Child, + &gEfiDhcp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + Private->Dhcp4Child + ); + } + + if (Private->Ip6Nic == NULL && Private->HttpCreated) { + HttpIoDestroyIo (&Private->HttpIo); + Private->HttpCreated = FALSE; + } + + 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); + + 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 + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + Private->Dhcp6Child + ); + } + + if (Private->Ip4Nic == NULL && 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; + } +} + +/** + 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 +HttpBootIp4DxeDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Try to open the DHCP4, HTTP4 and Device Path protocol. + // + Status = gBS->OpenProtocol ( + 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 + ); + 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 failed to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpBootIp4DxeDriverBindingStart ( + 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; + BOOLEAN FirstStart; + + FirstStart = FALSE; + + 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 { + FirstStart = TRUE; + + // + // 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; + 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; + } + + // + // Initialize the HII configuration form. + // + Status = HttpBootConfigFormInit (Private); + 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) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + Private->Ip4Nic->Private = Private; + Private->Ip4Nic->ImageHandle = This->DriverBindingHandle; + Private->Ip4Nic->Signature = HTTP_BOOT_VIRTUAL_NIC_SIGNATURE; + + // + // Create DHCP4 child instance. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + &Private->Dhcp4Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Dhcp4Child, + &gEfiDhcp4ProtocolGuid, + (VOID **) &Private->Dhcp4, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Get the Ip4Config2 protocol, it's required to configure the default gateway address. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp4Config2ProtocolGuid, + (VOID **) &Private->Ip4Config2, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Append IPv4 device path node. + // + Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH)); + if (Node == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH; + Node->Ipv4.Header.SubType = MSG_IPv4_DP; + SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH)); + Node->Ipv4.StaticIpAddress = FALSE; + DevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) 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->Ip4Nic->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + FreePool (DevicePath); + 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->Ip4Nic->LoadFile, &gHttpBootDxeLoadFile, sizeof (EFI_LOAD_FILE_PROTOCOL)); + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Ip4Nic->Controller, + &gEfiLoadFileProtocolGuid, + &Private->Ip4Nic->LoadFile, + &gEfiDevicePathProtocolGuid, + Private->Ip4Nic->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 Ipv4 NIC handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + Private->Ip4Nic->Controller, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + if (Private != NULL) { + if (FirstStart) { + gBS->UninstallProtocolInterface ( + ControllerHandle, + &gEfiCallerIdGuid, + &Private->Id + ); + } + + HttpBootDestroyIp4Children (This, Private); + HttpBootConfigFormUnload (Private); + + if (FirstStart) { + 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 +HttpBootIp4DxeDriverBindingStop ( + 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 = HttpBootGetNicByIp4Children (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; + } + + // + // Destroy all child instance and uninstall protocol interface. + // + HttpBootDestroyIp4Children (This, Private); + + if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) { + // + // Release the cached data. + // + HttpBootFreeCacheList (Private); + + // + // Unload the config form. + // + HttpBootConfigFormUnload (Private); + + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiCallerIdGuid, + &Private->Id + ); + FreePool (Private); + + } + + return EFI_SUCCESS; +} + +/** + 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 + ) +{ + EFI_STATUS Status; + + // + // Try to open the DHCP6, HTTP and Device Path protocol. + // + 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 failed 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; + BOOLEAN Ipv6Available; + BOOLEAN FirstStart; + + FirstStart = FALSE; + + 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 { + FirstStart = TRUE; + + // + // 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; + 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; + } + + // + // Initialize the HII configuration form. + // + Status = HttpBootConfigFormInit (Private); + 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; + } + + } + + // + // Set IPv6 available flag. + // + Status = HttpBootCheckIpv6Support (Private, &Ipv6Available); + if (EFI_ERROR (Status)) { + // + // Fail to get the data whether UNDI supports IPv6. + // Set default value to TRUE. + // + Ipv6Available = TRUE; + } + + if (!Ipv6Available) { + Status = EFI_UNSUPPORTED; + 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) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + Private->Ip6Nic->Private = Private; + Private->Ip6Nic->ImageHandle = This->DriverBindingHandle; + 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: + if (Private != NULL) { + if (FirstStart) { + gBS->UninstallProtocolInterface ( + ControllerHandle, + &gEfiCallerIdGuid, + &Private->Id + ); + } + + HttpBootDestroyIp6Children(This, Private); + HttpBootConfigFormUnload (Private); + + if (FirstStart) { + 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; + } + + // + // Destroy all child instance and uninstall protocol interface. + // + HttpBootDestroyIp6Children (This, Private); + + if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) { + // + // Release the cached data. + // + HttpBootFreeCacheList (Private); + + // + // Unload the config form. + // + HttpBootConfigFormUnload (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)) { + EfiLibUninstallDriverBindingComponentName2( + &gHttpBootIp4DxeDriverBinding, + &gHttpBootDxeComponentName, + &gHttpBootDxeComponentName2 + ); + } + return Status; +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.h new file mode 100644 index 00000000..55491935 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.h @@ -0,0 +1,525 @@ +/** @file + UEFI HTTP boot driver's private data structure and interfaces declaration. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +(C) Copyright 2016 - 2020 Hewlett Packard Enterprise Development LP<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_HTTP_BOOT_DXE_H__ +#define __EFI_HTTP_BOOT_DXE_H__ + +#include <Uefi.h> + +#include <IndustryStandard/Http11.h> +#include <IndustryStandard/Dhcp.h> + +// +// Libraries +// +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiHiiServicesLib.h> +#include <Library/UefiRuntimeServicesTableLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/BaseLib.h> +#include <Library/UefiLib.h> +#include <Library/DevicePathLib.h> +#include <Library/DebugLib.h> +#include <Library/NetLib.h> +#include <Library/HttpLib.h> +#include <Library/HttpIoLib.h> +#include <Library/HiiLib.h> +#include <Library/PrintLib.h> +#include <Library/DpcLib.h> + +// +// UEFI Driver Model Protocols +// +#include <Protocol/DriverBinding.h> +#include <Protocol/ComponentName2.h> +#include <Protocol/ComponentName.h> + +// +// Consumed Protocols +// +#include <Protocol/ServiceBinding.h> +#include <Protocol/HiiConfigAccess.h> +#include <Protocol/NetworkInterfaceIdentifier.h> +#include <Protocol/Dhcp4.h> +#include <Protocol/Dhcp6.h> +#include <Protocol/Dns6.h> +#include <Protocol/Http.h> +#include <Protocol/Ip4Config2.h> +#include <Protocol/Ip6Config.h> +#include <Protocol/RamDisk.h> +#include <Protocol/AdapterInformation.h> + +// +// Produced Protocols +// +#include <Protocol/LoadFile.h> +#include <Protocol/HttpBootCallback.h> + +// +// Consumed Guids +// +#include <Guid/HttpBootConfigHii.h> + +// +// Driver Version +// +#define HTTP_BOOT_DXE_VERSION 0xa + +// +// Standard Media Types defined in +// http://www.iana.org/assignments/media-types +// +#define HTTP_CONTENT_TYPE_APP_EFI "application/efi" +#define HTTP_CONTENT_TYPE_APP_IMG "application/vnd.efi-img" +#define HTTP_CONTENT_TYPE_APP_ISO "application/vnd.efi-iso" + +// +// Protocol instances +// +extern EFI_DRIVER_BINDING_PROTOCOL gHttpBootDxeDriverBinding; +extern EFI_COMPONENT_NAME2_PROTOCOL gHttpBootDxeComponentName2; +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; + +typedef enum { + ImageTypeEfi, + ImageTypeVirtualCd, + ImageTypeVirtualDisk, + ImageTypeMax +} HTTP_BOOT_IMAGE_TYPE; + +// +// Include files with internal function prototypes +// +#include "HttpBootComponentName.h" +#include "HttpBootDhcp4.h" +#include "HttpBootDhcp6.h" +#include "HttpBootImpl.h" +#include "HttpBootSupport.h" +#include "HttpBootClient.h" +#include "HttpBootConfig.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_HANDLE ImageHandle; + EFI_LOAD_FILE_PROTOCOL LoadFile; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + HTTP_BOOT_PRIVATE_DATA *Private; +}; + +#define HTTP_BOOT_PRIVATE_DATA_FROM_CALLBACK_INFO(Callback) \ + CR ( \ + Callback, \ + HTTP_BOOT_PRIVATE_DATA, \ + CallbackInfo, \ + HTTP_BOOT_PRIVATE_DATA_SIGNATURE \ + ) + +#define HTTP_BOOT_PRIVATE_DATA_FROM_CALLBACK_PROTOCOL(CallbackProtocol) \ + CR ( \ + CallbackProtocol, \ + HTTP_BOOT_PRIVATE_DATA, \ + LoadFileCallback, \ + HTTP_BOOT_PRIVATE_DATA_SIGNATURE \ + ) + +struct _HTTP_BOOT_PRIVATE_DATA { + UINT32 Signature; + EFI_HANDLE Controller; + + HTTP_BOOT_VIRTUAL_NIC *Ip4Nic; + HTTP_BOOT_VIRTUAL_NIC *Ip6Nic; + + // + // Consumed children + // + EFI_HANDLE Ip6Child; + EFI_HANDLE Dhcp4Child; + EFI_HANDLE Dhcp6Child; + HTTP_IO HttpIo; + BOOLEAN HttpCreated; + + // + // 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 protocol + // + EFI_LOAD_FILE_PROTOCOL LoadFile; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINT32 Id; + EFI_HTTP_BOOT_CALLBACK_PROTOCOL *HttpBootCallback; + EFI_HTTP_BOOT_CALLBACK_PROTOCOL LoadFileCallback; + + // + // Data for the default HTTP Boot callback protocol + // + UINT64 FileSize; + UINT64 ReceivedSize; + UINT32 Percentage; + + // + // HII callback info block + // + HTTP_BOOT_FORM_CALLBACK_INFO CallbackInfo; + + // + // Mode data + // + BOOLEAN UsingIpv6; + BOOLEAN Started; + EFI_IP_ADDRESS StationIp; + EFI_IP_ADDRESS SubnetMask; + EFI_IP_ADDRESS GatewayIp; + EFI_IP_ADDRESS ServerIp; + UINT16 Port; + UINT32 DnsServerCount; + EFI_IP_ADDRESS *DnsServerIp; + + // + // The URI string attempt to download through HTTP, may point to + // the memory in cached DHCP offer, or to the memory in FilePathUri. + // + CHAR8 *BootFileUri; + VOID *BootFileUriParser; + UINTN BootFileSize; + BOOLEAN NoGateway; + HTTP_BOOT_IMAGE_TYPE ImageType; + + // + // URI string extracted from the input FilePath parameter. + // + CHAR8 *FilePathUri; + VOID *FilePathUriParser; + + // + // Cached HTTP data + // + LIST_ENTRY CacheList; + + // + // Cached DHCP offer + // + // OfferIndex records the index of DhcpOffer[] buffer, and OfferCount records the num of each type of offer. + // + // It supposed that + // + // OfferNum: 8 + // OfferBuffer: [ProxyNameUri, DhcpNameUri, DhcpIpUri, ProxyNameUri, ProxyIpUri, DhcpOnly, DhcpIpUri, DhcpNameUriDns] + // (OfferBuffer is 0-based.) + // + // And assume that (DhcpIpUri is the first priority actually.) + // + // SelectIndex: 5 + // SelectProxyType: HttpOfferTypeProxyIpUri + // (SelectIndex is 1-based, and 0 means no one is selected.) + // + // So it should be + // + // DhcpIpUri DhcpNameUriDns DhcpDns DhcpOnly ProxyNameUri ProxyIpUri DhcpNameUri + // OfferCount: [ 2, 1, 0, 1, 2, 1, 1] + // + // OfferIndex: {[ 2, 7, 0, 5, 0, *4, 1] + // [ 6, 0, 0, 0, 3, 0, 0] + // [ 0, 0, 0, 0, 0, 0, 0] + // ... ]} + // (OfferIndex is 0-based.) + // + // + UINT32 SelectIndex; + UINT32 SelectProxyType; + HTTP_BOOT_DHCP_PACKET_CACHE OfferBuffer[HTTP_BOOT_OFFER_MAX_NUM]; + UINT32 OfferNum; + UINT32 OfferCount[HttpOfferTypeMax]; + UINT32 OfferIndex[HttpOfferTypeMax][HTTP_BOOT_OFFER_MAX_NUM]; +}; + +#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; + +/** + 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 +HttpBootIp4DxeDriverBindingSupported ( + 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 failed to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpBootIp4DxeDriverBindingStart ( + 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 +HttpBootIp4DxeDriverBindingStop ( + 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 +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 failed 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/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.inf b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.inf new file mode 100644 index 00000000..d64755ff --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.inf @@ -0,0 +1,101 @@ +## @file +# This modules produce the Load File Protocol for UEFI HTTP boot. +# +# Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +# (C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = HttpBootDxe + FILE_GUID = ecebcb00-d9c8-11e4-af3d-8cdcd426c973 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = HttpBootDxeDriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + MODULE_UNI_FILE = HttpBootDxe.uni + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + NetworkPkg/NetworkPkg.dec + +[Sources] + HttpBootConfigNVDataStruc.h + HttpBootDxe.h + HttpBootDxe.c + HttpBootConfig.h + HttpBootConfig.c + HttpBootComponentName.h + HttpBootComponentName.c + HttpBootImpl.h + HttpBootImpl.c + HttpBootDhcp4.h + HttpBootDhcp4.c + HttpBootDhcp6.h + HttpBootDhcp6.c + HttpBootSupport.h + HttpBootSupport.c + HttpBootClient.h + HttpBootClient.c + HttpBootConfigVfr.vfr + HttpBootConfigStrings.uni + +[LibraryClasses] + UefiDriverEntryPoint + UefiBootServicesTableLib + MemoryAllocationLib + BaseLib + UefiLib + DevicePathLib + DebugLib + NetLib + HttpLib + HttpIoLib + HiiLib + PrintLib + DpcLib + UefiHiiServicesLib + UefiBootManagerLib + +[Protocols] + ## TO_START + ## BY_START + gEfiDevicePathProtocolGuid + + gEfiLoadFileProtocolGuid ## BY_START + gEfiHttpServiceBindingProtocolGuid ## CONSUMES + gEfiHttpProtocolGuid ## CONSUMES + 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 + gEfiRamDiskProtocolGuid ## SOMETIMES_CONSUMES + gEfiHiiConfigAccessProtocolGuid ## BY_START + gEfiHttpBootCallbackProtocolGuid ## SOMETIMES_PRODUCES + gEfiAdapterInformationProtocolGuid ## SOMETIMES_CONSUMES + +[Guids] + ## SOMETIMES_CONSUMES ## GUID # HiiIsConfigHdrMatch mHttpBootConfigStorageName + ## SOMETIMES_PRODUCES ## GUID # HiiConstructConfigHdr mHttpBootConfigStorageName + ## SOMETIMES_PRODUCES ## GUID # HiiGetBrowserData mHttpBootConfigStorageName + ## SOMETIMES_CONSUMES ## HII + gHttpBootConfigGuid + gEfiVirtualCdGuid ## SOMETIMES_CONSUMES ## GUID + gEfiVirtualDiskGuid ## SOMETIMES_CONSUMES ## GUID + gEfiAdapterInfoUndiIpv6SupportGuid ## SOMETIMES_CONSUMES ## GUID + +[Pcd] + gEfiNetworkPkgTokenSpaceGuid.PcdAllowHttpConnections ## CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + HttpBootDxeExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.uni new file mode 100644 index 00000000..68143cc4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.uni @@ -0,0 +1,18 @@ +// /** @file
+// UEFI HTTP boot service.
+//
+// This driver provides EFI Load File Protocol which is used to download
+// the boot image from HTTP server. It could work with an IPv4 or IPv6 stack.
+//
+//
+// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "UEFI HTTP boot service"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This driver provides EFI Load File Protocol which is used to download the boot image from HTTP server. It could work with an IPv4 or IPv6 stack."
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni new file mode 100644 index 00000000..b1b4ed0f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// HttpBootDxe Localized Strings and Content
+//
+// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"UEFI HTTP BOOT DXE"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootImpl.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootImpl.c new file mode 100644 index 00000000..f91a9b2b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootImpl.c @@ -0,0 +1,767 @@ +/** @file + The implementation of EFI_LOAD_FILE_PROTOCOL for UEFI HTTP boot. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HttpBootDxe.h" + +/** + Install HTTP Boot Callback Protocol if not installed before. + + @param[in] Private Pointer to HTTP Boot private data. + + @retval EFI_SUCCESS HTTP Boot Callback Protocol installed successfully. + @retval Others Failed to install HTTP Boot Callback Protocol. + +**/ +EFI_STATUS +HttpBootInstallCallback ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_HANDLE ControllerHandle; + + if (!Private->UsingIpv6) { + ControllerHandle = Private->Ip4Nic->Controller; + } else { + ControllerHandle = Private->Ip6Nic->Controller; + } + + // + // Check whether gEfiHttpBootCallbackProtocolGuid already installed. + // + Status = gBS->HandleProtocol ( + ControllerHandle, + &gEfiHttpBootCallbackProtocolGuid, + (VOID **) &Private->HttpBootCallback + ); + if (Status == EFI_UNSUPPORTED) { + + CopyMem ( + &Private->LoadFileCallback, + &gHttpBootDxeHttpBootCallback, + sizeof (EFI_HTTP_BOOT_CALLBACK_PROTOCOL) + ); + + // + // Install a default callback if user didn't offer one. + // + Status = gBS->InstallProtocolInterface ( + &ControllerHandle, + &gEfiHttpBootCallbackProtocolGuid, + EFI_NATIVE_INTERFACE, + &Private->LoadFileCallback + ); + if (EFI_ERROR (Status)) { + return Status; + } + Private->HttpBootCallback = &Private->LoadFileCallback; + } + + return EFI_SUCCESS; +} + +/** + Uninstall HTTP Boot Callback Protocol if it's installed by this driver. + + @param[in] Private Pointer to HTTP Boot private data. + +**/ +VOID +HttpBootUninstallCallback ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + if (Private->HttpBootCallback == &Private->LoadFileCallback) { + gBS->UninstallProtocolInterface ( + Private->Controller, + &gEfiHttpBootCallbackProtocolGuid, + &Private->HttpBootCallback + ); + Private->HttpBootCallback = NULL; + } +} + +/** + Enable the use of UEFI HTTP boot function. + + If the driver has already been started but not satisfy the requirement (IP stack and + specified boot file path), this function will stop the driver and start it again. + + @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. + @param[in] FilePath The device specific path of the file to load. + + @retval EFI_SUCCESS HTTP boot was successfully enabled. + @retval EFI_INVALID_PARAMETER Private is NULL or FilePath is NULL. + @retval EFI_INVALID_PARAMETER The FilePath doesn't contain a valid URI device path node. + @retval EFI_ALREADY_STARTED The driver is already in started state. + @retval EFI_OUT_OF_RESOURCES There are not enough resources. + +**/ +EFI_STATUS +HttpBootStart ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN BOOLEAN UsingIpv6, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath + ) +{ + UINTN Index; + EFI_STATUS Status; + CHAR8 *Uri; + + Uri = NULL; + + if (Private == NULL || FilePath == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check the URI in the input FilePath, in order to see whether it is + // required to boot from a new specified boot file. + // + Status = HttpBootParseFilePath (FilePath, &Uri); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether we need to stop and restart the HTTP boot driver. + // + if (Private->Started) { + // + // Restart is needed in 2 cases: + // 1. Http boot driver has already been started but not on the required IP stack. + // 2. The specified boot file URI in FilePath is different with the one we have + // recorded before. + // + if ((UsingIpv6 != Private->UsingIpv6) || + ((Uri != NULL) && (AsciiStrCmp (Private->BootFileUri, Uri) != 0))) { + // + // Restart is required, first stop then continue this start function. + // + Status = HttpBootStop (Private); + if (EFI_ERROR (Status)) { + if (Uri != NULL) { + FreePool (Uri); + } + return Status; + } + } else { + // + // Restart is not required. + // + if (Uri != NULL) { + FreePool (Uri); + } + 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 { + if (Uri != NULL) { + FreePool (Uri); + } + return EFI_UNSUPPORTED; + } + + // + // Record the specified URI and prepare the URI parser if needed. + // + Private->FilePathUri = Uri; + if (Private->FilePathUri != NULL) { + Status = HttpParseUrl ( + Private->FilePathUri, + (UINT32) AsciiStrLen (Private->FilePathUri), + FALSE, + &Private->FilePathUriParser + ); + if (EFI_ERROR (Status)) { + FreePool (Private->FilePathUri); + return Status; + } + } + + // + // Init the content of cached DHCP offer list. + // + ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer)); + if (!Private->UsingIpv6) { + for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) { + Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = HTTP_CACHED_DHCP4_PACKET_MAX_SIZE; + } + } else { + for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) { + Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = HTTP_CACHED_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; + Print (L"\n>>Start HTTP Boot over IPv%d", Private->UsingIpv6 ? 6 : 4); + + return EFI_SUCCESS; +} + +/** + 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. + + @retval EFI_SUCCESS Boot info was successfully retrieved. + @retval EFI_INVALID_PARAMETER Private is NULL. + @retval EFI_NOT_STARTED The driver is in stopped state. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpBootDhcp ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!Private->Started) { + return EFI_NOT_STARTED; + } + + 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 { + // + // Start S.A.R.R process to get a IPv6 address and other boot information. + // + Status = HttpBootDhcp6Sarr (Private); + } + + return Status; +} + +/** + Attempt to download the boot file through HTTP message exchange. + + @param[in] Private The pointer to the driver's private data. + @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param[in] Buffer The memory buffer to transfer the file to. If Buffer is NULL, + then the size of the requested file is returned in + BufferSize. + @param[out] ImageType The image type of the downloaded file. + + @retval EFI_SUCCESS Boot file was loaded successfully. + @retval EFI_INVALID_PARAMETER Private is NULL, or ImageType is NULL, or BufferSize is NULL. + @retval EFI_INVALID_PARAMETER *BufferSize is not zero, and Buffer is NULL. + @retval EFI_NOT_STARTED The driver is in stopped state. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the boot file. BufferSize has + been updated with the size needed to complete the request. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpBootLoadFile ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN OUT UINTN *BufferSize, + IN VOID *Buffer, OPTIONAL + OUT HTTP_BOOT_IMAGE_TYPE *ImageType + ) +{ + EFI_STATUS Status; + + if (Private == NULL || ImageType == NULL || BufferSize == NULL ) { + return EFI_INVALID_PARAMETER; + } + + if (*BufferSize != 0 && Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!Private->Started) { + return EFI_NOT_STARTED; + } + + Status = HttpBootInstallCallback (Private); + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + + if (Private->BootFileUri == NULL) { + // + // Parse the cached offer to get the boot file URL first. + // + Status = HttpBootDiscoverBootInfo (Private); + if (EFI_ERROR (Status)) { + AsciiPrint ("\n Error: Could not retrieve NBP file size from HTTP server.\n"); + goto ON_EXIT; + } + } + + if (!Private->HttpCreated) { + // + // Create HTTP child. + // + Status = HttpBootCreateHttpIo (Private); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + if (Private->BootFileSize == 0) { + // + // Discover the information about the bootfile if we haven't. + // + + // + // Try to use HTTP HEAD method. + // + Status = HttpBootGetBootFile ( + Private, + TRUE, + &Private->BootFileSize, + NULL, + &Private->ImageType + ); + if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) { + // + // Failed to get file size by HEAD method, may be trunked encoding, try HTTP GET method. + // + ASSERT (Private->BootFileSize == 0); + Status = HttpBootGetBootFile ( + Private, + FALSE, + &Private->BootFileSize, + NULL, + &Private->ImageType + ); + if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) { + AsciiPrint ("\n Error: Could not retrieve NBP file size from HTTP server.\n"); + goto ON_EXIT; + } + } + } + + if (*BufferSize < Private->BootFileSize) { + *BufferSize = Private->BootFileSize; + *ImageType = Private->ImageType; + Status = EFI_BUFFER_TOO_SMALL; + goto ON_EXIT; + } + + // + // Load the boot file into Buffer + // + Status = HttpBootGetBootFile ( + Private, + FALSE, + BufferSize, + Buffer, + ImageType + ); + +ON_EXIT: + HttpBootUninstallCallback (Private); + + if (EFI_ERROR (Status)) { + if (Status == EFI_ACCESS_DENIED) { + AsciiPrint ("\n Error: Could not establish connection with HTTP server.\n"); + } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) { + AsciiPrint ("\n Error: Buffer size is smaller than the requested file.\n"); + } else if (Status == EFI_OUT_OF_RESOURCES) { + AsciiPrint ("\n Error: Could not allocate I/O buffers.\n"); + } else if (Status == EFI_DEVICE_ERROR) { + AsciiPrint ("\n Error: Network device error.\n"); + } else if (Status == EFI_TIMEOUT) { + AsciiPrint ("\n Error: Server response timeout.\n"); + } else if (Status == EFI_ABORTED) { + AsciiPrint ("\n Error: Remote boot cancelled.\n"); + } else if (Status != EFI_BUFFER_TOO_SMALL) { + AsciiPrint ("\n Error: Unexpected network error.\n"); + } + } + + return Status; +} + +/** + Disable the use of UEFI HTTP boot function. + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS HTTP boot was successfully disabled. + @retval EFI_NOT_STARTED The driver is already in stopped state. + @retval EFI_INVALID_PARAMETER Private is NULL. + @retval Others Unexpected error when stop the function. + +**/ +EFI_STATUS +HttpBootStop ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + UINTN Index; + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!Private->Started) { + return EFI_NOT_STARTED; + } + + if (Private->HttpCreated) { + HttpIoDestroyIo (&Private->HttpIo); + Private->HttpCreated = FALSE; + } + + Private->Started = FALSE; + ZeroMem (&Private->StationIp, sizeof (EFI_IP_ADDRESS)); + ZeroMem (&Private->SubnetMask, sizeof (EFI_IP_ADDRESS)); + ZeroMem (&Private->GatewayIp, sizeof (EFI_IP_ADDRESS)); + Private->Port = 0; + Private->BootFileUri = NULL; + Private->BootFileUriParser = NULL; + Private->BootFileSize = 0; + Private->SelectIndex = 0; + Private->SelectProxyType = HttpOfferTypeMax; + + if (!Private->UsingIpv6) { + // + // Stop and release the DHCP4 child. + // + Private->Dhcp4->Stop (Private->Dhcp4); + Private->Dhcp4->Configure (Private->Dhcp4, NULL); + + for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) { + if (Private->OfferBuffer[Index].Dhcp4.UriParser) { + HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp4.UriParser); + } + } + } else { + // + // 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); + } + } + } + + if (Private->DnsServerIp != NULL) { + FreePool (Private->DnsServerIp); + Private->DnsServerIp = NULL; + } + + if (Private->FilePathUri!= NULL) { + FreePool (Private->FilePathUri); + HttpUrlFreeParser (Private->FilePathUriParser); + Private->FilePathUri = NULL; + Private->FilePathUriParser = NULL; + } + + ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer)); + Private->OfferNum = 0; + ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); + ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); + + HttpBootFreeCacheList (Private); + + return EFI_SUCCESS; +} + +/** + Causes the driver to load a specified file. + + @param This Protocol instance pointer. + @param FilePath The device specific path of the file to load. + @param BootPolicy If TRUE, indicates that the request originates from the + boot manager is attempting to load FilePath as a boot + selection. If FALSE, then FilePath must match as exact file + to be loaded. + @param BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param Buffer The memory buffer to transfer the file to. IF Buffer is NULL, + then the size of the requested file is returned in + BufferSize. + + @retval EFI_SUCCESS The file was loaded. + @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy + @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or + BufferSize is NULL. + @retval EFI_NO_MEDIA No medium was present to load the file. + @retval EFI_DEVICE_ERROR The file was not loaded due to a device error. + @retval EFI_NO_RESPONSE The remote system did not respond. + @retval EFI_NOT_FOUND The file was not found. + @retval EFI_ABORTED The file load process was manually cancelled. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. + BufferSize has been updated with the size needed to complete + the request. + +**/ +EFI_STATUS +EFIAPI +HttpBootDxeLoadFile ( + IN EFI_LOAD_FILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN BOOLEAN BootPolicy, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ) +{ + HTTP_BOOT_PRIVATE_DATA *Private; + HTTP_BOOT_VIRTUAL_NIC *VirtualNic; + EFI_STATUS MediaStatus; + BOOLEAN UsingIpv6; + EFI_STATUS Status; + HTTP_BOOT_IMAGE_TYPE ImageType; + + if (This == NULL || BufferSize == NULL || FilePath == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Only support BootPolicy + // + if (!BootPolicy) { + return EFI_UNSUPPORTED; + } + + VirtualNic = HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE (This); + Private = VirtualNic->Private; + + // + // Check media status before HTTP boot start + // + MediaStatus = EFI_SUCCESS; + NetLibDetectMediaWaitTimeout (Private->Controller, HTTP_BOOT_CHECK_MEDIA_WAITING_TIME, &MediaStatus); + if (MediaStatus != EFI_SUCCESS) { + AsciiPrint ("\n Error: Could not detect network connection.\n"); + return EFI_NO_MEDIA; + } + + // + // Check whether the virtual nic is using IPv6 or not. + // + UsingIpv6 = FALSE; + if (VirtualNic == Private->Ip6Nic) { + UsingIpv6 = TRUE; + } + + // + // Initialize HTTP boot. + // + Status = HttpBootStart (Private, UsingIpv6, FilePath); + if (Status != EFI_SUCCESS && Status != EFI_ALREADY_STARTED) { + return Status; + } + + // + // Load the boot file. + // + ImageType = ImageTypeMax; + Status = HttpBootLoadFile (Private, BufferSize, Buffer, &ImageType); + if (EFI_ERROR (Status)) { + if (Status == EFI_BUFFER_TOO_SMALL && (ImageType == ImageTypeVirtualCd || ImageType == ImageTypeVirtualDisk)) { + Status = EFI_WARN_FILE_SYSTEM; + } else if (Status != EFI_BUFFER_TOO_SMALL) { + HttpBootStop (Private); + } + return Status; + } + + // + // Register the RAM Disk to the system if needed. + // + if (ImageType == ImageTypeVirtualCd || ImageType == ImageTypeVirtualDisk) { + Status = HttpBootRegisterRamDisk (Private, *BufferSize, Buffer, ImageType); + if (!EFI_ERROR (Status)) { + Status = EFI_WARN_FILE_SYSTEM; + } else { + AsciiPrint ("\n Error: Could not register RAM disk to the system.\n"); + } + } + + // + // Stop the HTTP Boot service after the boot image is downloaded. + // + HttpBootStop (Private); + return Status; +} + +/// +/// Load File Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_LOAD_FILE_PROTOCOL gHttpBootDxeLoadFile = { + HttpBootDxeLoadFile +}; + +/** + Callback function that is invoked when the HTTP Boot driver is about to transmit or has received a + packet. + + This function is invoked when the HTTP Boot driver is about to transmit or has received packet. + Parameters DataType and Received specify the type of event and the format of the buffer pointed + to by Data. Due to the polling nature of UEFI device drivers, this callback function should not + execute for more than 5 ms. + The returned status code determines the behavior of the HTTP Boot driver. + + @param[in] This Pointer to the EFI_HTTP_BOOT_CALLBACK_PROTOCOL instance. + @param[in] DataType The event that occurs in the current state. + @param[in] Received TRUE if the callback is being invoked due to a receive event. + FALSE if the callback is being invoked due to a transmit event. + @param[in] DataLength The length in bytes of the buffer pointed to by Data. + @param[in] Data A pointer to the buffer of data, the data type is specified by + DataType. + + @retval EFI_SUCCESS Tells the HTTP Boot driver to continue the HTTP Boot process. + @retval EFI_ABORTED Tells the HTTP Boot driver to abort the current HTTP Boot process. +**/ +EFI_STATUS +EFIAPI +HttpBootCallback ( + IN EFI_HTTP_BOOT_CALLBACK_PROTOCOL *This, + IN EFI_HTTP_BOOT_CALLBACK_DATA_TYPE DataType, + IN BOOLEAN Received, + IN UINT32 DataLength, + IN VOID *Data OPTIONAL + ) +{ + EFI_HTTP_MESSAGE *HttpMessage; + EFI_HTTP_HEADER *HttpHeader; + HTTP_BOOT_PRIVATE_DATA *Private; + UINT32 Percentage; + + Private = HTTP_BOOT_PRIVATE_DATA_FROM_CALLBACK_PROTOCOL(This); + + switch (DataType) { + case HttpBootDhcp4: + case HttpBootDhcp6: + Print (L"."); + break; + + case HttpBootHttpRequest: + if (Data != NULL) { + HttpMessage = (EFI_HTTP_MESSAGE *) Data; + if (HttpMessage->Data.Request->Method == HttpMethodGet && + HttpMessage->Data.Request->Url != NULL) { + Print (L"\n URI: %s\n", HttpMessage->Data.Request->Url); + } + } + break; + + case HttpBootHttpResponse: + if (Data != NULL) { + HttpMessage = (EFI_HTTP_MESSAGE *) Data; + + if (HttpMessage->Data.Response != NULL) { + if (HttpBootIsHttpRedirectStatusCode (HttpMessage->Data.Response->StatusCode)) { + // + // Server indicates the resource has been redirected to a different URL + // according to the section 6.4 of RFC7231 and the RFC 7538. + // Display the redirect information on the screen. + // + HttpHeader = HttpFindHeader ( + HttpMessage->HeaderCount, + HttpMessage->Headers, + HTTP_HEADER_LOCATION + ); + if (HttpHeader != NULL) { + Print (L"\n HTTP ERROR: Resource Redirected.\n New Location: %a\n", HttpHeader->FieldValue); + } + break; + } + } + + HttpHeader = HttpFindHeader ( + HttpMessage->HeaderCount, + HttpMessage->Headers, + HTTP_HEADER_CONTENT_LENGTH + ); + if (HttpHeader != NULL) { + Private->FileSize = AsciiStrDecimalToUintn (HttpHeader->FieldValue); + Private->ReceivedSize = 0; + Private->Percentage = 0; + } + } + break; + + case HttpBootHttpEntityBody: + if (DataLength != 0) { + if (Private->FileSize != 0) { + // + // We already know the file size, print in percentage format. + // + if (Private->ReceivedSize == 0) { + Print (L" File Size: %lu Bytes\n", Private->FileSize); + } + Private->ReceivedSize += DataLength; + Percentage = (UINT32) DivU64x64Remainder (MultU64x32 (Private->ReceivedSize, 100), Private->FileSize, NULL); + if (Private->Percentage != Percentage) { + Private->Percentage = Percentage; + Print (L"\r Downloading...%d%%", Percentage); + } + } else { + // + // In some case we couldn't get the file size from the HTTP header, so we + // just print the downloaded file size. + // + Private->ReceivedSize += DataLength; + Print (L"\r Downloading...%lu Bytes", Private->ReceivedSize); + } + } + break; + + default: + break; + }; + + return EFI_SUCCESS; +} + +/// +/// HTTP Boot Callback Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_HTTP_BOOT_CALLBACK_PROTOCOL gHttpBootDxeHttpBootCallback = { + HttpBootCallback +}; diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootImpl.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootImpl.h new file mode 100644 index 00000000..0e0d8e26 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootImpl.h @@ -0,0 +1,48 @@ +/** @file + The declaration of UEFI HTTP boot function. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#ifndef __EFI_HTTP_BOOT_IMPL_H__ +#define __EFI_HTTP_BOOT_IMPL_H__ + +#define HTTP_BOOT_CHECK_MEDIA_WAITING_TIME EFI_TIMER_PERIOD_SECONDS(20) + +/** + 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. + + @retval EFI_SUCCESS Boot info was successfully retrieved. + @retval EFI_INVALID_PARAMETER Private is NULL. + @retval EFI_NOT_STARTED The driver is in stopped state. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpBootDhcp ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +/** + Disable the use of UEFI HTTP boot function. + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS HTTP boot was successfully disabled. + @retval EFI_NOT_STARTED The driver is already in stopped state. + @retval EFI_INVALID_PARAMETER Private is NULL. + @retval Others Unexpected error when stop the function. + +**/ +EFI_STATUS +HttpBootStop ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +extern EFI_HTTP_BOOT_CALLBACK_PROTOCOL gHttpBootDxeHttpBootCallback; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootSupport.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootSupport.c new file mode 100644 index 00000000..44966fe6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootSupport.c @@ -0,0 +1,779 @@ +/** @file + Support functions implementation for UEFI HTTP boot driver. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +(C) Copyright 2016 - 2020 Hewlett Packard Enterprise Development LP<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HttpBootDxe.h" + + +/** + Get the Nic handle using any child handle in the IPv4 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv4. + + @return NicHandle The pointer to the Nic handle. + @return NULL Can't find the Nic handle. + +**/ +EFI_HANDLE +HttpBootGetNicByIp4Children ( + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_HANDLE NicHandle; + + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid); + if (NicHandle == NULL) { + return NULL; + } + } + + 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. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer The pointer to the buffer for ASCII string. + @param[in] Length The length of the required format. + +**/ +VOID +HttpBootUintnToAscDecWithFormat ( + IN UINTN Number, + IN UINT8 *Buffer, + IN INTN Length + ) +{ + UINTN Remainder; + + for (; Length > 0; Length--) { + Remainder = Number % 10; + Number /= 10; + Buffer[Length - 1] = (UINT8) ('0' + Remainder); + } +} + +/** + This function is to display the IPv4 address. + + @param[in] Ip The pointer to the IPv4 address. + +**/ +VOID +HttpBootShowIp4Addr ( + IN EFI_IPv4_ADDRESS *Ip + ) +{ + UINTN Index; + + for (Index = 0; Index < 4; Index++) { + AsciiPrint ("%d", Ip->Addr[Index]); + if (Index < 3) { + AsciiPrint ("."); + } + } +} + +/** + 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 (":"); + } + } +} + +/** + This function is to display the HTTP error status. + + @param[in] StatusCode The status code value in HTTP message. + +**/ +VOID +HttpBootPrintErrorMessage ( + EFI_HTTP_STATUS_CODE StatusCode + ) +{ + AsciiPrint ("\n"); + + switch (StatusCode) { + case HTTP_STATUS_300_MULTIPLE_CHOICES: + AsciiPrint ("\n Redirection: 300 Multiple Choices"); + break; + + case HTTP_STATUS_301_MOVED_PERMANENTLY: + AsciiPrint ("\n Redirection: 301 Moved Permanently"); + break; + + case HTTP_STATUS_302_FOUND: + AsciiPrint ("\n Redirection: 302 Found"); + break; + + case HTTP_STATUS_303_SEE_OTHER: + AsciiPrint ("\n Redirection: 303 See Other"); + break; + + case HTTP_STATUS_304_NOT_MODIFIED: + AsciiPrint ("\n Redirection: 304 Not Modified"); + break; + + case HTTP_STATUS_305_USE_PROXY: + AsciiPrint ("\n Redirection: 305 Use Proxy"); + break; + + case HTTP_STATUS_307_TEMPORARY_REDIRECT: + AsciiPrint ("\n Redirection: 307 Temporary Redirect"); + break; + + case HTTP_STATUS_308_PERMANENT_REDIRECT: + AsciiPrint ("\n Redirection: 308 Permanent Redirect"); + break; + + case HTTP_STATUS_400_BAD_REQUEST: + AsciiPrint ("\n Client Error: 400 Bad Request"); + break; + + case HTTP_STATUS_401_UNAUTHORIZED: + AsciiPrint ("\n Client Error: 401 Unauthorized"); + break; + + case HTTP_STATUS_402_PAYMENT_REQUIRED: + AsciiPrint ("\n Client Error: 402 Payment Required"); + break; + + case HTTP_STATUS_403_FORBIDDEN: + AsciiPrint ("\n Client Error: 403 Forbidden"); + break; + + case HTTP_STATUS_404_NOT_FOUND: + AsciiPrint ("\n Client Error: 404 Not Found"); + break; + + case HTTP_STATUS_405_METHOD_NOT_ALLOWED: + AsciiPrint ("\n Client Error: 405 Method Not Allowed"); + break; + + case HTTP_STATUS_406_NOT_ACCEPTABLE: + AsciiPrint ("\n Client Error: 406 Not Acceptable"); + break; + + case HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED: + AsciiPrint ("\n Client Error: 407 Proxy Authentication Required"); + break; + + case HTTP_STATUS_408_REQUEST_TIME_OUT: + AsciiPrint ("\n Client Error: 408 Request Timeout"); + break; + + case HTTP_STATUS_409_CONFLICT: + AsciiPrint ("\n Client Error: 409 Conflict"); + break; + + case HTTP_STATUS_410_GONE: + AsciiPrint ("\n Client Error: 410 Gone"); + break; + + case HTTP_STATUS_411_LENGTH_REQUIRED: + AsciiPrint ("\n Client Error: 411 Length Required"); + break; + + case HTTP_STATUS_412_PRECONDITION_FAILED: + AsciiPrint ("\n Client Error: 412 Precondition Failed"); + break; + + case HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE: + AsciiPrint ("\n Client Error: 413 Request Entity Too Large"); + break; + + case HTTP_STATUS_414_REQUEST_URI_TOO_LARGE: + AsciiPrint ("\n Client Error: 414 Request URI Too Long"); + break; + + case HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE: + AsciiPrint ("\n Client Error: 415 Unsupported Media Type"); + break; + + case HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED: + AsciiPrint ("\n Client Error: 416 Requested Range Not Satisfiable"); + break; + + case HTTP_STATUS_417_EXPECTATION_FAILED: + AsciiPrint ("\n Client Error: 417 Expectation Failed"); + break; + + case HTTP_STATUS_500_INTERNAL_SERVER_ERROR: + AsciiPrint ("\n Server Error: 500 Internal Server Error"); + break; + + case HTTP_STATUS_501_NOT_IMPLEMENTED: + AsciiPrint ("\n Server Error: 501 Not Implemented"); + break; + + case HTTP_STATUS_502_BAD_GATEWAY: + AsciiPrint ("\n Server Error: 502 Bad Gateway"); + break; + + case HTTP_STATUS_503_SERVICE_UNAVAILABLE: + AsciiPrint ("\n Server Error: 503 Service Unavailable"); + break; + + case HTTP_STATUS_504_GATEWAY_TIME_OUT: + AsciiPrint ("\n Server Error: 504 Gateway Timeout"); + break; + + case HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED: + AsciiPrint ("\n Server Error: 505 HTTP Version Not Supported"); + break; + + default: ; + + } +} + +/** + 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->Ip6Nic->ImageHandle, + &gEfiDns6ServiceBindingProtocolGuid, + &Dns6Handle + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->OpenProtocol ( + Dns6Handle, + &gEfiDns6ProtocolGuid, + (VOID **) &Dns6, + Private->Ip6Nic->ImageHandle, + 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->Ip6Nic->ImageHandle, + Private->Controller + ); + } + + if (Dns6Handle != NULL) { + NetLibDestroyServiceChild ( + Private->Controller, + Private->Ip6Nic->ImageHandle, + &gEfiDns6ServiceBindingProtocolGuid, + Dns6Handle + ); + } + + if (DnsServerList != NULL) { + FreePool (DnsServerList); + } + + return Status; +} + +/** + This function checks the HTTP(S) URI scheme. + + @param[in] Uri The pointer to the URI string. + + @retval EFI_SUCCESS The URI scheme is valid. + @retval EFI_INVALID_PARAMETER The URI scheme is not HTTP or HTTPS. + @retval EFI_ACCESS_DENIED HTTP is disabled and the URI is HTTP. + +**/ +EFI_STATUS +HttpBootCheckUriScheme ( + IN CHAR8 *Uri + ) +{ + UINTN Index; + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + // + // Convert the scheme to all lower case. + // + for (Index = 0; Index < AsciiStrLen (Uri); Index++) { + if (Uri[Index] == ':') { + break; + } + if (Uri[Index] >= 'A' && Uri[Index] <= 'Z') { + Uri[Index] -= (CHAR8)('A' - 'a'); + } + } + + // + // Return EFI_INVALID_PARAMETER if the URI is not HTTP or HTTPS. + // + if ((AsciiStrnCmp (Uri, "http://", 7) != 0) && (AsciiStrnCmp (Uri, "https://", 8) != 0)) { + DEBUG ((EFI_D_ERROR, "HttpBootCheckUriScheme: Invalid Uri.\n")); + return EFI_INVALID_PARAMETER; + } + + // + // HTTP is disabled, return EFI_ACCESS_DENIED if the URI is HTTP. + // + if (!PcdGetBool (PcdAllowHttpConnections) && (AsciiStrnCmp (Uri, "http://", 7) == 0)) { + DEBUG ((EFI_D_ERROR, "HttpBootCheckUriScheme: HTTP is disabled.\n")); + return EFI_ACCESS_DENIED; + } + + return Status; +} + +/** + Get the URI address string from the input device path. + + Caller need to free the buffer in the UriAddress pointer. + + @param[in] FilePath Pointer to the device path which contains a URI device path node. + @param[out] UriAddress The URI address string extract from the device path. + + @retval EFI_SUCCESS The URI string is returned. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + +**/ +EFI_STATUS +HttpBootParseFilePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + OUT CHAR8 **UriAddress + ) +{ + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + URI_DEVICE_PATH *UriDevicePath; + CHAR8 *Uri; + UINTN UriStrLength; + + if (FilePath == NULL) { + return EFI_INVALID_PARAMETER; + } + + *UriAddress = NULL; + + // + // Extract the URI address from the FilePath + // + TempDevicePath = FilePath; + while (!IsDevicePathEnd (TempDevicePath)) { + if ((DevicePathType (TempDevicePath) == MESSAGING_DEVICE_PATH) && + (DevicePathSubType (TempDevicePath) == MSG_URI_DP)) { + UriDevicePath = (URI_DEVICE_PATH*) TempDevicePath; + // + // UEFI Spec doesn't require the URI to be a NULL-terminated string + // So we allocate a new buffer and always append a '\0' to it. + // + UriStrLength = DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL); + if (UriStrLength == 0) { + // + // return a NULL UriAddress if it's a empty URI device path node. + // + break; + } + Uri = AllocatePool (UriStrLength + 1); + if (Uri == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (Uri, UriDevicePath->Uri, DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL)); + Uri[DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL)] = '\0'; + + *UriAddress = Uri; + } + TempDevicePath = NextDevicePathNode (TempDevicePath); + } + + return EFI_SUCCESS; +} + +/** + This function returns the image type according to server replied HTTP message + and also the image's URI info. + + @param[in] Uri The pointer to the image's URI string. + @param[in] UriParser URI Parse result returned by NetHttpParseUrl(). + @param[in] HeaderCount Number of HTTP header structures in Headers list. + @param[in] Headers Array containing list of HTTP headers. + @param[out] ImageType The image type of the downloaded file. + + @retval EFI_SUCCESS The image type is returned in ImageType. + @retval EFI_INVALID_PARAMETER ImageType, Uri or UriParser is NULL. + @retval EFI_INVALID_PARAMETER HeaderCount is not zero, and Headers is NULL. + @retval EFI_NOT_FOUND Failed to identify the image type. + @retval Others Unexpected error happened. + +**/ +EFI_STATUS +HttpBootCheckImageType ( + IN CHAR8 *Uri, + IN VOID *UriParser, + IN UINTN HeaderCount, + IN EFI_HTTP_HEADER *Headers, + OUT HTTP_BOOT_IMAGE_TYPE *ImageType + ) +{ + EFI_STATUS Status; + EFI_HTTP_HEADER *Header; + CHAR8 *FilePath; + CHAR8 *FilePost; + + if (Uri == NULL || UriParser == NULL || ImageType == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (HeaderCount != 0 && Headers == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Determine the image type by the HTTP Content-Type header field first. + // "application/efi" -> EFI Image + // "application/vnd.efi-iso" -> CD/DVD Image + // "application/vnd.efi-img" -> Virtual Disk Image + // + Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_TYPE); + if (Header != NULL) { + if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_EFI) == 0) { + *ImageType = ImageTypeEfi; + return EFI_SUCCESS; + } else if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_ISO) == 0) { + *ImageType = ImageTypeVirtualCd; + return EFI_SUCCESS; + } else if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_IMG) == 0) { + *ImageType = ImageTypeVirtualDisk; + return EFI_SUCCESS; + } + } + + // + // Determine the image type by file extension: + // *.efi -> EFI Image + // *.iso -> CD/DVD Image + // *.img -> Virtual Disk Image + // + Status = HttpUrlGetPath ( + Uri, + UriParser, + &FilePath + ); + if (EFI_ERROR (Status)) { + return Status; + } + + FilePost = FilePath + AsciiStrLen (FilePath) - 4; + if (AsciiStrCmp (FilePost, ".efi") == 0) { + *ImageType = ImageTypeEfi; + } else if (AsciiStrCmp (FilePost, ".iso") == 0) { + *ImageType = ImageTypeVirtualCd; + } else if (AsciiStrCmp (FilePost, ".img") == 0) { + *ImageType = ImageTypeVirtualDisk; + } else { + *ImageType = ImageTypeMax; + } + + FreePool (FilePath); + + return (*ImageType < ImageTypeMax) ? EFI_SUCCESS : EFI_NOT_FOUND; +} + +/** + This function register the RAM disk info to the system. + + @param[in] Private The pointer to the driver's private data. + @param[in] BufferSize The size of Buffer in bytes. + @param[in] Buffer The base address of the RAM disk. + @param[in] ImageType The image type of the file in Buffer. + + @retval EFI_SUCCESS The RAM disk has been registered. + @retval EFI_NOT_FOUND No RAM disk protocol instances were found. + @retval EFI_UNSUPPORTED The ImageType is not supported. + @retval Others Unexpected error happened. + +**/ +EFI_STATUS +HttpBootRegisterRamDisk ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN UINTN BufferSize, + IN VOID *Buffer, + IN HTTP_BOOT_IMAGE_TYPE ImageType + ) +{ + EFI_RAM_DISK_PROTOCOL *RamDisk; + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_GUID *RamDiskType; + + ASSERT (Private != NULL); + ASSERT (Buffer != NULL); + ASSERT (BufferSize != 0); + + Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID**) &RamDisk); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HTTP Boot: Couldn't find the RAM Disk protocol - %r\n", Status)); + return Status; + } + + if (ImageType == ImageTypeVirtualCd) { + RamDiskType = &gEfiVirtualCdGuid; + } else if (ImageType == ImageTypeVirtualDisk) { + RamDiskType = &gEfiVirtualDiskGuid; + } else { + return EFI_UNSUPPORTED; + } + + Status = RamDisk->Register ( + (UINTN)Buffer, + (UINT64)BufferSize, + RamDiskType, + Private->UsingIpv6 ? Private->Ip6Nic->DevicePath : Private->Ip4Nic->DevicePath, + &DevicePath + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HTTP Boot: Failed to register RAM Disk - %r\n", Status)); + } + + return Status; +} + +/** + Indicate if the HTTP status code indicates a redirection. + + @param[in] StatusCode HTTP status code from server. + + @return TRUE if it's redirection. + +**/ +BOOLEAN +HttpBootIsHttpRedirectStatusCode ( + IN EFI_HTTP_STATUS_CODE StatusCode + ) +{ + if (StatusCode == HTTP_STATUS_301_MOVED_PERMANENTLY || + StatusCode == HTTP_STATUS_302_FOUND || + StatusCode == HTTP_STATUS_307_TEMPORARY_REDIRECT || + StatusCode == HTTP_STATUS_308_PERMANENT_REDIRECT) { + return TRUE; + } + + return FALSE; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootSupport.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootSupport.h new file mode 100644 index 00000000..ea1de45e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootSupport.h @@ -0,0 +1,215 @@ +/** @file + Support functions declaration for UEFI HTTP boot driver. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +(C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_HTTP_BOOT_SUPPORT_H__ +#define __EFI_HTTP_BOOT_SUPPORT_H__ + +/** + Get the Nic handle using any child handle in the IPv4 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv4. + + @return NicHandle The pointer to the Nic handle. + @return NULL Can't find the Nic handle. + +**/ +EFI_HANDLE +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. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer The pointer to the buffer for ASCII string. + @param[in] Length The length of the required format. + +**/ +VOID +HttpBootUintnToAscDecWithFormat ( + IN UINTN Number, + IN UINT8 *Buffer, + IN INTN Length + ); + + +/** + This function is to display the IPv4 address. + + @param[in] Ip The pointer to the IPv4 address. + +**/ +VOID +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 + ); + +/** + This function is to display the HTTP error status. + + @param[in] StatusCode The status code value in HTTP message. + +**/ +VOID +HttpBootPrintErrorMessage ( + EFI_HTTP_STATUS_CODE StatusCode + ); + +/** + 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 + ); + +/** + This function checks the HTTP(S) URI scheme. + + @param[in] Uri The pointer to the URI string. + + @retval EFI_SUCCESS The URI scheme is valid. + @retval EFI_INVALID_PARAMETER The URI scheme is not HTTP or HTTPS. + @retval EFI_ACCESS_DENIED HTTP is disabled and the URI is HTTP. + +**/ +EFI_STATUS +HttpBootCheckUriScheme ( + IN CHAR8 *Uri + ); + +/** + Get the URI address string from the input device path. + + Caller need to free the buffer in the UriAddress pointer. + + @param[in] FilePath Pointer to the device path which contains a URI device path node. + @param[out] UriAddress The URI address string extract from the device path. + + @retval EFI_SUCCESS The URI string is returned. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + +**/ +EFI_STATUS +HttpBootParseFilePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + OUT CHAR8 **UriAddress + ); + +/** + This function returns the image type according to server replied HTTP message + and also the image's URI info. + + @param[in] Uri The pointer to the image's URI string. + @param[in] UriParser URI Parse result returned by NetHttpParseUrl(). + @param[in] HeaderCount Number of HTTP header structures in Headers list. + @param[in] Headers Array containing list of HTTP headers. + @param[out] ImageType The image type of the downloaded file. + + @retval EFI_SUCCESS The image type is returned in ImageType. + @retval EFI_INVALID_PARAMETER ImageType, Uri or UriParser is NULL. + @retval EFI_INVALID_PARAMETER HeaderCount is not zero, and Headers is NULL. + @retval EFI_NOT_FOUND Failed to identify the image type. + @retval Others Unexpected error happened. + +**/ +EFI_STATUS +HttpBootCheckImageType ( + IN CHAR8 *Uri, + IN VOID *UriParser, + IN UINTN HeaderCount, + IN EFI_HTTP_HEADER *Headers, + OUT HTTP_BOOT_IMAGE_TYPE *ImageType + ); + +/** + This function register the RAM disk info to the system. + + @param[in] Private The pointer to the driver's private data. + @param[in] BufferSize The size of Buffer in bytes. + @param[in] Buffer The base address of the RAM disk. + @param[in] ImageType The image type of the file in Buffer. + + @retval EFI_SUCCESS The RAM disk has been registered. + @retval EFI_NOT_FOUND No RAM disk protocol instances were found. + @retval EFI_UNSUPPORTED The ImageType is not supported. + @retval Others Unexpected error happened. + +**/ +EFI_STATUS +HttpBootRegisterRamDisk ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN UINTN BufferSize, + IN VOID *Buffer, + IN HTTP_BOOT_IMAGE_TYPE ImageType + ); + +/** + Indicate if the HTTP status code indicates a redirection. + + @param[in] StatusCode HTTP status code from server. + + @return TRUE if it's redirection. + +**/ +BOOLEAN +HttpBootIsHttpRedirectStatusCode ( + IN EFI_HTTP_STATUS_CODE StatusCode + ); +#endif |