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
HttpBootUpdateDevicePath(IN HTTP_BOOT_PRIVATE_DATA * Private)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
HttpBootDhcp4ExtractUriInfo(IN HTTP_BOOT_PRIVATE_DATA * Private)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
HttpBootDhcp6ExtractUriInfo(IN HTTP_BOOT_PRIVATE_DATA * Private)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
HttpBootDiscoverBootInfo(IN OUT HTTP_BOOT_PRIVATE_DATA * Private)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
HttpBootCreateHttpIo(IN HTTP_BOOT_PRIVATE_DATA * Private)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
HttpBootFreeCache(IN HTTP_BOOT_CACHE_CONTENT * Cache)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
HttpBootFreeCacheList(IN HTTP_BOOT_PRIVATE_DATA * Private)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
HttpBootGetFileFromCache(IN HTTP_BOOT_PRIVATE_DATA * Private,IN CHAR16 * Uri,IN OUT UINTN * BufferSize,OUT UINT8 * Buffer)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
HttpBootGetBootFileCallback(IN HTTP_BODY_PARSE_EVENT EventType,IN CHAR8 * Data,IN UINTN Length,IN VOID * Context)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
HttpBootGetBootFile(IN HTTP_BOOT_PRIVATE_DATA * Private,IN BOOLEAN HeaderOnly,IN OUT UINTN * BufferSize,OUT UINT8 * Buffer)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