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