diff options
author | Fu Siyuan <siyuan.fu@intel.com> | 2015-07-07 09:29:28 +0000 |
---|---|---|
committer | sfu5 <sfu5@Edk2> | 2015-07-07 09:29:28 +0000 |
commit | c4545d769fc26d6bb70c4236eab26df498ed784e (patch) | |
tree | cab080611aece0aff6587bdb1a4d740d0a9b7169 /NetworkPkg/HttpBootDxe | |
parent | eb4d0b5e85f0c2657e1fbb1156a4ec9bbeb0ed27 (diff) | |
download | edk2-platforms-c4545d769fc26d6bb70c4236eab26df498ed784e.tar.xz |
NetworkPkg: Add UEFI HTTP boot driver.
This patch add the implementation for UEFI HTTP boot driver.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Fu Siyuan <siyuan.fu@intel.com>
Reviewed-by: Ye Ting <ting.ye@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17857 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'NetworkPkg/HttpBootDxe')
-rw-r--r-- | NetworkPkg/HttpBootDxe/HttpBootClient.c | 830 | ||||
-rw-r--r-- | NetworkPkg/HttpBootDxe/HttpBootClient.h | 139 | ||||
-rw-r--r-- | NetworkPkg/HttpBootDxe/HttpBootComponentName.c | 180 | ||||
-rw-r--r-- | NetworkPkg/HttpBootDxe/HttpBootComponentName.h | 99 | ||||
-rw-r--r-- | NetworkPkg/HttpBootDxe/HttpBootDhcp4.c | 829 | ||||
-rw-r--r-- | NetworkPkg/HttpBootDxe/HttpBootDhcp4.h | 279 | ||||
-rw-r--r-- | NetworkPkg/HttpBootDxe/HttpBootDxe.c | 559 | ||||
-rw-r--r-- | NetworkPkg/HttpBootDxe/HttpBootDxe.h | 303 | ||||
-rw-r--r-- | NetworkPkg/HttpBootDxe/HttpBootDxe.inf | 68 | ||||
-rw-r--r-- | NetworkPkg/HttpBootDxe/HttpBootDxe.uni | bin | 0 -> 2008 bytes | |||
-rw-r--r-- | NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni | bin | 0 -> 1328 bytes | |||
-rw-r--r-- | NetworkPkg/HttpBootDxe/HttpBootImpl.c | 352 | ||||
-rw-r--r-- | NetworkPkg/HttpBootDxe/HttpBootImpl.h | 50 | ||||
-rw-r--r-- | NetworkPkg/HttpBootDxe/HttpBootSupport.c | 590 | ||||
-rw-r--r-- | NetworkPkg/HttpBootDxe/HttpBootSupport.h | 250 |
15 files changed, 4528 insertions, 0 deletions
diff --git a/NetworkPkg/HttpBootDxe/HttpBootClient.c b/NetworkPkg/HttpBootDxe/HttpBootClient.c new file mode 100644 index 0000000000..2bf28c2c4d --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootClient.c @@ -0,0 +1,830 @@ +/** @file + Implementation of the boot file download function. + +Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpBootDxe.h" + +/** + Update the IP and URL 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 *TmpDevicePath; + EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; + UINTN Length; + EFI_STATUS Status; + + TmpDevicePath = 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)); + + TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + if (TmpDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } else { + ASSERT (FALSE); + } + + // + // Update the URI node with the boot file URI. + // + Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (Private->BootFileUri); + Node = AllocatePool (Length); + if (Node == NULL) { + FreePool (TmpDevicePath); + 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)); + + NewDevicePath = AppendDevicePathNode (TmpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + FreePool (TmpDevicePath); + if (NewDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Reinstall the device path protocol of the child handle. + // + Status = gBS->ReinstallProtocolInterface ( + Private->ChildHandle, + &gEfiDevicePathProtocolGuid, + Private->DevicePath, + NewDevicePath + ); + if (EFI_ERROR (Status)) { + return Status; + } + + FreePool (Private->DevicePath); + Private->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 +HttpBootExtractUriInfo ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + HTTP_BOOT_DHCP4_PACKET_CACHE *SelectOffer; + HTTP_BOOT_DHCP4_PACKET_CACHE *HttpOffer; + UINT32 SelectIndex; + UINT32 ProxyIndex; + 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); + + 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 ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) { + HttpOffer = SelectOffer; + } else { + ASSERT (Private->SelectProxyType != HttpOfferTypeMax); + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; + HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4; + } + + // + // Configure the default DNS server if server assigned. + // + if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || (SelectOffer->OfferType == HttpOfferTypeDhcpDns)) { + Option = SelectOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER]; + ASSERT (Option != NULL); + Status = HttpBootRegisterIp4Dns ( + Private, + Option->Length, + Option->Data + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Extract the port from URL, and use default HTTP port 80 if not provided. + // + Status = HttpUrlGetPort ( + (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data, + HttpOffer->UriParser, + &Private->Port + ); + if (EFI_ERROR (Status) || Private->Port == 0) { + Private->Port = 80; + } + + // + // Record the URI of boot file from the selected HTTP offer. + // + Private->BootFileUriParser = HttpOffer->UriParser; + Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data; + + + // + // All boot informations are valid here. + // + AsciiPrint ("\n URI: %a", Private->BootFileUri); + + // + // Update the device path to include the IP and boot URI information. + // + Status = HttpBootUpdateDevicePath (Private); + + 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 = HttpBootExtractUriInfo (Private); + } else { + ASSERT (FALSE); + } + + return Status; +} + +/** + 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; + + 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); + } else { + ASSERT (FALSE); + } + + Status = HttpIoCreateIo ( + Private->Image, + Private->Controller, + Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4, + &ConfigData, + &Private->HttpIo + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Private->HttpCreated = TRUE; + return EFI_SUCCESS; +} + +/** + 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. + + @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 + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + HTTP_BOOT_CACHE_CONTENT *Cache; + HTTP_BOOT_ENTITY_DATA *EntityData; + UINTN CopyedSize; + + if (Uri == NULL || BufferSize == 0 || Buffer == 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 cache, 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; +} + +/** + 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); + } +} + +/** + 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; + + // + // We only care about the entity data. + // + if (EventType != BodyParseEventOnData) { + return EFI_SUCCESS; + } + + CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context; + + // + // Save the data into cache list. + // + 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); + + // + // 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); + } + + 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. + + @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 + ) +{ + EFI_STATUS Status; + CHAR8 *HostName; + EFI_HTTP_REQUEST_DATA *RequestData; + HTTP_IO_RESOPNSE_DATA *ResponseData; + HTTP_IO_RESOPNSE_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; + CHAR16 *Url; + + ASSERT (Private != NULL); + ASSERT (Private->HttpCreated); + + if (BufferSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (*BufferSize != 0 && Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // First, check whether we already cached the requested Uri. + // + Url = AllocatePool ((AsciiStrLen (Private->BootFileUri) + 1) * sizeof (CHAR16)); + if (Url == NULL) { + return EFI_OUT_OF_RESOURCES; + } + AsciiStrToUnicodeStr (Private->BootFileUri, Url); + if (!HeaderOnly) { + Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer); + 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. + // + Cache = NULL; + if (!HeaderOnly) { + Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT)); + if (Cache == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ERROR_1; + } + 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 = HttpBootCreateHeader (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 = HttpBootSetHeader ( + HttpIoHeader, + HTTP_FIELD_NAME_HOST, + HostName + ); + FreePool (HostName); + if (EFI_ERROR (Status)) { + goto ERROR_3; + } + + // + // Add HTTP header field 2: Accept + // + Status = HttpBootSetHeader ( + HttpIoHeader, + HTTP_FIELD_NAME_ACCEPT, + "*/*" + ); + if (EFI_ERROR (Status)) { + goto ERROR_3; + } + + // + // Add HTTP header field 3: User-Agent + // + Status = HttpBootSetHeader ( + HttpIoHeader, + HTTP_FIELD_NAME_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; + if (RequestData->Url == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ERROR_4; + } + AsciiStrToUnicodeStr (Private->BootFileUri, RequestData->Url); + + // + // 2.3 Record the request info in a temp cache item. + // + if (!HeaderOnly) { + 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_RESOPNSE_DATA)); + if (ResponseData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ERROR_4; + } + Status = HttpIoRecvResponse ( + &Private->HttpIo, + TRUE, + ResponseData + ); + if (EFI_ERROR (Status)) { + goto ERROR_5; + } + + // + // 3.2 Cache the response header. + // + if (!HeaderOnly) { + Cache->ResponseData = ResponseData; + } + + // + // 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; + 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. + // + if (!HeaderOnly) { + ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESOPNSE_DATA)); + while (!HttpIsMessageComplete (Parser)) { + // + // Allocate a new block to hold the message-body. + // + Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE); + if (Block == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ERROR_6; + } + ResponseBody.Body = (CHAR8*) Block; + ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE; + Status = HttpIoRecvResponse ( + &Private->HttpIo, + FALSE, + &ResponseBody + ); + if (EFI_ERROR (Status)) { + goto ERROR_6; + } + + // + // Parse the new received block of the message-body, the block will be saved in cache. + // + Context.NewBlock = TRUE; + Context.Block = Block; + Status = HttpParseMessageBody ( + Parser, + ResponseBody.BodyLength, + ResponseBody.Body + ); + if (EFI_ERROR (Status)) { + goto ERROR_6; + } + } + } + + // + // 3.5 Message-body receive & parse is completed, get the file size. + // + Status = HttpGetEntityLength (Parser, &ContentLength); + if (EFI_ERROR (Status)) { + goto ERROR_6; + } + + if (*BufferSize < ContentLength) { + Status = EFI_BUFFER_TOO_SMALL; + } + *BufferSize = ContentLength; + + // + // 4. Save the cache item to driver's cache list and return. + // + if (!HeaderOnly) { + Cache->EntityLength = ContentLength; + InsertTailList (&Private->CacheList, &Cache->Link); + } + + if (Parser != NULL) { + HttpFreeMsgParser (Parser); + } + + return EFI_SUCCESS; + +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: + HttpBootFreeHeader (HttpIoHeader); +ERROR_2: + if (Cache != NULL) { + FreePool (Cache); + } +ERROR_1: + if (Url != NULL) { + FreePool (Url); + } + + return Status; +} diff --git a/NetworkPkg/HttpBootDxe/HttpBootClient.h b/NetworkPkg/HttpBootDxe/HttpBootClient.h new file mode 100644 index 0000000000..3f6671333f --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootClient.h @@ -0,0 +1,139 @@ +/** @file + Declaration of the boot file download function. + +Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_HTTP_BOOT_HTTP_H__ +#define __EFI_HTTP_BOOT_HTTP_H__ + +#define HTTP_BOOT_REQUEST_TIMEOUT 5000 // 5 seconds in uints of millisecond. +#define HTTP_BOOT_BLOCK_SIZE 1024 + +#define HTTP_FIELD_NAME_USER_AGENT "User-Agent" +#define HTTP_FIELD_NAME_HOST "Host" +#define HTTP_FIELD_NAME_ACCEPT "Accept" + + +#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_RESOPNSE_DATA *ResponseData; // Not include any message-body data. + 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_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. + + @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 + ); + +/** + 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/NetworkPkg/HttpBootDxe/HttpBootComponentName.c b/NetworkPkg/HttpBootDxe/HttpBootComponentName.c new file mode 100644 index 0000000000..4e4a52a8fa --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootComponentName.c @@ -0,0 +1,180 @@ +/** @file + Implementation of EFI_COMPONENT_NAME_PROTOCOL and EFI_COMPONENT_NAME2_PROTOCOL protocol. + +Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpBootDxe.h" + +/// +/// 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) { + 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/NetworkPkg/HttpBootDxe/HttpBootComponentName.h b/NetworkPkg/HttpBootDxe/HttpBootComponentName.h new file mode 100644 index 0000000000..706fa8da5b --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootComponentName.h @@ -0,0 +1,99 @@ +/** @file + Declaration of HTTP boot driver's EFI_COMPONENT_NAME_PROTOCOL and + EFI_COMPONENT_NAME2_PROTOCOL function. + +Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_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/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c b/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c new file mode 100644 index 0000000000..7486d24ead --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c @@ -0,0 +1,829 @@ +/** @file + Functions implementation related with DHCPv4 for HTTP boot driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpBootDxe.h" + +// +// This is a map from the interested DHCP4 option tags' index to the tag value. +// +UINT8 mInterestedDhcp4Tags[HTTP_BOOT_DHCP4_TAG_INDEX_MAX] = { + HTTP_BOOT_DHCP4_TAG_BOOTFILE_LEN, + HTTP_BOOT_DHCP4_TAG_OVERLOAD, + HTTP_BOOT_DHCP4_TAG_MSG_TYPE, + HTTP_BOOT_DHCP4_TAG_SERVER_ID, + HTTP_BOOT_DHCP4_TAG_CLASS_ID, + HTTP_BOOT_DHCP4_TAG_BOOTFILE, + HTTP_BOOT_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 = HTTP_BOOT_DHCP4_TAG_PARA_LIST; + OptList[Index]->Length = 27; + OptEnt.Para = (HTTP_BOOT_DHCP4_OPTION_PARA *) OptList[Index]->Data; + OptEnt.Para->ParaList[0] = HTTP_BOOT_DHCP4_TAG_NETMASK; + OptEnt.Para->ParaList[1] = HTTP_BOOT_DHCP4_TAG_TIME_OFFSET; + OptEnt.Para->ParaList[2] = HTTP_BOOT_DHCP4_TAG_ROUTER; + OptEnt.Para->ParaList[3] = HTTP_BOOT_DHCP4_TAG_TIME_SERVER; + OptEnt.Para->ParaList[4] = HTTP_BOOT_DHCP4_TAG_NAME_SERVER; + OptEnt.Para->ParaList[5] = HTTP_BOOT_DHCP4_TAG_DNS_SERVER; + OptEnt.Para->ParaList[6] = HTTP_BOOT_DHCP4_TAG_HOSTNAME; + OptEnt.Para->ParaList[7] = HTTP_BOOT_DHCP4_TAG_BOOTFILE_LEN; + OptEnt.Para->ParaList[8] = HTTP_BOOT_DHCP4_TAG_DOMAINNAME; + OptEnt.Para->ParaList[9] = HTTP_BOOT_DHCP4_TAG_ROOTPATH; + OptEnt.Para->ParaList[10] = HTTP_BOOT_DHCP4_TAG_EXTEND_PATH; + OptEnt.Para->ParaList[11] = HTTP_BOOT_DHCP4_TAG_EMTU; + OptEnt.Para->ParaList[12] = HTTP_BOOT_DHCP4_TAG_TTL; + OptEnt.Para->ParaList[13] = HTTP_BOOT_DHCP4_TAG_BROADCAST; + OptEnt.Para->ParaList[14] = HTTP_BOOT_DHCP4_TAG_NIS_DOMAIN; + OptEnt.Para->ParaList[15] = HTTP_BOOT_DHCP4_TAG_NIS_SERVER; + OptEnt.Para->ParaList[16] = HTTP_BOOT_DHCP4_TAG_NTP_SERVER; + OptEnt.Para->ParaList[17] = HTTP_BOOT_DHCP4_TAG_VENDOR; + OptEnt.Para->ParaList[18] = HTTP_BOOT_DHCP4_TAG_REQUEST_IP; + OptEnt.Para->ParaList[19] = HTTP_BOOT_DHCP4_TAG_LEASE; + OptEnt.Para->ParaList[20] = HTTP_BOOT_DHCP4_TAG_SERVER_ID; + OptEnt.Para->ParaList[21] = HTTP_BOOT_DHCP4_TAG_T1; + OptEnt.Para->ParaList[22] = HTTP_BOOT_DHCP4_TAG_T2; + OptEnt.Para->ParaList[23] = HTTP_BOOT_DHCP4_TAG_CLASS_ID; + OptEnt.Para->ParaList[25] = HTTP_BOOT_DHCP4_TAG_BOOTFILE; + OptEnt.Para->ParaList[26] = HTTP_BOOT_DHCP4_TAG_UUID; + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append UUID/Guid-based client identifier option + // + OptList[Index]->OpCode = HTTP_BOOT_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 programable 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 = HTTP_BOOT_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 = HTTP_BOOT_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 = HTTP_BOOT_DHCP4_TAG_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 != HTTP_BOOT_DHCP4_TAG_EOP) { + + if (Option->OpCode == OptTag) { + // + // Found the required option. + // + return Option; + } + + // + // Skip the current option to the next. + // + if (Option->OpCode == HTTP_BOOT_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. + +**/ +VOID +HttpBootCacheDhcp4Packet ( + IN EFI_DHCP4_PACKET *Dst, + IN EFI_DHCP4_PACKET *Src + ) +{ + ASSERT (Dst->Size >= Src->Length); + + CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length); + Dst->Length = Src->Length; +} + +/** + 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; + + IsDnsOffer = FALSE; + IpExpressedUri = FALSE; + IsProxyOffer = FALSE; + IsHttpOffer = 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) { + 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 >= 9) && + (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 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 (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) { + OfferType = IsProxyOffer ? HttpOfferTypeProxyIpUri : HttpOfferTypeDhcpIpUri; + } else { + if (!IsProxyOffer) { + OfferType = IsDnsOffer ? HttpOfferTypeDhcpNameUriDns : HttpOfferTypeDhcpNameUri; + } else { + OfferType = HttpOfferTypeProxyNameUri; + } + } + + } else { + if (!IsProxyOffer) { + OfferType = IsDnsOffer ? HttpOfferTypeDhcpDns : HttpOfferTypeDhcpOnly; + } else { + return EFI_DEVICE_ERROR; + } + } + + 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. + +**/ +VOID +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; + + 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. + // + HttpBootCacheDhcp4Packet (Offer, RcvdOffer); + + // + // Validate the DHCPv4 packet, and parse the options and offer type. + // + if (EFI_ERROR (HttpBootParseDhcp4Packet (Cache4))) { + return; + } + + // + // 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++; +} + +/** + Select an DHCPv4 offer, and record SelectIndex and SelectProxyType. + + @param[in] Private Pointer to HTTP boot driver private data. + +**/ +VOID +HttpBootSelectDhcp4Offer ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + Private->SelectIndex = 0; + Private->SelectProxyType = HttpOfferTypeMax; + + // + // Priority1: HttpOfferTypeDhcpIpUri + // 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[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; + + if ((Dhcp4Event != Dhcp4RcvdOffer) && (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), + HTTP_BOOT_DHCP4_TAG_MAXMSG + ); + if (MaxMsgSize != NULL) { + Value = HTONS (HTTP_BOOT_DHCP4_PACKET_MAX_SIZE); + CopyMem (MaxMsgSize->Data, &Value, sizeof (Value)); + } + + Status = EFI_SUCCESS; + switch (Dhcp4Event) { + case Dhcp4RcvdOffer: + Status = EFI_NOT_READY; + if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) { + // + // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record + // the OfferIndex and OfferCount. + // + HttpBootCacheDhcp4Offer (Private, Packet); + } + break; + + case Dhcp4SelectOffer: + // + // Select offer according to the priority in UEFI spec, and record the SelectIndex + // and SelectProxyType. + // + HttpBootSelectDhcp4Offer (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 +HttpBootSetIpPolicy ( + 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 = HttpBootSetIpPolicy (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/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h b/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h new file mode 100644 index 0000000000..42a3874444 --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h @@ -0,0 +1,279 @@ +/** @file + Functions declaration related with DHCPv4 for HTTP boot driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_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 + +// +// Dhcp Options +// +#define HTTP_BOOT_DHCP4_TAG_PAD 0 // Pad Option +#define HTTP_BOOT_DHCP4_TAG_EOP 255 // End Option +#define HTTP_BOOT_DHCP4_TAG_NETMASK 1 // Subnet Mask +#define HTTP_BOOT_DHCP4_TAG_TIME_OFFSET 2 // Time Offset from UTC +#define HTTP_BOOT_DHCP4_TAG_ROUTER 3 // Router option, +#define HTTP_BOOT_DHCP4_TAG_TIME_SERVER 4 // Time Server +#define HTTP_BOOT_DHCP4_TAG_NAME_SERVER 5 // Name Server +#define HTTP_BOOT_DHCP4_TAG_DNS_SERVER 6 // Domain Name Server +#define HTTP_BOOT_DHCP4_TAG_HOSTNAME 12 // Host Name +#define HTTP_BOOT_DHCP4_TAG_BOOTFILE_LEN 13 // Boot File Size +#define HTTP_BOOT_DHCP4_TAG_DUMP 14 // Merit Dump File +#define HTTP_BOOT_DHCP4_TAG_DOMAINNAME 15 // Domain Name +#define HTTP_BOOT_DHCP4_TAG_ROOTPATH 17 // Root path +#define HTTP_BOOT_DHCP4_TAG_EXTEND_PATH 18 // Extensions Path +#define HTTP_BOOT_DHCP4_TAG_EMTU 22 // Maximum Datagram Reassembly Size +#define HTTP_BOOT_DHCP4_TAG_TTL 23 // Default IP Time-to-live +#define HTTP_BOOT_DHCP4_TAG_BROADCAST 28 // Broadcast Address +#define HTTP_BOOT_DHCP4_TAG_NIS_DOMAIN 40 // Network Information Service Domain +#define HTTP_BOOT_DHCP4_TAG_NIS_SERVER 41 // Network Information Servers +#define HTTP_BOOT_DHCP4_TAG_NTP_SERVER 42 // Network Time Protocol Servers +#define HTTP_BOOT_DHCP4_TAG_VENDOR 43 // Vendor Specific Information +#define HTTP_BOOT_DHCP4_TAG_REQUEST_IP 50 // Requested IP Address +#define HTTP_BOOT_DHCP4_TAG_LEASE 51 // IP Address Lease Time +#define HTTP_BOOT_DHCP4_TAG_OVERLOAD 52 // Option Overload +#define HTTP_BOOT_DHCP4_TAG_MSG_TYPE 53 // DHCP Message Type +#define HTTP_BOOT_DHCP4_TAG_SERVER_ID 54 // Server Identifier +#define HTTP_BOOT_DHCP4_TAG_PARA_LIST 55 // Parameter Request List +#define HTTP_BOOT_DHCP4_TAG_MAXMSG 57 // Maximum DHCP Message Size +#define HTTP_BOOT_DHCP4_TAG_T1 58 // Renewal (T1) Time Value +#define HTTP_BOOT_DHCP4_TAG_T2 59 // Rebinding (T2) Time Value +#define HTTP_BOOT_DHCP4_TAG_CLASS_ID 60 // Vendor class identifier +#define HTTP_BOOT_DHCP4_TAG_CLIENT_ID 61 // Client-identifier +#define HTTP_BOOT_DHCP4_TAG_TFTP 66 // TFTP server name +#define HTTP_BOOT_DHCP4_TAG_BOOTFILE 67 // Bootfile name +#define HTTP_BOOT_DHCP4_TAG_ARCH 93 +#define HTTP_BOOT_DHCP4_TAG_UNDI 94 +#define HTTP_BOOT_DHCP4_TAG_UUID 97 + +#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 0x000F +#elif defined (MDE_CPU_X64) +#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE 0x0010 +#elif defined (MDE_CPU_ARM) +#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE 0x0012 +#elif defined (MDE_CPU_AARCH64) +#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE 0x0013 +#endif + +/// DHCP offer types among HTTP boot. +/// Dhcp4 and Dhcp6 share this definition, and corresponding +/// relatioinship is as follows: +/// Dhcp4Discover <> Dhcp6Solicit +/// Dhcp4Offer <> Dhcp6Advertise +/// Dhcp4Request <> Dhcp6Request +/// Dhcp4Ack <> DHcp6Reply +/// +typedef enum { + // + // <IP address, IP expressed URI> or + // <IP address, IP expressed URI, Name-server (will be ignored)> + // + HttpOfferTypeDhcpIpUri, + // + // <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; + +typedef union { + EFI_DHCP4_PACKET Offer; + EFI_DHCP4_PACKET Ack; + UINT8 Buffer[HTTP_BOOT_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; + +/** + 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/NetworkPkg/HttpBootDxe/HttpBootDxe.c b/NetworkPkg/HttpBootDxe/HttpBootDxe.c new file mode 100644 index 0000000000..0d2c7abd30 --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootDxe.c @@ -0,0 +1,559 @@ +/** @file + Driver Binding functions implementation for UEFI HTTP boot. + +Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpBootDxe.h" + +/// +/// Driver Binding Protocol instance +/// +EFI_DRIVER_BINDING_PROTOCOL gHttpBootIp4DxeDriverBinding = { + HttpBootIp4DxeDriverBindingSupported, + HttpBootIp4DxeDriverBindingStart, + HttpBootIp4DxeDriverBindingStop, + HTTP_BOOT_DXE_VERSION, + NULL, + NULL +}; + +/** + 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); + ASSERT (Private->UsingIpv6 == FALSE); + + if (Private->Dhcp4Child != NULL) { + gBS->CloseProtocol ( + Private->Dhcp4Child, + &gEfiDhcp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + Private->Dhcp4Child + ); + } + + if (Private->HttpCreated) { + HttpIoDestroyIo (&Private->HttpIo); + Private->HttpCreated = FALSE; + } + + gBS->CloseProtocol ( + Private->Controller, + &gEfiCallerIdGuid, + This->DriverBindingHandle, + Private->ChildHandle + ); + + gBS->UninstallMultipleProtocolInterfaces ( + Private->ChildHandle, + &gEfiLoadFileProtocolGuid, + &Private->LoadFile, + &gEfiDevicePathProtocolGuid, + Private->DevicePath, + NULL + ); + + if (Private->DevicePath != NULL) { + FreePool (Private->DevicePath); + Private->DevicePath = 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 failded 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; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + // + // Initialize the private data structure. + // + Private = AllocateZeroPool (sizeof (HTTP_BOOT_PRIVATE_DATA)); + if (Private == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Private->Signature = HTTP_BOOT_PRIVATE_DATA_SIGNATURE; + Private->Controller = ControllerHandle; + Private->Image = This->ImageHandle; + Private->UsingIpv6 = FALSE; + InitializeListHead (&Private->CacheList); + + // + // Create DHCP 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; + } + + // + // Get the NII interface if it exists, it's not required. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + (VOID **) &Private->Nii, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Private->Nii = NULL; + } + + // + // Open Device Path Protocol to prepare for appending IP and URI node. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &Private->ParentDevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Append IPv4 device path node. + // + 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->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + FreePool (DevicePath); + if (Private->DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Create a child handle for the HTTP boot and install DevPath and Load file protocol on it. + // + CopyMem (&Private->LoadFile, &gHttpBootDxeLoadFile, sizeof (Private->LoadFile)); + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->ChildHandle, + &gEfiLoadFileProtocolGuid, + &Private->LoadFile, + &gEfiDevicePathProtocolGuid, + Private->DevicePath, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Install a protocol with Caller Id Guid to the NIC, this is just to build the relationship between + // NIC handle and the private data. + // + Status = gBS->InstallProtocolInterface ( + &ControllerHandle, + &gEfiCallerIdGuid, + EFI_NATIVE_INTERFACE, + &Private->Id + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Open the Caller Id child to setup a parent-child relationship between + // real NIC handle and the HTTP boot child handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + Private->ChildHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + + HttpBootDestroyIp4Children (This, Private); + FreePool (Private); + + return Status; +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +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; + } + + // + // Destory all child instance and uninstall protocol interface. + // + HttpBootDestroyIp4Children (This, Private); + + // + // Release the cached data. + // + HttpBootFreeCacheList (Private); + + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiCallerIdGuid, + &Private->Id + ); + FreePool (Private); + + return EFI_SUCCESS; +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + @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 + ) +{ + // + // Install UEFI Driver Model protocol(s). + // + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gHttpBootIp4DxeDriverBinding, + ImageHandle, + &gHttpBootDxeComponentName, + &gHttpBootDxeComponentName2 + ); +} + diff --git a/NetworkPkg/HttpBootDxe/HttpBootDxe.h b/NetworkPkg/HttpBootDxe/HttpBootDxe.h new file mode 100644 index 0000000000..8fce03df06 --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootDxe.h @@ -0,0 +1,303 @@ +/** @file + UEFI HTTP boot driver's private data structure and interfaces declaration. + +Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_HTTP_BOOT_DXE_H__ +#define __EFI_HTTP_BOOT_DXE_H__ + +#include <Uefi.h> + +// +// Libraries +// +#include <Library/UefiBootServicesTableLib.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> + +// +// UEFI Driver Model Protocols +// +#include <Protocol/DriverBinding.h> +#include <Protocol/ComponentName2.h> +#include <Protocol/ComponentName.h> + +// +// Consumed Protocols +// +#include <Protocol/NetworkInterfaceIdentifier.h> +#include <Protocol/Dhcp4.h> +#include <Protocol/Http.h> +#include <Protocol/Ip4Config2.h> + +// +// Produced Protocols +// +#include <Protocol/LoadFile.h> + +// +// Driver Version +// +#define HTTP_BOOT_DXE_VERSION 0xa + +// +// 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; + +// +// Include files with internal function prototypes +// +#include "HttpBootComponentName.h" +#include "HttpBootDhcp4.h" +#include "HttpBootImpl.h" +#include "HttpBootSupport.h" +#include "HttpBootClient.h" + +typedef union { + HTTP_BOOT_DHCP4_PACKET_CACHE Dhcp4; +} HTTP_BOOT_DHCP_PACKET_CACHE; + +struct _HTTP_BOOT_PRIVATE_DATA { + UINT32 Signature; + EFI_HANDLE Controller; + EFI_HANDLE Image; + + // + // Cousumed children + // + EFI_HANDLE Dhcp4Child; + HTTP_IO HttpIo; + BOOLEAN HttpCreated; + + // + // Consumed protocol + // + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + EFI_DHCP4_PROTOCOL *Dhcp4; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + + // + // Produced children + // + EFI_HANDLE ChildHandle; + + // + // Produced protocol + // + EFI_LOAD_FILE_PROTOCOL LoadFile; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINT32 Id; + + // + // Mode data + // + BOOLEAN UsingIpv6; + BOOLEAN Started; + EFI_IP_ADDRESS StationIp; + EFI_IP_ADDRESS SubnetMask; + EFI_IP_ADDRESS GatewayIp; + UINT16 Port; + CHAR8 *BootFileUri; + VOID *BootFileUriParser; + UINTN BootFileSize; + + // + // 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_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) + +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 failded 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 + ); + +#endif diff --git a/NetworkPkg/HttpBootDxe/HttpBootDxe.inf b/NetworkPkg/HttpBootDxe/HttpBootDxe.inf new file mode 100644 index 0000000000..a60830400c --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootDxe.inf @@ -0,0 +1,68 @@ +## @file +# This modules produce the Load File Protocol for UEFI HTTP boot. +# +# Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = 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 + +[Sources] + HttpBootDxe.h + HttpBootDxe.c + HttpBootComponentName.h + HttpBootComponentName.c + HttpBootImpl.h + HttpBootImpl.c + HttpBootDhcp4.h + HttpBootDhcp4.c + HttpBootSupport.h + HttpBootSupport.c + HttpBootClient.h + HttpBootClient.c + +[LibraryClasses] + UefiDriverEntryPoint + UefiBootServicesTableLib + MemoryAllocationLib + BaseLib + UefiLib + DevicePathLib + DebugLib + NetLib + HttpLib + +[Protocols] + ## TO_START + ## BY_START + gEfiDevicePathProtocolGuid + + gEfiLoadFileProtocolGuid ## BY_START + gEfiHttpServiceBindingProtocolGuid ## CONSUMES + gEfiHttpProtocolGuid ## CONSUMES + gEfiDhcp4ServiceBindingProtocolGuid ## TO_START + gEfiDhcp4ProtocolGuid ## TO_START + gEfiIp4Config2ProtocolGuid ## TO_START + gEfiNetworkInterfaceIdentifierProtocolGuid_31 ## SOMETIMES_CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + HttpBootDxeExtra.uni diff --git a/NetworkPkg/HttpBootDxe/HttpBootDxe.uni b/NetworkPkg/HttpBootDxe/HttpBootDxe.uni Binary files differnew file mode 100644 index 0000000000..68a33794b2 --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootDxe.uni diff --git a/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni b/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni Binary files differnew file mode 100644 index 0000000000..2d45c4cda5 --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni diff --git a/NetworkPkg/HttpBootDxe/HttpBootImpl.c b/NetworkPkg/HttpBootDxe/HttpBootImpl.c new file mode 100644 index 0000000000..711cc3c8c5 --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootImpl.c @@ -0,0 +1,352 @@ +/** @file + The implementation of EFI_LOAD_FILE_PROTOCOL for UEFI HTTP boot. + +Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpBootDxe.h" + +/** + Enable 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 enabled. + @retval EFI_INVALID_PARAMETER Private is NULL. + @retval EFI_ALREADY_STARTED The driver is already in started state. + +**/ +EFI_STATUS +HttpBootStart ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + UINTN Index; + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Private->Started) { + return EFI_ALREADY_STARTED; + } + + if (!Private->UsingIpv6) { + // + // Init the content of cached DHCP offer list. + // + ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer)); + for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) { + Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = HTTP_BOOT_DHCP4_PACKET_MAX_SIZE; + } + } else { + ASSERT (FALSE); + } + + Private->Started = TRUE; + + return EFI_SUCCESS; +} + +/** + Attempt to complete a DHCPv4 D.O.R.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) { + Status = HttpBootDhcp4Dora (Private); + } else { + ASSERT (FALSE); + } + + 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. + + @retval EFI_SUCCESS Boot file was loaded successfully. + @retval EFI_INVALID_PARAMETER Private 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 + ) +{ + EFI_STATUS Status; + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!Private->Started) { + return EFI_NOT_STARTED; + } + + Status = EFI_DEVICE_ERROR; + + if (Private->BootFileUri == NULL) { + // + // Parse the cached offer to get the boot file URL first. + // + Status = HttpBootDiscoverBootInfo (Private); + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (!Private->HttpCreated) { + // + // Create HTTP child. + // + Status = HttpBootCreateHttpIo (Private); + if (EFI_ERROR (Status)) { + return Status; + } + } + + 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 + ); + 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 + ); + if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + } + } + + if (*BufferSize < Private->BootFileSize) { + *BufferSize = Private->BootFileSize; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Load the boot file into Buffer + // + return HttpBootGetBootFile ( + Private, + FALSE, + BufferSize, + Buffer + ); +} + +/** + 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) { + for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) { + if (Private->OfferBuffer[Index].Dhcp4.UriParser) { + HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp4.UriParser); + } + } + } else { + ASSERT (FALSE); + } + + ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer)); + Private->OfferNum = 0; + ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); + ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); + + 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; + BOOLEAN MediaPresent; + EFI_STATUS Status; + + if (This == NULL || BufferSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Only support BootPolicy + // + if (!BootPolicy) { + return EFI_UNSUPPORTED; + } + + Private = HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE (This); + + // + // Check media status before HTTP boot start + // + MediaPresent = TRUE; + NetLibDetectMedia (Private->Controller, &MediaPresent); + if (!MediaPresent) { + return EFI_NO_MEDIA; + } + + // + // Initialize HTTP boot and load the boot file. + // + Status = HttpBootStart (Private); + if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) { + Status = HttpBootLoadFile (Private, BufferSize, Buffer); + } + + if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) { + HttpBootStop (Private); + } + + return Status; +} + +/// +/// Load File Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_LOAD_FILE_PROTOCOL gHttpBootDxeLoadFile = { + HttpBootDxeLoadFile +}; diff --git a/NetworkPkg/HttpBootDxe/HttpBootImpl.h b/NetworkPkg/HttpBootDxe/HttpBootImpl.h new file mode 100644 index 0000000000..90742dd159 --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootImpl.h @@ -0,0 +1,50 @@ +/** @file + The declaration of UEFI HTTP boot function. + +Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#ifndef __EFI_HTTP_BOOT_IMPL_H__ +#define __EFI_HTTP_BOOT_IMPL_H__ + +/** + Attempt to complete a DHCPv4 D.O.R.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 + ); + +#endif diff --git a/NetworkPkg/HttpBootDxe/HttpBootSupport.c b/NetworkPkg/HttpBootDxe/HttpBootSupport.c new file mode 100644 index 0000000000..e21b50d066 --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootSupport.c @@ -0,0 +1,590 @@ +/** @file + Support functions implementation for UEFI HTTP boot driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpBootDxe.h" + + +/** + 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; +} + + +/** + 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; + + while (Length > 0) { + Length--; + Remainder = Number % 10; + Number /= 10; + Buffer[Length] = (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 ("."); + } + } +} + +/** + Create a HTTP_IO_HEADER to hold the HTTP header items. + + @param[in] MaxHeaderCount The maximun number of HTTP header in this holder. + + @return A pointer of the HTTP header holder or NULL if failed. + +**/ +HTTP_IO_HEADER * +HttpBootCreateHeader ( + UINTN MaxHeaderCount +) +{ + HTTP_IO_HEADER *HttpIoHeader; + + if (MaxHeaderCount == 0) { + return NULL; + } + + HttpIoHeader = AllocateZeroPool (sizeof (HTTP_IO_HEADER) + MaxHeaderCount * sizeof (EFI_HTTP_HEADER)); + if (HttpIoHeader == NULL) { + return NULL; + } + + HttpIoHeader->MaxHeaderCount = MaxHeaderCount; + HttpIoHeader->Headers = (EFI_HTTP_HEADER *) (HttpIoHeader + 1); + + return HttpIoHeader; +} + +/** + Destroy the HTTP_IO_HEADER and release the resouces. + + @param[in] HttpIoHeader Point to the HTTP header holder to be destroyed. + +**/ +VOID +HttpBootFreeHeader ( + IN HTTP_IO_HEADER *HttpIoHeader + ) +{ + UINTN Index; + + if (HttpIoHeader != NULL) { + if (HttpIoHeader->HeaderCount != 0) { + for (Index = 0; Index < HttpIoHeader->HeaderCount; Index++) { + FreePool (HttpIoHeader->Headers[Index].FieldName); + FreePool (HttpIoHeader->Headers[Index].FieldValue); + } + } + FreePool (HttpIoHeader); + } +} + +/** + Find a specified header field according to the field name. + + @param[in] HeaderCount Number of HTTP header structures in Headers list. + @param[in] Headers Array containing list of HTTP headers. + @param[in] FieldName Null terminated string which describes a field name. + + @return Pointer to the found header or NULL. + +**/ +EFI_HTTP_HEADER * +HttpBootFindHeader ( + IN UINTN HeaderCount, + IN EFI_HTTP_HEADER *Headers, + IN CHAR8 *FieldName + ) +{ + UINTN Index; + + if (HeaderCount == 0 || Headers == NULL || FieldName == NULL) { + return NULL; + } + + for (Index = 0; Index < HeaderCount; Index++){ + // + // Field names are case-insensitive (RFC 2616). + // + if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) { + return &Headers[Index]; + } + } + return NULL; +} + +/** + Set or update a HTTP header with the field name and corresponding value. + + @param[in] HttpIoHeader Point to the HTTP header holder. + @param[in] FieldName Null terminated string which describes a field name. + @param[in] FieldValue Null terminated string which describes the corresponding field value. + + @retval EFI_SUCCESS The HTTP header has been set or updated. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resource to complete the operation. + @retval Other Unexpected error happened. + +**/ +EFI_STATUS +HttpBootSetHeader ( + IN HTTP_IO_HEADER *HttpIoHeader, + IN CHAR8 *FieldName, + IN CHAR8 *FieldValue + ) +{ + EFI_HTTP_HEADER *Header; + UINTN StrSize; + CHAR8 *NewFieldValue; + + if (HttpIoHeader == NULL || FieldName == NULL || FieldValue == NULL) { + return EFI_INVALID_PARAMETER; + } + + Header = HttpBootFindHeader (HttpIoHeader->HeaderCount, HttpIoHeader->Headers, FieldName); + if (Header == NULL) { + // + // Add a new header. + // + if (HttpIoHeader->HeaderCount >= HttpIoHeader->MaxHeaderCount) { + return EFI_OUT_OF_RESOURCES; + } + Header = &HttpIoHeader->Headers[HttpIoHeader->HeaderCount]; + + StrSize = AsciiStrSize (FieldName); + Header->FieldName = AllocatePool (StrSize); + if (Header->FieldName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (Header->FieldName, FieldName, StrSize); + Header->FieldName[StrSize -1] = '\0'; + + StrSize = AsciiStrSize (FieldValue); + Header->FieldValue = AllocatePool (StrSize); + if (Header->FieldValue == NULL) { + FreePool (Header->FieldName); + return EFI_OUT_OF_RESOURCES; + } + CopyMem (Header->FieldValue, FieldValue, StrSize); + Header->FieldValue[StrSize -1] = '\0'; + + HttpIoHeader->HeaderCount++; + } else { + // + // Update an existing one. + // + StrSize = AsciiStrSize (FieldValue); + NewFieldValue = AllocatePool (StrSize); + if (NewFieldValue == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (NewFieldValue, FieldValue, StrSize); + NewFieldValue[StrSize -1] = '\0'; + + if (Header->FieldValue != NULL) { + FreePool (Header->FieldValue); + } + Header->FieldValue = NewFieldValue; + } + + return EFI_SUCCESS; +} + +/** + Notify the callback function when an event is triggered. + + @param[in] Event The triggered event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +HttpIoCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + *((BOOLEAN *) Context) = TRUE; +} + +/** + Create a HTTP_IO to access the HTTP service. It will create and configure + a HTTP child handle. + + @param[in] Image The handle of the driver image. + @param[in] Controller The handle of the controller. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + @param[in] ConfigData The HTTP_IO configuration data. + @param[out] HttpIo The HTTP_IO. + + @retval EFI_SUCCESS The HTTP_IO is created and configured. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_UNSUPPORTED One or more of the control options are not + supported in the implementation. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval Others Failed to create the HTTP_IO or configure it. + +**/ +EFI_STATUS +HttpIoCreateIo ( + IN EFI_HANDLE Image, + IN EFI_HANDLE Controller, + IN UINT8 IpVersion, + IN HTTP_IO_CONFIG_DATA *ConfigData, + OUT HTTP_IO *HttpIo + ) +{ + EFI_STATUS Status; + EFI_HTTP_CONFIG_DATA HttpConfigData; + EFI_HTTPv4_ACCESS_POINT Http4AccessPoint; + EFI_HTTP_PROTOCOL *Http; + EFI_EVENT Event; + + if ((Image == NULL) || (Controller == NULL) || (ConfigData == NULL) || (HttpIo == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) { + return EFI_UNSUPPORTED; + } + + ZeroMem (HttpIo, sizeof (HTTP_IO)); + + // + // Create the HTTP child instance and get the HTTP protocol. + // + Status = NetLibCreateServiceChild ( + Controller, + Image, + &gEfiHttpServiceBindingProtocolGuid, + &HttpIo->Handle + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + HttpIo->Handle, + &gEfiHttpProtocolGuid, + (VOID **) &Http, + Image, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) || (Http == NULL)) { + goto ON_ERROR; + } + + // + // Init the configuration data and configure the HTTP child. + // + HttpIo->Image = Image; + HttpIo->Controller = Controller; + HttpIo->IpVersion = IpVersion; + HttpIo->Http = Http; + + ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA)); + HttpConfigData.HttpVersion = HttpVersion11; + HttpConfigData.TimeOutMillisec = ConfigData->Config4.RequestTimeOut; + if (HttpIo->IpVersion == IP_VERSION_4) { + HttpConfigData.LocalAddressIsIPv6 = FALSE; + + Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress; + Http4AccessPoint.LocalPort = ConfigData->Config4.LocalPort; + IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp); + IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask); + HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint; + } else { + ASSERT (FALSE); + } + + Status = Http->Configure (Http, &HttpConfigData); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create events for variuos asynchronous operations. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpIoCommonNotify, + &HttpIo->IsTxDone, + &Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + HttpIo->ReqToken.Event = Event; + HttpIo->ReqToken.Message = &HttpIo->ReqMessage; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpIoCommonNotify, + &HttpIo->IsRxDone, + &Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + HttpIo->RspToken.Event = Event; + HttpIo->RspToken.Message = &HttpIo->RspMessage; + + return EFI_SUCCESS; + +ON_ERROR: + HttpIoDestroyIo (HttpIo); + + return Status; +} + +/** + Destroy the HTTP_IO and release the resouces. + + @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed. + +**/ +VOID +HttpIoDestroyIo ( + IN HTTP_IO *HttpIo + ) +{ + EFI_HTTP_PROTOCOL *Http; + EFI_EVENT Event; + + if (HttpIo == NULL) { + return; + } + + Event = HttpIo->ReqToken.Event; + if (Event != NULL) { + gBS->CloseEvent (Event); + } + + Event = HttpIo->RspToken.Event; + if (Event != NULL) { + gBS->CloseEvent (Event); + } + + Http = HttpIo->Http; + if (Http != NULL) { + Http->Configure (Http, NULL); + gBS->CloseProtocol ( + HttpIo->Handle, + &gEfiHttpProtocolGuid, + HttpIo->Image, + HttpIo->Controller + ); + } + + NetLibDestroyServiceChild ( + HttpIo->Controller, + HttpIo->Image, + &gEfiHttpServiceBindingProtocolGuid, + HttpIo->Handle + ); +} + +/** + Synchronously send a HTTP REQUEST message to the server. + + @param[in] HttpIo The HttpIo wrapping the HTTP service. + @param[in] Request A pointer to storage such data as URL and HTTP method. + @param[in] HeaderCount Number of HTTP header structures in Headers list. + @param[in] Headers Array containing list of HTTP headers. + @param[in] BodyLength Length in bytes of the HTTP body. + @param[in] Body Body associated with the HTTP request. + + @retval EFI_SUCCESS The HTTP request is trasmitted. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpIoSendRequest ( + IN HTTP_IO *HttpIo, + IN EFI_HTTP_REQUEST_DATA *Request, + IN UINTN HeaderCount, + IN EFI_HTTP_HEADER *Headers, + IN UINTN BodyLength, + IN VOID *Body + ) +{ + EFI_STATUS Status; + EFI_HTTP_PROTOCOL *Http; + + if (HttpIo == NULL || HttpIo->Http == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpIo->ReqToken.Status = EFI_NOT_READY; + HttpIo->ReqToken.Message->Data.Request = Request; + HttpIo->ReqToken.Message->HeaderCount = HeaderCount; + HttpIo->ReqToken.Message->Headers = Headers; + HttpIo->ReqToken.Message->BodyLength = BodyLength; + HttpIo->ReqToken.Message->Body = Body; + + // + // Queue the request token to HTTP instances. + // + Http = HttpIo->Http; + HttpIo->IsTxDone = FALSE; + Status = Http->Request ( + Http, + &HttpIo->ReqToken + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Poll the network until transmit finish. + // + while (!HttpIo->IsTxDone) { + Http->Poll (Http); + } + + return HttpIo->ReqToken.Status; +} + +/** + Synchronously receive a HTTP RESPONSE message from the server. + + @param[in] HttpIo The HttpIo wrapping the HTTP service. + @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header). + FALSE to continue receive the previous response message. + @param[out] ResponseData Point to a wrapper of the received response data. + + @retval EFI_SUCCESS The HTTP resopnse is received. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpIoRecvResponse ( + IN HTTP_IO *HttpIo, + IN BOOLEAN RecvMsgHeader, + OUT HTTP_IO_RESOPNSE_DATA *ResponseData + ) +{ + EFI_STATUS Status; + EFI_HTTP_PROTOCOL *Http; + + if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Queue the response token to HTTP instances. + // + HttpIo->RspToken.Status = EFI_NOT_READY; + if (RecvMsgHeader) { + HttpIo->RspToken.Message->Data.Response = &ResponseData->Response; + } else { + HttpIo->RspToken.Message->Data.Response = NULL; + } + HttpIo->RspToken.Message->HeaderCount = 0; + HttpIo->RspToken.Message->Headers = NULL; + HttpIo->RspToken.Message->BodyLength = ResponseData->BodyLength; + HttpIo->RspToken.Message->Body = ResponseData->Body; + + Http = HttpIo->Http; + HttpIo->IsRxDone = FALSE; + Status = Http->Response ( + Http, + &HttpIo->RspToken + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Poll the network until transmit finish. + // + while (!HttpIo->IsRxDone) { + Http->Poll (Http); + } + + // + // Store the received data into the wrapper. + // + Status = HttpIo->ReqToken.Status; + if (!EFI_ERROR (Status)) { + ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount; + ResponseData->Headers = HttpIo->RspToken.Message->Headers; + ResponseData->BodyLength = HttpIo->RspToken.Message->BodyLength; + } + + return Status; +} diff --git a/NetworkPkg/HttpBootDxe/HttpBootSupport.h b/NetworkPkg/HttpBootDxe/HttpBootSupport.h new file mode 100644 index 0000000000..d1fa287920 --- /dev/null +++ b/NetworkPkg/HttpBootDxe/HttpBootSupport.h @@ -0,0 +1,250 @@ +/** @file + Support functions declaration for UEFI HTTP boot driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_HTTP_BOOT_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 + ); + +/** + 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 + ); + +// +// A wrapper structure to hold the HTTP headers. +// +typedef struct { + UINTN MaxHeaderCount; + UINTN HeaderCount; + EFI_HTTP_HEADER *Headers; +} HTTP_IO_HEADER; + +/** + Create a HTTP_IO_HEADER to hold the HTTP header items. + + @param[in] MaxHeaderCount The maximun number of HTTP header in this holder. + + @return A pointer of the HTTP header holder or NULL if failed. + +**/ +HTTP_IO_HEADER * +HttpBootCreateHeader ( + IN UINTN MaxHeaderCount + ); + +/** + Destroy the HTTP_IO_HEADER and release the resouces. + + @param[in] HttpIoHeader Point to the HTTP header holder to be destroyed. + +**/ +VOID +HttpBootFreeHeader ( + IN HTTP_IO_HEADER *HttpIoHeader + ); + +/** + Set or update a HTTP header with the field name and corresponding value. + + @param[in] HttpIoHeader Point to the HTTP header holder. + @param[in] FieldName Null terminated string which describes a field name. + @param[in] FieldValue Null terminated string which describes the corresponding field value. + + @retval EFI_SUCCESS The HTTP header has been set or updated. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resource to complete the operation. + @retval Other Unexpected error happened. + +**/ +EFI_STATUS +HttpBootSetHeader ( + IN HTTP_IO_HEADER *HttpIoHeader, + IN CHAR8 *FieldName, + IN CHAR8 *FieldValue + ); + +// +// HTTP_IO configuration data for IPv4 +// +typedef struct { + EFI_HTTP_VERSION HttpVersion; + UINT32 RequestTimeOut; // In milliseconds. + UINT32 ResponseTimeOut; // In milliseconds. + BOOLEAN UseDefaultAddress; + EFI_IPv4_ADDRESS LocalIp; + EFI_IPv4_ADDRESS SubnetMask; + UINT16 LocalPort; +} HTTP4_IO_CONFIG_DATA; + +// +// HTTP_IO configuration +// +typedef union { + HTTP4_IO_CONFIG_DATA Config4; +} HTTP_IO_CONFIG_DATA; + +// +// HTTO_IO wrapper of the EFI HTTP service. +// +typedef struct { + UINT8 IpVersion; + EFI_HANDLE Image; + EFI_HANDLE Controller; + EFI_HANDLE Handle; + + EFI_HTTP_PROTOCOL *Http; + + EFI_HTTP_TOKEN ReqToken; + EFI_HTTP_MESSAGE ReqMessage; + EFI_HTTP_TOKEN RspToken; + EFI_HTTP_MESSAGE RspMessage; + + BOOLEAN IsTxDone; + BOOLEAN IsRxDone; +} HTTP_IO; + +// +// A wrapper structure to hold the received HTTP response data. +// +typedef struct { + EFI_HTTP_RESPONSE_DATA Response; + UINTN HeaderCount; + EFI_HTTP_HEADER *Headers; + UINTN BodyLength; + CHAR8 *Body; +} HTTP_IO_RESOPNSE_DATA; + +/** + Create a HTTP_IO to access the HTTP service. It will create and configure + a HTTP child handle. + + @param[in] Image The handle of the driver image. + @param[in] Controller The handle of the controller. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + @param[in] ConfigData The HTTP_IO configuration data. + @param[out] HttpIo The HTTP_IO. + + @retval EFI_SUCCESS The HTTP_IO is created and configured. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_UNSUPPORTED One or more of the control options are not + supported in the implementation. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval Others Failed to create the HTTP_IO or configure it. + +**/ +EFI_STATUS +HttpIoCreateIo ( + IN EFI_HANDLE Image, + IN EFI_HANDLE Controller, + IN UINT8 IpVersion, + IN HTTP_IO_CONFIG_DATA *ConfigData, + OUT HTTP_IO *HttpIo + ); + +/** + Destroy the HTTP_IO and release the resouces. + + @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed. + +**/ +VOID +HttpIoDestroyIo ( + IN HTTP_IO *HttpIo + ); + +/** + Synchronously send a HTTP REQUEST message to the server. + + @param[in] HttpIo The HttpIo wrapping the HTTP service. + @param[in] Request A pointer to storage such data as URL and HTTP method. + @param[in] HeaderCount Number of HTTP header structures in Headers list. + @param[in] Headers Array containing list of HTTP headers. + @param[in] BodyLength Length in bytes of the HTTP body. + @param[in] Body Body associated with the HTTP request. + + @retval EFI_SUCCESS The HTTP request is trasmitted. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpIoSendRequest ( + IN HTTP_IO *HttpIo, + IN EFI_HTTP_REQUEST_DATA *Request, OPTIONAL + IN UINTN HeaderCount, + IN EFI_HTTP_HEADER *Headers, OPTIONAL + IN UINTN BodyLength, + IN VOID *Body OPTIONAL + ); + +/** + Synchronously receive a HTTP RESPONSE message from the server. + + @param[in] HttpIo The HttpIo wrapping the HTTP service. + @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header). + FALSE to continue receive the previous response message. + @param[out] ResponseData Point to a wrapper of the received response data. + + @retval EFI_SUCCESS The HTTP resopnse is received. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpIoRecvResponse ( + IN HTTP_IO *HttpIo, + IN BOOLEAN RecvMsgHeader, + OUT HTTP_IO_RESOPNSE_DATA *ResponseData + ); + +#endif |