-/** @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;
-}
+/** @file\r
+ Implementation of the boot file download function.\r
+\r
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials are licensed and made available under \r
+the terms and conditions of the BSD License that accompanies this distribution. \r
+The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php. \r
+ \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "HttpBootDxe.h"\r
+\r
+/**\r
+ Update the IP and URL device path node to include the boot resource information.\r
+\r
+ @param[in] Private The pointer to the driver's private data.\r
+\r
+ @retval EFI_SUCCESS Device patch successfully updated.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.\r
+ @retval Others Unexpected error happened.\r
+ \r
+**/\r
+EFI_STATUS\r
+HttpBootUpdateDevicePath (\r
+ IN HTTP_BOOT_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_DEV_PATH *Node;\r
+ EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;\r
+ UINTN Length;\r
+ EFI_STATUS Status;\r
+\r
+ TmpDevicePath = NULL;\r
+ \r
+ //\r
+ // Update the IP node with DHCP assigned information.\r
+ //\r
+ if (!Private->UsingIpv6) {\r
+ Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));\r
+ if (Node == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH;\r
+ Node->Ipv4.Header.SubType = MSG_IPv4_DP;\r
+ SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));\r
+ CopyMem (&Node->Ipv4.LocalIpAddress, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));\r
+ Node->Ipv4.RemotePort = Private->Port;\r
+ Node->Ipv4.Protocol = EFI_IP_PROTO_TCP;\r
+ Node->Ipv4.StaticIpAddress = FALSE;\r
+ CopyMem (&Node->Ipv4.GatewayIpAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Node->Ipv4.SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+ } else {\r
+ Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH));\r
+ if (Node == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ Node->Ipv6.Header.Type = MESSAGING_DEVICE_PATH;\r
+ Node->Ipv6.Header.SubType = MSG_IPv6_DP;\r
+ SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH));\r
+ Node->Ipv6.PrefixLength = IP6_PREFIX_LENGTH;\r
+ Node->Ipv6.RemotePort = Private->Port;\r
+ Node->Ipv6.Protocol = EFI_IP_PROTO_TCP; \r
+ Node->Ipv6.IpAddressOrigin = 0;\r
+ CopyMem (&Node->Ipv6.LocalIpAddress, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
+ CopyMem (&Node->Ipv6.RemoteIpAddress, &Private->ServerIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
+ CopyMem (&Node->Ipv6.GatewayIpAddress, &Private->GatewayIp.v6, sizeof (EFI_IPv6_ADDRESS));\r
+ }\r
+ \r
+ TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);\r
+ FreePool (Node);\r
+ if (TmpDevicePath == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Update the URI node with the boot file URI.\r
+ //\r
+ Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (Private->BootFileUri);\r
+ Node = AllocatePool (Length);\r
+ if (Node == NULL) {\r
+ FreePool (TmpDevicePath);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ Node->DevPath.Type = MESSAGING_DEVICE_PATH;\r
+ Node->DevPath.SubType = MSG_URI_DP;\r
+ SetDevicePathNodeLength (Node, Length);\r
+ CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Private->BootFileUri, AsciiStrSize (Private->BootFileUri));\r
+ \r
+ NewDevicePath = AppendDevicePathNode (TmpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);\r
+ FreePool (Node);\r
+ FreePool (TmpDevicePath);\r
+ if (NewDevicePath == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ if (!Private->UsingIpv6) {\r
+ //\r
+ // Reinstall the device path protocol of the child handle.\r
+ //\r
+ Status = gBS->ReinstallProtocolInterface (\r
+ Private->Ip4Nic->Controller,\r
+ &gEfiDevicePathProtocolGuid,\r
+ Private->Ip4Nic->DevicePath,\r
+ NewDevicePath\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ \r
+ FreePool (Private->Ip4Nic->DevicePath);\r
+ Private->Ip4Nic->DevicePath = NewDevicePath;\r
+ } else {\r
+ //\r
+ // Reinstall the device path protocol of the child handle.\r
+ //\r
+ Status = gBS->ReinstallProtocolInterface (\r
+ Private->Ip6Nic->Controller,\r
+ &gEfiDevicePathProtocolGuid,\r
+ Private->Ip6Nic->DevicePath,\r
+ NewDevicePath\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ FreePool (Private->Ip6Nic->DevicePath);\r
+ Private->Ip6Nic->DevicePath = NewDevicePath;\r
+ }\r
+ \r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Parse the boot file URI information from the selected Dhcp4 offer packet.\r
+\r
+ @param[in] Private The pointer to the driver's private data.\r
+\r
+ @retval EFI_SUCCESS Successfully parsed out all the boot information.\r
+ @retval Others Failed to parse out the boot information.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootDhcp4ExtractUriInfo (\r
+ IN HTTP_BOOT_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ HTTP_BOOT_DHCP4_PACKET_CACHE *SelectOffer;\r
+ HTTP_BOOT_DHCP4_PACKET_CACHE *HttpOffer;\r
+ UINT32 SelectIndex;\r
+ UINT32 ProxyIndex;\r
+ EFI_DHCP4_PACKET_OPTION *Option;\r
+ EFI_STATUS Status;\r
+\r
+ ASSERT (Private != NULL);\r
+ ASSERT (Private->SelectIndex != 0);\r
+ SelectIndex = Private->SelectIndex - 1;\r
+ ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // SelectOffer contains the IP address configuration and name server configuration.\r
+ // HttpOffer contains the boot file URL.\r
+ //\r
+ SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp4;\r
+ if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {\r
+ HttpOffer = SelectOffer;\r
+ } else {\r
+ ASSERT (Private->SelectProxyType != HttpOfferTypeMax);\r
+ ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];\r
+ HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4;\r
+ }\r
+\r
+ //\r
+ // Configure the default DNS server if server assigned.\r
+ //\r
+ if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || (SelectOffer->OfferType == HttpOfferTypeDhcpDns)) {\r
+ Option = SelectOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER];\r
+ ASSERT (Option != NULL);\r
+ Status = HttpBootRegisterIp4Dns (\r
+ Private,\r
+ Option->Length,\r
+ Option->Data\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Extract the port from URL, and use default HTTP port 80 if not provided.\r
+ //\r
+ Status = HttpUrlGetPort (\r
+ (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data,\r
+ HttpOffer->UriParser,\r
+ &Private->Port\r
+ );\r
+ if (EFI_ERROR (Status) || Private->Port == 0) {\r
+ Private->Port = 80;\r
+ }\r
+ \r
+ //\r
+ // Record the URI of boot file from the selected HTTP offer.\r
+ //\r
+ Private->BootFileUriParser = HttpOffer->UriParser;\r
+ Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data;\r
+\r
+ \r
+ //\r
+ // All boot informations are valid here.\r
+ //\r
+ AsciiPrint ("\n URI: %a", Private->BootFileUri);\r
+\r
+ //\r
+ // Update the device path to include the IP and boot URI information.\r
+ //\r
+ Status = HttpBootUpdateDevicePath (Private);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Parse the boot file URI information from the selected Dhcp6 offer packet.\r
+\r
+ @param[in] Private The pointer to the driver's private data.\r
+\r
+ @retval EFI_SUCCESS Successfully parsed out all the boot information.\r
+ @retval Others Failed to parse out the boot information.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootDhcp6ExtractUriInfo (\r
+ IN HTTP_BOOT_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ HTTP_BOOT_DHCP6_PACKET_CACHE *SelectOffer;\r
+ HTTP_BOOT_DHCP6_PACKET_CACHE *HttpOffer;\r
+ UINT32 SelectIndex;\r
+ UINT32 ProxyIndex;\r
+ EFI_DHCP6_PACKET_OPTION *Option;\r
+ EFI_IPv6_ADDRESS IpAddr;\r
+ CHAR8 *HostName;\r
+ CHAR16 *HostNameStr;\r
+ EFI_STATUS Status;\r
+\r
+ ASSERT (Private != NULL);\r
+ ASSERT (Private->SelectIndex != 0);\r
+ SelectIndex = Private->SelectIndex - 1;\r
+ ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);\r
+\r
+ Status = EFI_SUCCESS;\r
+ HostName = NULL;\r
+ //\r
+ // SelectOffer contains the IP address configuration and name server configuration.\r
+ // HttpOffer contains the boot file URL.\r
+ //\r
+ SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp6;\r
+ if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {\r
+ HttpOffer = SelectOffer;\r
+ } else {\r
+ ASSERT (Private->SelectProxyType != HttpOfferTypeMax);\r
+ ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];\r
+ HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp6;\r
+ }\r
+\r
+ //\r
+ // Set the Local station address to IP layer.\r
+ //\r
+ Status = HttpBootSetIp6Address (Private);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ \r
+ //\r
+ // Configure the default DNS server if server assigned.\r
+ //\r
+ if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || (SelectOffer->OfferType == HttpOfferTypeDhcpDns)) {\r
+ Option = SelectOffer->OptList[HTTP_BOOT_DHCP6_IDX_DNS_SERVER];\r
+ ASSERT (Option != NULL);\r
+ Status = HttpBootSetIp6Dns (\r
+ Private,\r
+ HTONS (Option->OpLen),\r
+ Option->Data\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+ \r
+ //\r
+ // Extract the HTTP server Ip frome URL. This is used to Check route table \r
+ // whether can send message to HTTP Server Ip through the GateWay.\r
+ //\r
+ Status = HttpUrlGetIp6 (\r
+ (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,\r
+ HttpOffer->UriParser,\r
+ &IpAddr\r
+ );\r
+ \r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // The Http server address is expressed by Name Ip, so perform DNS resolution\r
+ //\r
+ Status = HttpUrlGetHostName (\r
+ (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,\r
+ HttpOffer->UriParser,\r
+ &HostName\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ \r
+ HostNameStr = AllocateZeroPool ((AsciiStrLen (HostName) + 1) * sizeof (CHAR16));\r
+ if (HostNameStr == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+ \r
+ AsciiStrToUnicodeStr (HostName, HostNameStr);\r
+ Status = HttpBootDns (Private, HostNameStr, &IpAddr);\r
+ FreePool (HostNameStr);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ } \r
+ } \r
+ \r
+ CopyMem (&Private->ServerIp.v6, &IpAddr, sizeof (EFI_IPv6_ADDRESS)); \r
+ \r
+ //\r
+ // register the IPv6 gateway address to the network device.\r
+ //\r
+ Status = HttpBootSetIp6Gateway (Private);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ \r
+ //\r
+ // Extract the port from URL, and use default HTTP port 80 if not provided.\r
+ //\r
+ Status = HttpUrlGetPort (\r
+ (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,\r
+ HttpOffer->UriParser,\r
+ &Private->Port\r
+ );\r
+ if (EFI_ERROR (Status) || Private->Port == 0) {\r
+ Private->Port = 80;\r
+ }\r
+ \r
+ //\r
+ // Record the URI of boot file from the selected HTTP offer.\r
+ //\r
+ Private->BootFileUriParser = HttpOffer->UriParser;\r
+ Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data;\r
+\r
+ \r
+ //\r
+ // All boot informations are valid here.\r
+ //\r
+ AsciiPrint ("\n URI: %a", Private->BootFileUri);\r
+ //\r
+ // Update the device path to include the IP and boot URI information.\r
+ //\r
+ Status = HttpBootUpdateDevicePath (Private);\r
+\r
+Error:\r
+ \r
+ if (HostName != NULL) {\r
+ FreePool (HostName);\r
+ }\r
+ \r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Discover all the boot information for boot file.\r
+\r
+ @param[in, out] Private The pointer to the driver's private data.\r
+\r
+ @retval EFI_SUCCESS Successfully obtained all the boot information .\r
+ @retval Others Failed to retrieve the boot information.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootDiscoverBootInfo (\r
+ IN OUT HTTP_BOOT_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ \r
+ //\r
+ // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and\r
+ // other Http boot information.\r
+ //\r
+ Status = HttpBootDhcp (Private);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (!Private->UsingIpv6) {\r
+ Status = HttpBootDhcp4ExtractUriInfo (Private);\r
+ } else {\r
+ Status = HttpBootDhcp6ExtractUriInfo (Private);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Create a HttpIo instance for the file download.\r
+\r
+ @param[in] Private The pointer to the driver's private data.\r
+\r
+ @retval EFI_SUCCESS Successfully created.\r
+ @retval Others Failed to create HttpIo.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootCreateHttpIo (\r
+ IN HTTP_BOOT_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ HTTP_IO_CONFIG_DATA ConfigData;\r
+ EFI_STATUS Status;\r
+\r
+ ASSERT (Private != NULL);\r
+\r
+ ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA));\r
+ if (!Private->UsingIpv6) {\r
+ ConfigData.Config4.HttpVersion = HttpVersion11;\r
+ ConfigData.Config4.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;\r
+ IP4_COPY_ADDRESS (&ConfigData.Config4.LocalIp, &Private->StationIp.v4);\r
+ IP4_COPY_ADDRESS (&ConfigData.Config4.SubnetMask, &Private->SubnetMask.v4);\r
+ } else {\r
+ ConfigData.Config6.HttpVersion = HttpVersion11;\r
+ ConfigData.Config6.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;\r
+ IP6_COPY_ADDRESS (&ConfigData.Config6.LocalIp, &Private->StationIp.v6);\r
+ }\r
+\r
+ Status = HttpIoCreateIo (\r
+ Private->Image,\r
+ Private->Controller,\r
+ Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4,\r
+ &ConfigData,\r
+ &Private->HttpIo\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Private->HttpCreated = TRUE;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Get the file content from cached data.\r
+\r
+ @param[in] Private The pointer to the driver's private data.\r
+ @param[in] Uri Uri of the file to be retrieved from cache.\r
+ @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return\r
+ code of EFI_SUCCESS, the amount of data transferred to\r
+ Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,\r
+ the size of Buffer required to retrieve the requested file.\r
+ @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,\r
+ then the size of the requested file is returned in\r
+ BufferSize.\r
+\r
+ @retval EFI_SUCCESS Successfully created.\r
+ @retval Others Failed to create HttpIo.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootGetFileFromCache (\r
+ IN HTTP_BOOT_PRIVATE_DATA *Private,\r
+ IN CHAR16 *Uri,\r
+ IN OUT UINTN *BufferSize,\r
+ OUT UINT8 *Buffer\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *Entry2;\r
+ HTTP_BOOT_CACHE_CONTENT *Cache;\r
+ HTTP_BOOT_ENTITY_DATA *EntityData;\r
+ UINTN CopyedSize;\r
+ \r
+ if (Uri == NULL || BufferSize == 0 || Buffer == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ NET_LIST_FOR_EACH (Entry, &Private->CacheList) {\r
+ Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);\r
+ //\r
+ // Compare the URI to see whether we already have a cache for this file.\r
+ //\r
+ if ((Cache->RequestData != NULL) &&\r
+ (Cache->RequestData->Url != NULL) &&\r
+ (StrCmp (Uri, Cache->RequestData->Url) == 0)) \r
+ {\r
+ //\r
+ // Hit cache, check buffer size.\r
+ //\r
+ if (*BufferSize < Cache->EntityLength) {\r
+ *BufferSize = Cache->EntityLength;\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ //\r
+ // Fill data to buffer.\r
+ //\r
+ CopyedSize = 0;\r
+ NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) {\r
+ EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link);\r
+ if (*BufferSize > CopyedSize) {\r
+ CopyMem (\r
+ Buffer + CopyedSize,\r
+ EntityData->DataStart,\r
+ MIN (EntityData->DataLength, *BufferSize - CopyedSize)\r
+ );\r
+ CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize);\r
+ }\r
+ }\r
+ *BufferSize = CopyedSize;\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ Release all the resource of a cache item.\r
+\r
+ @param[in] Cache The pointer to the cache item.\r
+\r
+**/\r
+VOID\r
+HttpBootFreeCache (\r
+ IN HTTP_BOOT_CACHE_CONTENT *Cache\r
+ )\r
+{\r
+ UINTN Index;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *NextEntry;\r
+ HTTP_BOOT_ENTITY_DATA *EntityData;\r
+\r
+ if (Cache != NULL) {\r
+ //\r
+ // Free the request data\r
+ //\r
+ if (Cache->RequestData != NULL) {\r
+ if (Cache->RequestData->Url != NULL) {\r
+ FreePool (Cache->RequestData->Url);\r
+ }\r
+ FreePool (Cache->RequestData);\r
+ }\r
+\r
+ //\r
+ // Free the response header\r
+ //\r
+ if (Cache->ResponseData != NULL) {\r
+ if (Cache->ResponseData->Headers != NULL) {\r
+ for (Index = 0; Index < Cache->ResponseData->HeaderCount; Index++) {\r
+ FreePool (Cache->ResponseData->Headers[Index].FieldName);\r
+ FreePool (Cache->ResponseData->Headers[Index].FieldValue);\r
+ }\r
+ FreePool (Cache->ResponseData->Headers);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Free the response body\r
+ //\r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Cache->EntityDataList) {\r
+ EntityData = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_ENTITY_DATA, Link);\r
+ if (EntityData->Block != NULL) {\r
+ FreePool (EntityData->Block);\r
+ }\r
+ RemoveEntryList (&EntityData->Link);\r
+ FreePool (EntityData);\r
+ }\r
+\r
+ FreePool (Cache);\r
+ }\r
+}\r
+\r
+/**\r
+ Clean up all cached data.\r
+\r
+ @param[in] Private The pointer to the driver's private data.\r
+\r
+**/\r
+VOID\r
+HttpBootFreeCacheList (\r
+ IN HTTP_BOOT_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *NextEntry;\r
+ HTTP_BOOT_CACHE_CONTENT *Cache;\r
+ \r
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->CacheList) {\r
+ Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);\r
+ RemoveEntryList (&Cache->Link);\r
+ HttpBootFreeCache (Cache);\r
+ }\r
+}\r
+\r
+/**\r
+ A callback function to intercept events during message parser.\r
+\r
+ This function will be invoked during HttpParseMessageBody() with various events type. An error\r
+ return status of the callback function will cause the HttpParseMessageBody() aborted.\r
+\r
+ @param[in] EventType Event type of this callback call.\r
+ @param[in] Data A pointer to data buffer.\r
+ @param[in] Length Length in bytes of the Data.\r
+ @param[in] Context Callback context set by HttpInitMsgParser().\r
+\r
+ @retval EFI_SUCCESS Continue to parser the message body.\r
+ @retval Others Abort the parse.\r
+ \r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HttpBootGetBootFileCallback (\r
+ IN HTTP_BODY_PARSE_EVENT EventType,\r
+ IN CHAR8 *Data,\r
+ IN UINTN Length,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ HTTP_BOOT_CALLBACK_DATA *CallbackData;\r
+ HTTP_BOOT_ENTITY_DATA *NewEntityData;\r
+\r
+ //\r
+ // We only care about the entity data.\r
+ //\r
+ if (EventType != BodyParseEventOnData) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context;\r
+ //\r
+ // Copy data if caller has provided a buffer.\r
+ //\r
+ if (CallbackData->BufferSize > CallbackData->CopyedSize) {\r
+ CopyMem (\r
+ CallbackData->Buffer + CallbackData->CopyedSize,\r
+ Data,\r
+ MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize)\r
+ );\r
+ CallbackData->CopyedSize += MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize);\r
+ }\r
+\r
+ //\r
+ // The caller doesn't provide a buffer, save the block into cache list.\r
+ //\r
+ if (CallbackData->Cache != NULL) {\r
+ NewEntityData = AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA));\r
+ if (NewEntityData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ if (CallbackData->NewBlock) {\r
+ NewEntityData->Block = CallbackData->Block;\r
+ CallbackData->Block = NULL;\r
+ }\r
+ NewEntityData->DataLength = Length;\r
+ NewEntityData->DataStart = (UINT8*) Data;\r
+ InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link);\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This function download the boot file by using UEFI HTTP protocol.\r
+ \r
+ @param[in] Private The pointer to the driver's private data.\r
+ @param[in] HeaderOnly Only request the response header, it could save a lot of time if\r
+ the caller only want to know the size of the requested file.\r
+ @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return\r
+ code of EFI_SUCCESS, the amount of data transferred to\r
+ Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,\r
+ the size of Buffer required to retrieve the requested file.\r
+ @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,\r
+ then the size of the requested file is returned in\r
+ BufferSize.\r
+\r
+ @retval EFI_SUCCESS The file was loaded.\r
+ @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources\r
+ @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.\r
+ BufferSize has been updated with the size needed to complete\r
+ the request.\r
+ @retval Others Unexpected error happened.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootGetBootFile (\r
+ IN HTTP_BOOT_PRIVATE_DATA *Private,\r
+ IN BOOLEAN HeaderOnly,\r
+ IN OUT UINTN *BufferSize,\r
+ OUT UINT8 *Buffer\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CHAR8 *HostName;\r
+ EFI_HTTP_REQUEST_DATA *RequestData;\r
+ HTTP_IO_RESOPNSE_DATA *ResponseData;\r
+ HTTP_IO_RESOPNSE_DATA ResponseBody;\r
+ HTTP_IO *HttpIo;\r
+ HTTP_IO_HEADER *HttpIoHeader;\r
+ VOID *Parser;\r
+ HTTP_BOOT_CALLBACK_DATA Context;\r
+ UINTN ContentLength;\r
+ HTTP_BOOT_CACHE_CONTENT *Cache;\r
+ UINT8 *Block;\r
+ CHAR16 *Url;\r
+ \r
+ ASSERT (Private != NULL);\r
+ ASSERT (Private->HttpCreated);\r
+\r
+ if (BufferSize == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (*BufferSize != 0 && Buffer == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // First, check whether we already cached the requested Uri.\r
+ //\r
+ Url = AllocatePool ((AsciiStrLen (Private->BootFileUri) + 1) * sizeof (CHAR16));\r
+ if (Url == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ AsciiStrToUnicodeStr (Private->BootFileUri, Url);\r
+ if (!HeaderOnly) {\r
+ Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer);\r
+ if (Status != EFI_NOT_FOUND) {\r
+ FreePool (Url);\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Not found in cache, try to download it through HTTP.\r
+ //\r
+\r
+ //\r
+ // 1. Create a temp cache item for the requested URI if caller doesn't provide buffer.\r
+ //\r
+ Cache = NULL;\r
+ if ((!HeaderOnly) && (*BufferSize == 0)) {\r
+ Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT));\r
+ if (Cache == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ERROR_1;\r
+ }\r
+ InitializeListHead (&Cache->EntityDataList);\r
+ }\r
+\r
+ //\r
+ // 2. Send HTTP request message.\r
+ //\r
+\r
+ //\r
+ // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:\r
+ // Host\r
+ // Accept\r
+ // User-Agent\r
+ //\r
+ HttpIoHeader = HttpBootCreateHeader (3);\r
+ if (HttpIoHeader == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ERROR_2;\r
+ }\r
+\r
+ //\r
+ // Add HTTP header field 1: Host\r
+ //\r
+ HostName = NULL;\r
+ Status = HttpUrlGetHostName (\r
+ Private->BootFileUri,\r
+ Private->BootFileUriParser,\r
+ &HostName\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ERROR_3;\r
+ }\r
+ Status = HttpBootSetHeader (\r
+ HttpIoHeader,\r
+ HTTP_FIELD_NAME_HOST,\r
+ HostName\r
+ );\r
+ FreePool (HostName);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ERROR_3;\r
+ }\r
+\r
+ //\r
+ // Add HTTP header field 2: Accept\r
+ //\r
+ Status = HttpBootSetHeader (\r
+ HttpIoHeader,\r
+ HTTP_FIELD_NAME_ACCEPT,\r
+ "*/*"\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ERROR_3;\r
+ }\r
+\r
+ //\r
+ // Add HTTP header field 3: User-Agent\r
+ //\r
+ Status = HttpBootSetHeader (\r
+ HttpIoHeader,\r
+ HTTP_FIELD_NAME_USER_AGENT,\r
+ HTTP_USER_AGENT_EFI_HTTP_BOOT\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ERROR_3;\r
+ }\r
+\r
+ //\r
+ // 2.2 Build the rest of HTTP request info.\r
+ //\r
+ RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA));\r
+ if (RequestData == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ERROR_3;\r
+ }\r
+ RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet;\r
+ RequestData->Url = Url;\r
+ if (RequestData->Url == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ERROR_4;\r
+ }\r
+ AsciiStrToUnicodeStr (Private->BootFileUri, RequestData->Url);\r
+\r
+ //\r
+ // 2.3 Record the request info in a temp cache item.\r
+ //\r
+ if (Cache != NULL) {\r
+ Cache->RequestData = RequestData;\r
+ }\r
+\r
+ //\r
+ // 2.4 Send out the request to HTTP server.\r
+ //\r
+ HttpIo = &Private->HttpIo;\r
+ Status = HttpIoSendRequest (\r
+ HttpIo,\r
+ RequestData,\r
+ HttpIoHeader->HeaderCount,\r
+ HttpIoHeader->Headers,\r
+ 0,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ERROR_4;\r
+ }\r
+\r
+ //\r
+ // 3. Receive HTTP response message.\r
+ //\r
+\r
+ //\r
+ // 3.1 First step, use zero BodyLength to only receive the response headers.\r
+ //\r
+ ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESOPNSE_DATA));\r
+ if (ResponseData == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ERROR_4;\r
+ }\r
+ Status = HttpIoRecvResponse (\r
+ &Private->HttpIo,\r
+ TRUE,\r
+ ResponseData\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ERROR_5;\r
+ }\r
+\r
+ //\r
+ // 3.2 Cache the response header.\r
+ //\r
+ if (Cache != NULL) {\r
+ Cache->ResponseData = ResponseData;\r
+ }\r
+ \r
+ //\r
+ // 3.3 Init a message-body parser from the header information.\r
+ //\r
+ Parser = NULL;\r
+ Context.NewBlock = FALSE;\r
+ Context.Block = NULL;\r
+ Context.CopyedSize = 0;\r
+ Context.Buffer = Buffer;\r
+ Context.BufferSize = *BufferSize;\r
+ Context.Cache = Cache;\r
+ Status = HttpInitMsgParser (\r
+ HeaderOnly? HttpMethodHead : HttpMethodGet,\r
+ ResponseData->Response.StatusCode,\r
+ ResponseData->HeaderCount,\r
+ ResponseData->Headers,\r
+ HttpBootGetBootFileCallback,\r
+ (VOID*) &Context,\r
+ &Parser\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ERROR_6;\r
+ }\r
+\r
+ //\r
+ // 3.4 Continue to receive and parse message-body if needed.\r
+ //\r
+ Block = NULL;\r
+ if (!HeaderOnly) {\r
+ ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESOPNSE_DATA));\r
+ while (!HttpIsMessageComplete (Parser)) {\r
+ //\r
+ // Allocate a block to hold the message-body, if caller doesn't provide\r
+ // a buffer, the block will be cached and we will allocate a new one here.\r
+ //\r
+ if (Block == NULL || Context.BufferSize == 0) {\r
+ Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE);\r
+ if (Block == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ERROR_6;\r
+ }\r
+ Context.NewBlock = TRUE;\r
+ Context.Block = Block;\r
+ } else {\r
+ Context.NewBlock = FALSE;\r
+ }\r
+\r
+ ResponseBody.Body = (CHAR8*) Block;\r
+ ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE;\r
+ Status = HttpIoRecvResponse (\r
+ &Private->HttpIo,\r
+ FALSE,\r
+ &ResponseBody\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ERROR_6;\r
+ }\r
+\r
+ //\r
+ // Parse the new received block of the message-body, the block will be saved in cache.\r
+ //\r
+ Status = HttpParseMessageBody (\r
+ Parser,\r
+ ResponseBody.BodyLength,\r
+ ResponseBody.Body\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ERROR_6;\r
+ }\r
+ }\r
+ }\r
+ \r
+ //\r
+ // 3.5 Message-body receive & parse is completed, get the file size.\r
+ //\r
+ Status = HttpGetEntityLength (Parser, &ContentLength);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ERROR_6;\r
+ }\r
+\r
+ if (*BufferSize < ContentLength) {\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ }\r
+ *BufferSize = ContentLength;\r
+\r
+ //\r
+ // 4. Save the cache item to driver's cache list and return.\r
+ //\r
+ if (Cache != NULL) {\r
+ Cache->EntityLength = ContentLength;\r
+ InsertTailList (&Private->CacheList, &Cache->Link);\r
+ }\r
+\r
+ if (Parser != NULL) {\r
+ HttpFreeMsgParser (Parser);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+ \r
+ERROR_6:\r
+ if (Parser != NULL) {\r
+ HttpFreeMsgParser (Parser);\r
+ }\r
+ if (Context.Block != NULL) {\r
+ FreePool (Context.Block);\r
+ }\r
+ HttpBootFreeCache (Cache);\r
+ \r
+ERROR_5:\r
+ if (ResponseData != NULL) {\r
+ FreePool (ResponseData);\r
+ }\r
+ERROR_4:\r
+ if (RequestData != NULL) {\r
+ FreePool (RequestData);\r
+ }\r
+ERROR_3:\r
+ HttpBootFreeHeader (HttpIoHeader);\r
+ERROR_2:\r
+ if (Cache != NULL) {\r
+ FreePool (Cache);\r
+ }\r
+ERROR_1:\r
+ if (Url != NULL) {\r
+ FreePool (Url);\r
+ }\r
+\r
+ return Status;\r
+}\r