Home | History | Annotate | Download | only in HttpBootDxe
      1 /** @file
      2   Implementation of the boot file download function.
      3 
      4 Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials are licensed and made available under
      6 the terms and conditions of the BSD License that accompanies this distribution.
      7 The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php.
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "HttpBootDxe.h"
     16 
     17 /**
     18   Update the IP and URL device path node to include the boot resource information.
     19 
     20   @param[in]    Private            The pointer to the driver's private data.
     21 
     22   @retval EFI_SUCCESS              Device patch successfully updated.
     23   @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.
     24   @retval Others                   Unexpected error happened.
     25 
     26 **/
     27 EFI_STATUS
     28 HttpBootUpdateDevicePath (
     29   IN   HTTP_BOOT_PRIVATE_DATA   *Private
     30   )
     31 {
     32   EFI_DEV_PATH               *Node;
     33   EFI_DEVICE_PATH_PROTOCOL   *TmpDevicePath;
     34   EFI_DEVICE_PATH_PROTOCOL   *NewDevicePath;
     35   UINTN                      Length;
     36   EFI_STATUS                 Status;
     37 
     38   TmpDevicePath = NULL;
     39 
     40   //
     41   // Update the IP node with DHCP assigned information.
     42   //
     43   if (!Private->UsingIpv6) {
     44     Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));
     45     if (Node == NULL) {
     46       return EFI_OUT_OF_RESOURCES;
     47     }
     48     Node->Ipv4.Header.Type    = MESSAGING_DEVICE_PATH;
     49     Node->Ipv4.Header.SubType = MSG_IPv4_DP;
     50     SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));
     51     CopyMem (&Node->Ipv4.LocalIpAddress, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
     52     Node->Ipv4.RemotePort      = Private->Port;
     53     Node->Ipv4.Protocol        = EFI_IP_PROTO_TCP;
     54     Node->Ipv4.StaticIpAddress = FALSE;
     55     CopyMem (&Node->Ipv4.GatewayIpAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));
     56     CopyMem (&Node->Ipv4.SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
     57   } else {
     58     Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH));
     59     if (Node == NULL) {
     60       return EFI_OUT_OF_RESOURCES;
     61     }
     62     Node->Ipv6.Header.Type     = MESSAGING_DEVICE_PATH;
     63     Node->Ipv6.Header.SubType  = MSG_IPv6_DP;
     64     SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH));
     65     Node->Ipv6.PrefixLength    = IP6_PREFIX_LENGTH;
     66     Node->Ipv6.RemotePort      = Private->Port;
     67     Node->Ipv6.Protocol        = EFI_IP_PROTO_TCP;
     68     Node->Ipv6.IpAddressOrigin = 0;
     69     CopyMem (&Node->Ipv6.LocalIpAddress, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));
     70     CopyMem (&Node->Ipv6.RemoteIpAddress, &Private->ServerIp.v6, sizeof (EFI_IPv6_ADDRESS));
     71     CopyMem (&Node->Ipv6.GatewayIpAddress, &Private->GatewayIp.v6, sizeof (EFI_IPv6_ADDRESS));
     72   }
     73 
     74   TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
     75   FreePool (Node);
     76   if (TmpDevicePath == NULL) {
     77     return EFI_OUT_OF_RESOURCES;
     78   }
     79 
     80   //
     81   // Update the URI node with the boot file URI.
     82   //
     83   Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (Private->BootFileUri);
     84   Node = AllocatePool (Length);
     85   if (Node == NULL) {
     86     FreePool (TmpDevicePath);
     87     return EFI_OUT_OF_RESOURCES;
     88   }
     89   Node->DevPath.Type    = MESSAGING_DEVICE_PATH;
     90   Node->DevPath.SubType = MSG_URI_DP;
     91   SetDevicePathNodeLength (Node, Length);
     92   CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Private->BootFileUri, AsciiStrSize (Private->BootFileUri));
     93 
     94   NewDevicePath = AppendDevicePathNode (TmpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
     95   FreePool (Node);
     96   FreePool (TmpDevicePath);
     97   if (NewDevicePath == NULL) {
     98     return EFI_OUT_OF_RESOURCES;
     99   }
    100 
    101   if (!Private->UsingIpv6) {
    102     //
    103     // Reinstall the device path protocol of the child handle.
    104     //
    105     Status = gBS->ReinstallProtocolInterface (
    106                     Private->Ip4Nic->Controller,
    107                     &gEfiDevicePathProtocolGuid,
    108                     Private->Ip4Nic->DevicePath,
    109                     NewDevicePath
    110                     );
    111     if (EFI_ERROR (Status)) {
    112       return Status;
    113     }
    114 
    115     FreePool (Private->Ip4Nic->DevicePath);
    116     Private->Ip4Nic->DevicePath = NewDevicePath;
    117   } else {
    118     //
    119     // Reinstall the device path protocol of the child handle.
    120     //
    121     Status = gBS->ReinstallProtocolInterface (
    122                     Private->Ip6Nic->Controller,
    123                     &gEfiDevicePathProtocolGuid,
    124                     Private->Ip6Nic->DevicePath,
    125                     NewDevicePath
    126                     );
    127     if (EFI_ERROR (Status)) {
    128       return Status;
    129     }
    130     FreePool (Private->Ip6Nic->DevicePath);
    131     Private->Ip6Nic->DevicePath = NewDevicePath;
    132   }
    133 
    134   return EFI_SUCCESS;
    135 }
    136 
    137 /**
    138   Parse the boot file URI information from the selected Dhcp4 offer packet.
    139 
    140   @param[in]    Private        The pointer to the driver's private data.
    141 
    142   @retval EFI_SUCCESS          Successfully parsed out all the boot information.
    143   @retval Others               Failed to parse out the boot information.
    144 
    145 **/
    146 EFI_STATUS
    147 HttpBootDhcp4ExtractUriInfo (
    148   IN     HTTP_BOOT_PRIVATE_DATA   *Private
    149   )
    150 {
    151   HTTP_BOOT_DHCP4_PACKET_CACHE    *SelectOffer;
    152   HTTP_BOOT_DHCP4_PACKET_CACHE    *HttpOffer;
    153   UINT32                          SelectIndex;
    154   UINT32                          ProxyIndex;
    155   EFI_DHCP4_PACKET_OPTION         *Option;
    156   EFI_STATUS                      Status;
    157 
    158   ASSERT (Private != NULL);
    159   ASSERT (Private->SelectIndex != 0);
    160   SelectIndex = Private->SelectIndex - 1;
    161   ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);
    162 
    163   Status = EFI_SUCCESS;
    164 
    165   //
    166   // SelectOffer contains the IP address configuration and name server configuration.
    167   // HttpOffer contains the boot file URL.
    168   //
    169   SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp4;
    170   if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {
    171     HttpOffer = SelectOffer;
    172   } else {
    173     ASSERT (Private->SelectProxyType != HttpOfferTypeMax);
    174     ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
    175     HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4;
    176   }
    177 
    178   //
    179   // Configure the default DNS server if server assigned.
    180   //
    181   if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || (SelectOffer->OfferType == HttpOfferTypeDhcpDns)) {
    182     Option = SelectOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER];
    183     ASSERT (Option != NULL);
    184     Status = HttpBootRegisterIp4Dns (
    185                Private,
    186                Option->Length,
    187                Option->Data
    188                );
    189     if (EFI_ERROR (Status)) {
    190       return Status;
    191     }
    192   }
    193 
    194   //
    195   // Extract the port from URL, and use default HTTP port 80 if not provided.
    196   //
    197   Status = HttpUrlGetPort (
    198              (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data,
    199              HttpOffer->UriParser,
    200              &Private->Port
    201              );
    202   if (EFI_ERROR (Status) || Private->Port == 0) {
    203     Private->Port = 80;
    204   }
    205 
    206   //
    207   // Record the URI of boot file from the selected HTTP offer.
    208   //
    209   Private->BootFileUriParser = HttpOffer->UriParser;
    210   Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data;
    211 
    212 
    213   //
    214   // All boot informations are valid here.
    215   //
    216   AsciiPrint ("\n  URI: %a", Private->BootFileUri);
    217 
    218   //
    219   // Update the device path to include the IP and boot URI information.
    220   //
    221   Status = HttpBootUpdateDevicePath (Private);
    222 
    223   return Status;
    224 }
    225 
    226 /**
    227   Parse the boot file URI information from the selected Dhcp6 offer packet.
    228 
    229   @param[in]    Private        The pointer to the driver's private data.
    230 
    231   @retval EFI_SUCCESS          Successfully parsed out all the boot information.
    232   @retval Others               Failed to parse out the boot information.
    233 
    234 **/
    235 EFI_STATUS
    236 HttpBootDhcp6ExtractUriInfo (
    237   IN     HTTP_BOOT_PRIVATE_DATA   *Private
    238   )
    239 {
    240   HTTP_BOOT_DHCP6_PACKET_CACHE    *SelectOffer;
    241   HTTP_BOOT_DHCP6_PACKET_CACHE    *HttpOffer;
    242   UINT32                          SelectIndex;
    243   UINT32                          ProxyIndex;
    244   EFI_DHCP6_PACKET_OPTION         *Option;
    245   EFI_IPv6_ADDRESS                IpAddr;
    246   CHAR8                           *HostName;
    247   CHAR16                          *HostNameStr;
    248   EFI_STATUS                      Status;
    249 
    250   ASSERT (Private != NULL);
    251   ASSERT (Private->SelectIndex != 0);
    252   SelectIndex = Private->SelectIndex - 1;
    253   ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);
    254 
    255   Status   = EFI_SUCCESS;
    256   HostName = NULL;
    257   //
    258   // SelectOffer contains the IP address configuration and name server configuration.
    259   // HttpOffer contains the boot file URL.
    260   //
    261   SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp6;
    262   if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {
    263     HttpOffer = SelectOffer;
    264   } else {
    265     ASSERT (Private->SelectProxyType != HttpOfferTypeMax);
    266     ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
    267     HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp6;
    268   }
    269 
    270   //
    271   //  Set the Local station address to IP layer.
    272   //
    273   Status = HttpBootSetIp6Address (Private);
    274   if (EFI_ERROR (Status)) {
    275     return Status;
    276   }
    277 
    278   //
    279   // Configure the default DNS server if server assigned.
    280   //
    281   if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || (SelectOffer->OfferType == HttpOfferTypeDhcpDns)) {
    282     Option = SelectOffer->OptList[HTTP_BOOT_DHCP6_IDX_DNS_SERVER];
    283     ASSERT (Option != NULL);
    284     Status = HttpBootSetIp6Dns (
    285                Private,
    286                HTONS (Option->OpLen),
    287                Option->Data
    288                );
    289     if (EFI_ERROR (Status)) {
    290       return Status;
    291     }
    292   }
    293 
    294   //
    295   // Extract the HTTP server Ip frome URL. This is used to Check route table
    296   // whether can send message to HTTP Server Ip through the GateWay.
    297   //
    298   Status = HttpUrlGetIp6 (
    299              (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,
    300              HttpOffer->UriParser,
    301              &IpAddr
    302              );
    303 
    304   if (EFI_ERROR (Status)) {
    305     //
    306     // The Http server address is expressed by Name Ip, so perform DNS resolution
    307     //
    308     Status = HttpUrlGetHostName (
    309                (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,
    310                HttpOffer->UriParser,
    311                &HostName
    312                );
    313     if (EFI_ERROR (Status)) {
    314       return Status;
    315     }
    316 
    317     HostNameStr = AllocateZeroPool ((AsciiStrLen (HostName) + 1) * sizeof (CHAR16));
    318     if (HostNameStr == NULL) {
    319       Status = EFI_OUT_OF_RESOURCES;
    320       goto Error;
    321     }
    322 
    323     AsciiStrToUnicodeStr (HostName, HostNameStr);
    324     Status = HttpBootDns (Private, HostNameStr, &IpAddr);
    325     FreePool (HostNameStr);
    326     if (EFI_ERROR (Status)) {
    327       goto Error;
    328     }
    329   }
    330 
    331   CopyMem (&Private->ServerIp.v6, &IpAddr, sizeof (EFI_IPv6_ADDRESS));
    332 
    333   //
    334   // register the IPv6 gateway address to the network device.
    335   //
    336   Status = HttpBootSetIp6Gateway (Private);
    337   if (EFI_ERROR (Status)) {
    338     return Status;
    339   }
    340 
    341   //
    342   // Extract the port from URL, and use default HTTP port 80 if not provided.
    343   //
    344   Status = HttpUrlGetPort (
    345              (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,
    346              HttpOffer->UriParser,
    347              &Private->Port
    348              );
    349   if (EFI_ERROR (Status) || Private->Port == 0) {
    350     Private->Port = 80;
    351   }
    352 
    353   //
    354   // Record the URI of boot file from the selected HTTP offer.
    355   //
    356   Private->BootFileUriParser = HttpOffer->UriParser;
    357   Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data;
    358 
    359 
    360   //
    361   // All boot informations are valid here.
    362   //
    363   AsciiPrint ("\n  URI: %a", Private->BootFileUri);
    364   //
    365   // Update the device path to include the IP and boot URI information.
    366   //
    367   Status = HttpBootUpdateDevicePath (Private);
    368 
    369 Error:
    370 
    371   if (HostName != NULL) {
    372     FreePool (HostName);
    373   }
    374 
    375   return Status;
    376 }
    377 
    378 
    379 /**
    380   Discover all the boot information for boot file.
    381 
    382   @param[in, out]    Private        The pointer to the driver's private data.
    383 
    384   @retval EFI_SUCCESS          Successfully obtained all the boot information .
    385   @retval Others               Failed to retrieve the boot information.
    386 
    387 **/
    388 EFI_STATUS
    389 HttpBootDiscoverBootInfo (
    390   IN OUT HTTP_BOOT_PRIVATE_DATA   *Private
    391   )
    392 {
    393   EFI_STATUS              Status;
    394 
    395   //
    396   // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
    397   // other Http boot information.
    398   //
    399   Status = HttpBootDhcp (Private);
    400   if (EFI_ERROR (Status)) {
    401     return Status;
    402   }
    403 
    404   if (!Private->UsingIpv6) {
    405     Status = HttpBootDhcp4ExtractUriInfo (Private);
    406   } else {
    407     Status = HttpBootDhcp6ExtractUriInfo (Private);
    408   }
    409 
    410   return Status;
    411 }
    412 
    413 /**
    414   Create a HttpIo instance for the file download.
    415 
    416   @param[in]    Private        The pointer to the driver's private data.
    417 
    418   @retval EFI_SUCCESS          Successfully created.
    419   @retval Others               Failed to create HttpIo.
    420 
    421 **/
    422 EFI_STATUS
    423 HttpBootCreateHttpIo (
    424   IN     HTTP_BOOT_PRIVATE_DATA       *Private
    425   )
    426 {
    427   HTTP_IO_CONFIG_DATA          ConfigData;
    428   EFI_STATUS                   Status;
    429 
    430   ASSERT (Private != NULL);
    431 
    432   ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA));
    433   if (!Private->UsingIpv6) {
    434     ConfigData.Config4.HttpVersion    = HttpVersion11;
    435     ConfigData.Config4.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;
    436     IP4_COPY_ADDRESS (&ConfigData.Config4.LocalIp, &Private->StationIp.v4);
    437     IP4_COPY_ADDRESS (&ConfigData.Config4.SubnetMask, &Private->SubnetMask.v4);
    438   } else {
    439     ConfigData.Config6.HttpVersion    = HttpVersion11;
    440     ConfigData.Config6.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;
    441     IP6_COPY_ADDRESS (&ConfigData.Config6.LocalIp, &Private->StationIp.v6);
    442   }
    443 
    444   Status = HttpIoCreateIo (
    445              Private->Image,
    446              Private->Controller,
    447              Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4,
    448              &ConfigData,
    449              &Private->HttpIo
    450              );
    451   if (EFI_ERROR (Status)) {
    452     return Status;
    453   }
    454 
    455   Private->HttpCreated = TRUE;
    456   return EFI_SUCCESS;
    457 }
    458 
    459 /**
    460   Release all the resource of a cache item.
    461 
    462   @param[in]          Cache         The pointer to the cache item.
    463 
    464 **/
    465 VOID
    466 HttpBootFreeCache (
    467   IN  HTTP_BOOT_CACHE_CONTENT    *Cache
    468   )
    469 {
    470   UINTN                       Index;
    471   LIST_ENTRY                  *Entry;
    472   LIST_ENTRY                  *NextEntry;
    473   HTTP_BOOT_ENTITY_DATA       *EntityData;
    474 
    475   if (Cache != NULL) {
    476     //
    477     // Free the request data
    478     //
    479     if (Cache->RequestData != NULL) {
    480       if (Cache->RequestData->Url != NULL) {
    481         FreePool (Cache->RequestData->Url);
    482       }
    483       FreePool (Cache->RequestData);
    484     }
    485 
    486     //
    487     // Free the response header
    488     //
    489     if (Cache->ResponseData != NULL) {
    490       if (Cache->ResponseData->Headers != NULL) {
    491         for (Index = 0; Index < Cache->ResponseData->HeaderCount; Index++) {
    492           FreePool (Cache->ResponseData->Headers[Index].FieldName);
    493           FreePool (Cache->ResponseData->Headers[Index].FieldValue);
    494         }
    495         FreePool (Cache->ResponseData->Headers);
    496       }
    497     }
    498 
    499     //
    500     // Free the response body
    501     //
    502     NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Cache->EntityDataList) {
    503       EntityData = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_ENTITY_DATA, Link);
    504       if (EntityData->Block != NULL) {
    505         FreePool (EntityData->Block);
    506       }
    507       RemoveEntryList (&EntityData->Link);
    508       FreePool (EntityData);
    509     }
    510 
    511     FreePool (Cache);
    512   }
    513 }
    514 
    515 /**
    516   Clean up all cached data.
    517 
    518   @param[in]          Private         The pointer to the driver's private data.
    519 
    520 **/
    521 VOID
    522 HttpBootFreeCacheList (
    523   IN     HTTP_BOOT_PRIVATE_DATA   *Private
    524   )
    525 {
    526   LIST_ENTRY                  *Entry;
    527   LIST_ENTRY                  *NextEntry;
    528   HTTP_BOOT_CACHE_CONTENT     *Cache;
    529 
    530   NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->CacheList) {
    531     Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);
    532     RemoveEntryList (&Cache->Link);
    533     HttpBootFreeCache (Cache);
    534   }
    535 }
    536 
    537 /**
    538   Get the file content from cached data.
    539 
    540   @param[in]          Private         The pointer to the driver's private data.
    541   @param[in]          Uri             Uri of the file to be retrieved from cache.
    542   @param[in, out]     BufferSize      On input the size of Buffer in bytes. On output with a return
    543                                       code of EFI_SUCCESS, the amount of data transferred to
    544                                       Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
    545                                       the size of Buffer required to retrieve the requested file.
    546   @param[out]         Buffer          The memory buffer to transfer the file to. IF Buffer is NULL,
    547                                       then the size of the requested file is returned in
    548                                       BufferSize.
    549 
    550   @retval EFI_SUCCESS          Successfully created.
    551   @retval Others               Failed to create HttpIo.
    552 
    553 **/
    554 EFI_STATUS
    555 HttpBootGetFileFromCache (
    556   IN     HTTP_BOOT_PRIVATE_DATA   *Private,
    557   IN     CHAR16                   *Uri,
    558   IN OUT UINTN                    *BufferSize,
    559      OUT UINT8                    *Buffer
    560   )
    561 {
    562   LIST_ENTRY                  *Entry;
    563   LIST_ENTRY                  *Entry2;
    564   HTTP_BOOT_CACHE_CONTENT     *Cache;
    565   HTTP_BOOT_ENTITY_DATA       *EntityData;
    566   UINTN                       CopyedSize;
    567 
    568   if (Uri == NULL || BufferSize == 0 || Buffer == NULL) {
    569     return EFI_INVALID_PARAMETER;
    570   }
    571 
    572   //
    573   // Search file in the cache list, the cache entry will be released upon a successful
    574   // match.
    575   //
    576   NET_LIST_FOR_EACH (Entry, &Private->CacheList) {
    577     Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);
    578     //
    579     // Compare the URI to see whether we already have a cache for this file.
    580     //
    581     if ((Cache->RequestData != NULL) &&
    582         (Cache->RequestData->Url != NULL) &&
    583         (StrCmp (Uri, Cache->RequestData->Url) == 0))
    584     {
    585       //
    586       // Hit cache, check buffer size.
    587       //
    588       if (*BufferSize < Cache->EntityLength) {
    589         *BufferSize = Cache->EntityLength;
    590         return EFI_BUFFER_TOO_SMALL;
    591       }
    592 
    593       //
    594       // Fill data to buffer.
    595       //
    596       CopyedSize = 0;
    597       NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) {
    598         EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link);
    599         if (*BufferSize > CopyedSize) {
    600           CopyMem (
    601             Buffer + CopyedSize,
    602             EntityData->DataStart,
    603             MIN (EntityData->DataLength, *BufferSize - CopyedSize)
    604             );
    605           CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize);
    606         }
    607       }
    608       *BufferSize = CopyedSize;
    609 
    610       //
    611       // On success, free the cached data to release the memory resource.
    612       //
    613       RemoveEntryList (&Cache->Link);
    614       HttpBootFreeCache (Cache);
    615       return EFI_SUCCESS;
    616     }
    617   }
    618 
    619   return EFI_NOT_FOUND;
    620 }
    621 
    622 /**
    623   A callback function to intercept events during message parser.
    624 
    625   This function will be invoked during HttpParseMessageBody() with various events type. An error
    626   return status of the callback function will cause the HttpParseMessageBody() aborted.
    627 
    628   @param[in]    EventType          Event type of this callback call.
    629   @param[in]    Data               A pointer to data buffer.
    630   @param[in]    Length             Length in bytes of the Data.
    631   @param[in]    Context            Callback context set by HttpInitMsgParser().
    632 
    633   @retval EFI_SUCCESS              Continue to parser the message body.
    634   @retval Others                   Abort the parse.
    635 
    636 **/
    637 EFI_STATUS
    638 EFIAPI
    639 HttpBootGetBootFileCallback (
    640   IN HTTP_BODY_PARSE_EVENT      EventType,
    641   IN CHAR8                      *Data,
    642   IN UINTN                      Length,
    643   IN VOID                       *Context
    644   )
    645 {
    646   HTTP_BOOT_CALLBACK_DATA      *CallbackData;
    647   HTTP_BOOT_ENTITY_DATA        *NewEntityData;
    648 
    649   //
    650   // We only care about the entity data.
    651   //
    652   if (EventType != BodyParseEventOnData) {
    653     return EFI_SUCCESS;
    654   }
    655 
    656   CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context;
    657   //
    658   // Copy data if caller has provided a buffer.
    659   //
    660   if (CallbackData->BufferSize > CallbackData->CopyedSize) {
    661     CopyMem (
    662       CallbackData->Buffer + CallbackData->CopyedSize,
    663       Data,
    664       MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize)
    665       );
    666     CallbackData->CopyedSize += MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize);
    667   }
    668 
    669   //
    670   // The caller doesn't provide a buffer, save the block into cache list.
    671   //
    672   if (CallbackData->Cache != NULL) {
    673     NewEntityData = AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA));
    674     if (NewEntityData == NULL) {
    675       return EFI_OUT_OF_RESOURCES;
    676     }
    677     if (CallbackData->NewBlock) {
    678       NewEntityData->Block = CallbackData->Block;
    679       CallbackData->Block = NULL;
    680     }
    681     NewEntityData->DataLength = Length;
    682     NewEntityData->DataStart  = (UINT8*) Data;
    683     InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link);
    684   }
    685   return EFI_SUCCESS;
    686 }
    687 
    688 /**
    689   This function download the boot file by using UEFI HTTP protocol.
    690 
    691   @param[in]       Private         The pointer to the driver's private data.
    692   @param[in]       HeaderOnly      Only request the response header, it could save a lot of time if
    693                                    the caller only want to know the size of the requested file.
    694   @param[in, out]  BufferSize      On input the size of Buffer in bytes. On output with a return
    695                                    code of EFI_SUCCESS, the amount of data transferred to
    696                                    Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
    697                                    the size of Buffer required to retrieve the requested file.
    698   @param[out]      Buffer          The memory buffer to transfer the file to. IF Buffer is NULL,
    699                                    then the size of the requested file is returned in
    700                                    BufferSize.
    701 
    702   @retval EFI_SUCCESS              The file was loaded.
    703   @retval EFI_INVALID_PARAMETER    BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.
    704   @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources
    705   @retval EFI_BUFFER_TOO_SMALL     The BufferSize is too small to read the current directory entry.
    706                                    BufferSize has been updated with the size needed to complete
    707                                    the request.
    708   @retval Others                   Unexpected error happened.
    709 
    710 **/
    711 EFI_STATUS
    712 HttpBootGetBootFile (
    713   IN     HTTP_BOOT_PRIVATE_DATA   *Private,
    714   IN     BOOLEAN                  HeaderOnly,
    715   IN OUT UINTN                    *BufferSize,
    716      OUT UINT8                    *Buffer
    717   )
    718 {
    719   EFI_STATUS                 Status;
    720   CHAR8                      *HostName;
    721   EFI_HTTP_REQUEST_DATA      *RequestData;
    722   HTTP_IO_RESOPNSE_DATA      *ResponseData;
    723   HTTP_IO_RESOPNSE_DATA      ResponseBody;
    724   HTTP_IO                    *HttpIo;
    725   HTTP_IO_HEADER             *HttpIoHeader;
    726   VOID                       *Parser;
    727   HTTP_BOOT_CALLBACK_DATA    Context;
    728   UINTN                      ContentLength;
    729   HTTP_BOOT_CACHE_CONTENT    *Cache;
    730   UINT8                      *Block;
    731   CHAR16                     *Url;
    732   BOOLEAN                    IdentityMode;
    733   UINTN                      ReceivedSize;
    734 
    735   ASSERT (Private != NULL);
    736   ASSERT (Private->HttpCreated);
    737 
    738   if (BufferSize == NULL) {
    739     return EFI_INVALID_PARAMETER;
    740   }
    741 
    742   if (*BufferSize != 0 && Buffer == NULL) {
    743     return EFI_INVALID_PARAMETER;
    744   }
    745 
    746   //
    747   // First, check whether we already cached the requested Uri.
    748   //
    749   Url = AllocatePool ((AsciiStrLen (Private->BootFileUri) + 1) * sizeof (CHAR16));
    750   if (Url == NULL) {
    751     return EFI_OUT_OF_RESOURCES;
    752   }
    753   AsciiStrToUnicodeStr (Private->BootFileUri, Url);
    754   if (!HeaderOnly) {
    755     Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer);
    756     if (Status != EFI_NOT_FOUND) {
    757       FreePool (Url);
    758       return Status;
    759     }
    760   }
    761 
    762   //
    763   // Not found in cache, try to download it through HTTP.
    764   //
    765 
    766   //
    767   // 1. Create a temp cache item for the requested URI if caller doesn't provide buffer.
    768   //
    769   Cache = NULL;
    770   if ((!HeaderOnly) && (*BufferSize == 0)) {
    771     Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT));
    772     if (Cache == NULL) {
    773       Status = EFI_OUT_OF_RESOURCES;
    774       goto ERROR_1;
    775     }
    776     InitializeListHead (&Cache->EntityDataList);
    777   }
    778 
    779   //
    780   // 2. Send HTTP request message.
    781   //
    782 
    783   //
    784   // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:
    785   //       Host
    786   //       Accept
    787   //       User-Agent
    788   //
    789   HttpIoHeader = HttpBootCreateHeader (3);
    790   if (HttpIoHeader == NULL) {
    791     Status = EFI_OUT_OF_RESOURCES;
    792     goto ERROR_2;
    793   }
    794 
    795   //
    796   // Add HTTP header field 1: Host
    797   //
    798   HostName = NULL;
    799   Status = HttpUrlGetHostName (
    800              Private->BootFileUri,
    801              Private->BootFileUriParser,
    802              &HostName
    803              );
    804   if (EFI_ERROR (Status)) {
    805     goto ERROR_3;
    806   }
    807   Status = HttpBootSetHeader (
    808              HttpIoHeader,
    809              HTTP_FIELD_NAME_HOST,
    810              HostName
    811              );
    812   FreePool (HostName);
    813   if (EFI_ERROR (Status)) {
    814     goto ERROR_3;
    815   }
    816 
    817   //
    818   // Add HTTP header field 2: Accept
    819   //
    820   Status = HttpBootSetHeader (
    821              HttpIoHeader,
    822              HTTP_FIELD_NAME_ACCEPT,
    823              "*/*"
    824              );
    825   if (EFI_ERROR (Status)) {
    826     goto ERROR_3;
    827   }
    828 
    829   //
    830   // Add HTTP header field 3: User-Agent
    831   //
    832   Status = HttpBootSetHeader (
    833              HttpIoHeader,
    834              HTTP_FIELD_NAME_USER_AGENT,
    835              HTTP_USER_AGENT_EFI_HTTP_BOOT
    836              );
    837   if (EFI_ERROR (Status)) {
    838     goto ERROR_3;
    839   }
    840 
    841   //
    842   // 2.2 Build the rest of HTTP request info.
    843   //
    844   RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA));
    845   if (RequestData == NULL) {
    846     Status = EFI_OUT_OF_RESOURCES;
    847     goto ERROR_3;
    848   }
    849   RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet;
    850   RequestData->Url = Url;
    851   if (RequestData->Url == NULL) {
    852     Status = EFI_OUT_OF_RESOURCES;
    853     goto ERROR_4;
    854   }
    855   AsciiStrToUnicodeStr (Private->BootFileUri, RequestData->Url);
    856 
    857   //
    858   // 2.3 Record the request info in a temp cache item.
    859   //
    860   if (Cache != NULL) {
    861     Cache->RequestData = RequestData;
    862   }
    863 
    864   //
    865   // 2.4 Send out the request to HTTP server.
    866   //
    867   HttpIo = &Private->HttpIo;
    868   Status = HttpIoSendRequest (
    869              HttpIo,
    870              RequestData,
    871              HttpIoHeader->HeaderCount,
    872              HttpIoHeader->Headers,
    873              0,
    874              NULL
    875             );
    876   if (EFI_ERROR (Status)) {
    877     goto ERROR_4;
    878   }
    879 
    880   //
    881   // 3. Receive HTTP response message.
    882   //
    883 
    884   //
    885   // 3.1 First step, use zero BodyLength to only receive the response headers.
    886   //
    887   ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESOPNSE_DATA));
    888   if (ResponseData == NULL) {
    889     Status = EFI_OUT_OF_RESOURCES;
    890     goto ERROR_4;
    891   }
    892   Status = HttpIoRecvResponse (
    893              &Private->HttpIo,
    894              TRUE,
    895              ResponseData
    896              );
    897   if (EFI_ERROR (Status)) {
    898     goto ERROR_5;
    899   }
    900 
    901   //
    902   // 3.2 Cache the response header.
    903   //
    904   if (Cache != NULL) {
    905     Cache->ResponseData = ResponseData;
    906   }
    907 
    908   //
    909   // 3.3 Init a message-body parser from the header information.
    910   //
    911   Parser = NULL;
    912   Context.NewBlock   = FALSE;
    913   Context.Block      = NULL;
    914   Context.CopyedSize = 0;
    915   Context.Buffer     = Buffer;
    916   Context.BufferSize = *BufferSize;
    917   Context.Cache      = Cache;
    918   Status = HttpInitMsgParser (
    919              HeaderOnly? HttpMethodHead : HttpMethodGet,
    920              ResponseData->Response.StatusCode,
    921              ResponseData->HeaderCount,
    922              ResponseData->Headers,
    923              HttpBootGetBootFileCallback,
    924              (VOID*) &Context,
    925              &Parser
    926              );
    927   if (EFI_ERROR (Status)) {
    928     goto ERROR_6;
    929   }
    930 
    931   //
    932   // 3.4 Continue to receive and parse message-body if needed.
    933   //
    934   Block = NULL;
    935   if (!HeaderOnly) {
    936     //
    937     // 3.4.1, check whether we are in identity transfer-coding.
    938     //
    939     ContentLength = 0;
    940     Status = HttpGetEntityLength (Parser, &ContentLength);
    941     if (!EFI_ERROR (Status)) {
    942       IdentityMode = TRUE;
    943     } else {
    944       IdentityMode = FALSE;
    945     }
    946 
    947     //
    948     // 3.4.2, start the message-body download, the identity and chunked transfer-coding
    949     // is handled in different path here.
    950     //
    951     ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESOPNSE_DATA));
    952     if (IdentityMode) {
    953       //
    954       // In identity transfer-coding there is no need to parse the message body,
    955       // just download the message body to the user provided buffer directly.
    956       //
    957       ReceivedSize = 0;
    958       while (ReceivedSize < ContentLength) {
    959         ResponseBody.Body       = (CHAR8*) Buffer + ReceivedSize;
    960         ResponseBody.BodyLength = *BufferSize - ReceivedSize;
    961         Status = HttpIoRecvResponse (
    962                    &Private->HttpIo,
    963                    FALSE,
    964                    &ResponseBody
    965                    );
    966         if (EFI_ERROR (Status)) {
    967           goto ERROR_6;
    968         }
    969         ReceivedSize += ResponseBody.BodyLength;
    970       }
    971     } else {
    972       //
    973       // In "chunked" transfer-coding mode, so we need to parse the received
    974       // data to get the real entity content.
    975       //
    976       Block = NULL;
    977       while (!HttpIsMessageComplete (Parser)) {
    978         //
    979         // Allocate a buffer in Block to hold the message-body.
    980         // If caller provides a buffer, this Block will be reused in every HttpIoRecvResponse().
    981         // Otherwise a buffer, the buffer in Block will be cached and we should allocate a new before
    982         // every HttpIoRecvResponse().
    983         //
    984         if (Block == NULL || Context.BufferSize == 0) {
    985           Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE);
    986           if (Block == NULL) {
    987             Status = EFI_OUT_OF_RESOURCES;
    988             goto ERROR_6;
    989           }
    990           Context.NewBlock = TRUE;
    991           Context.Block = Block;
    992         } else {
    993           Context.NewBlock = FALSE;
    994         }
    995 
    996         ResponseBody.Body       = (CHAR8*) Block;
    997         ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE;
    998         Status = HttpIoRecvResponse (
    999                    &Private->HttpIo,
   1000                    FALSE,
   1001                    &ResponseBody
   1002                    );
   1003         if (EFI_ERROR (Status)) {
   1004           goto ERROR_6;
   1005         }
   1006 
   1007         //
   1008         // Parse the new received block of the message-body, the block will be saved in cache.
   1009         //
   1010         Status = HttpParseMessageBody (
   1011                    Parser,
   1012                    ResponseBody.BodyLength,
   1013                    ResponseBody.Body
   1014                    );
   1015         if (EFI_ERROR (Status)) {
   1016           goto ERROR_6;
   1017         }
   1018       }
   1019     }
   1020   }
   1021 
   1022   //
   1023   // 3.5 Message-body receive & parse is completed, we should be able to get the file size now.
   1024   //
   1025   Status = HttpGetEntityLength (Parser, &ContentLength);
   1026   if (EFI_ERROR (Status)) {
   1027     goto ERROR_6;
   1028   }
   1029 
   1030   if (*BufferSize < ContentLength) {
   1031     Status = EFI_BUFFER_TOO_SMALL;
   1032   }
   1033   *BufferSize = ContentLength;
   1034 
   1035   //
   1036   // 4. Save the cache item to driver's cache list and return.
   1037   //
   1038   if (Cache != NULL) {
   1039     Cache->EntityLength = ContentLength;
   1040     InsertTailList (&Private->CacheList, &Cache->Link);
   1041   }
   1042 
   1043   if (Parser != NULL) {
   1044     HttpFreeMsgParser (Parser);
   1045   }
   1046 
   1047   return EFI_SUCCESS;
   1048 
   1049 ERROR_6:
   1050   if (Parser != NULL) {
   1051     HttpFreeMsgParser (Parser);
   1052   }
   1053   if (Context.Block != NULL) {
   1054     FreePool (Context.Block);
   1055   }
   1056   HttpBootFreeCache (Cache);
   1057 
   1058 ERROR_5:
   1059   if (ResponseData != NULL) {
   1060     FreePool (ResponseData);
   1061   }
   1062 ERROR_4:
   1063   if (RequestData != NULL) {
   1064     FreePool (RequestData);
   1065   }
   1066 ERROR_3:
   1067   HttpBootFreeHeader (HttpIoHeader);
   1068 ERROR_2:
   1069   if (Cache != NULL) {
   1070     FreePool (Cache);
   1071   }
   1072 ERROR_1:
   1073   if (Url != NULL) {
   1074     FreePool (Url);
   1075   }
   1076 
   1077   return Status;
   1078 }
   1079