HttpBootClient.c revision ef422fc53c4bc978767fcee35b284f61c02ea6d5
1/** @file
2  Implementation of the boot file download function.
3
4Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials are licensed and made available under
6the terms and conditions of the BSD License that accompanies this distribution.
7The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php.
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT 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**/
27EFI_STATUS
28HttpBootUpdateDevicePath (
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**/
146EFI_STATUS
147HttpBootDhcp4ExtractUriInfo (
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**/
235EFI_STATUS
236HttpBootDhcp6ExtractUriInfo (
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
369Error:
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**/
388EFI_STATUS
389HttpBootDiscoverBootInfo (
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**/
422EFI_STATUS
423HttpBootCreateHttpIo (
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**/
465VOID
466HttpBootFreeCache (
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**/
521VOID
522HttpBootFreeCacheList (
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**/
554EFI_STATUS
555HttpBootGetFileFromCache (
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**/
637EFI_STATUS
638EFIAPI
639HttpBootGetBootFileCallback (
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**/
711EFI_STATUS
712HttpBootGetBootFile (
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_RESPONSE_DATA      *ResponseData;
723  HTTP_IO_RESPONSE_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_RESPONSE_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_RESPONSE_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
1049ERROR_6:
1050  if (Parser != NULL) {
1051    HttpFreeMsgParser (Parser);
1052  }
1053  if (Context.Block != NULL) {
1054    FreePool (Context.Block);
1055  }
1056  HttpBootFreeCache (Cache);
1057
1058ERROR_5:
1059  if (ResponseData != NULL) {
1060    FreePool (ResponseData);
1061  }
1062ERROR_4:
1063  if (RequestData != NULL) {
1064    FreePool (RequestData);
1065  }
1066ERROR_3:
1067  HttpBootFreeHeader (HttpIoHeader);
1068ERROR_2:
1069  if (Cache != NULL) {
1070    FreePool (Cache);
1071  }
1072ERROR_1:
1073  if (Url != NULL) {
1074    FreePool (Url);
1075  }
1076
1077  return Status;
1078}
1079