2 Implementation of the boot file download function.
4 Copyright (c) 2015 - 2017, 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.
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.
16 #include "HttpBootDxe.h"
19 Update the device path node to include the boot resource information.
21 @param[in] Private The pointer to the driver's private data.
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.
29 HttpBootUpdateDevicePath (
30 IN HTTP_BOOT_PRIVATE_DATA
*Private
34 EFI_DEVICE_PATH_PROTOCOL
*TmpIpDevicePath
;
35 EFI_DEVICE_PATH_PROTOCOL
*TmpDnsDevicePath
;
36 EFI_DEVICE_PATH_PROTOCOL
*NewDevicePath
;
40 TmpIpDevicePath
= NULL
;
41 TmpDnsDevicePath
= NULL
;
44 // Update the IP node with DHCP assigned information.
46 if (!Private
->UsingIpv6
) {
47 Node
= AllocateZeroPool (sizeof (IPv4_DEVICE_PATH
));
49 return EFI_OUT_OF_RESOURCES
;
51 Node
->Ipv4
.Header
.Type
= MESSAGING_DEVICE_PATH
;
52 Node
->Ipv4
.Header
.SubType
= MSG_IPv4_DP
;
53 SetDevicePathNodeLength (Node
, sizeof (IPv4_DEVICE_PATH
));
54 CopyMem (&Node
->Ipv4
.LocalIpAddress
, &Private
->StationIp
, sizeof (EFI_IPv4_ADDRESS
));
55 Node
->Ipv4
.RemotePort
= Private
->Port
;
56 Node
->Ipv4
.Protocol
= EFI_IP_PROTO_TCP
;
57 Node
->Ipv4
.StaticIpAddress
= FALSE
;
58 CopyMem (&Node
->Ipv4
.GatewayIpAddress
, &Private
->GatewayIp
, sizeof (EFI_IPv4_ADDRESS
));
59 CopyMem (&Node
->Ipv4
.SubnetMask
, &Private
->SubnetMask
, sizeof (EFI_IPv4_ADDRESS
));
61 Node
= AllocateZeroPool (sizeof (IPv6_DEVICE_PATH
));
63 return EFI_OUT_OF_RESOURCES
;
65 Node
->Ipv6
.Header
.Type
= MESSAGING_DEVICE_PATH
;
66 Node
->Ipv6
.Header
.SubType
= MSG_IPv6_DP
;
67 SetDevicePathNodeLength (Node
, sizeof (IPv6_DEVICE_PATH
));
68 Node
->Ipv6
.PrefixLength
= IP6_PREFIX_LENGTH
;
69 Node
->Ipv6
.RemotePort
= Private
->Port
;
70 Node
->Ipv6
.Protocol
= EFI_IP_PROTO_TCP
;
71 Node
->Ipv6
.IpAddressOrigin
= 0;
72 CopyMem (&Node
->Ipv6
.LocalIpAddress
, &Private
->StationIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
73 CopyMem (&Node
->Ipv6
.RemoteIpAddress
, &Private
->ServerIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
74 CopyMem (&Node
->Ipv6
.GatewayIpAddress
, &Private
->GatewayIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
77 TmpIpDevicePath
= AppendDevicePathNode (Private
->ParentDevicePath
, (EFI_DEVICE_PATH_PROTOCOL
*) Node
);
79 if (TmpIpDevicePath
== NULL
) {
80 return EFI_OUT_OF_RESOURCES
;
84 // Update the DNS node with DNS server IP list if existed.
86 if (Private
->DnsServerIp
!= NULL
) {
87 Length
= sizeof (EFI_DEVICE_PATH_PROTOCOL
) + sizeof (Node
->Dns
.IsIPv6
) + Private
->DnsServerCount
* sizeof (EFI_IP_ADDRESS
);
88 Node
= AllocatePool (Length
);
90 FreePool (TmpIpDevicePath
);
91 return EFI_OUT_OF_RESOURCES
;
93 Node
->DevPath
.Type
= MESSAGING_DEVICE_PATH
;
94 Node
->DevPath
.SubType
= MSG_DNS_DP
;
95 SetDevicePathNodeLength (Node
, Length
);
96 Node
->Dns
.IsIPv6
= Private
->UsingIpv6
? 0x01 : 0x00;
97 CopyMem ((UINT8
*) Node
+ sizeof (EFI_DEVICE_PATH_PROTOCOL
) + sizeof (Node
->Dns
.IsIPv6
), Private
->DnsServerIp
, Private
->DnsServerCount
* sizeof (EFI_IP_ADDRESS
));
99 TmpDnsDevicePath
= AppendDevicePathNode (TmpIpDevicePath
, (EFI_DEVICE_PATH_PROTOCOL
*) Node
);
101 FreePool (TmpIpDevicePath
);
102 TmpIpDevicePath
= NULL
;
103 if (TmpDnsDevicePath
== NULL
) {
104 return EFI_OUT_OF_RESOURCES
;
109 // Update the URI node with the boot file URI.
111 Length
= sizeof (EFI_DEVICE_PATH_PROTOCOL
) + AsciiStrSize (Private
->BootFileUri
);
112 Node
= AllocatePool (Length
);
114 if (TmpIpDevicePath
!= NULL
) {
115 FreePool (TmpIpDevicePath
);
117 if (TmpDnsDevicePath
!= NULL
) {
118 FreePool (TmpDnsDevicePath
);
120 return EFI_OUT_OF_RESOURCES
;
122 Node
->DevPath
.Type
= MESSAGING_DEVICE_PATH
;
123 Node
->DevPath
.SubType
= MSG_URI_DP
;
124 SetDevicePathNodeLength (Node
, Length
);
125 CopyMem ((UINT8
*) Node
+ sizeof (EFI_DEVICE_PATH_PROTOCOL
), Private
->BootFileUri
, AsciiStrSize (Private
->BootFileUri
));
127 if (TmpDnsDevicePath
!= NULL
) {
128 NewDevicePath
= AppendDevicePathNode (TmpDnsDevicePath
, (EFI_DEVICE_PATH_PROTOCOL
*) Node
);
129 FreePool (TmpDnsDevicePath
);
131 ASSERT (TmpIpDevicePath
!= NULL
);
132 NewDevicePath
= AppendDevicePathNode (TmpIpDevicePath
, (EFI_DEVICE_PATH_PROTOCOL
*) Node
);
133 FreePool (TmpIpDevicePath
);
136 if (NewDevicePath
== NULL
) {
137 return EFI_OUT_OF_RESOURCES
;
140 if (!Private
->UsingIpv6
) {
142 // Reinstall the device path protocol of the child handle.
144 Status
= gBS
->ReinstallProtocolInterface (
145 Private
->Ip4Nic
->Controller
,
146 &gEfiDevicePathProtocolGuid
,
147 Private
->Ip4Nic
->DevicePath
,
150 if (EFI_ERROR (Status
)) {
154 FreePool (Private
->Ip4Nic
->DevicePath
);
155 Private
->Ip4Nic
->DevicePath
= NewDevicePath
;
158 // Reinstall the device path protocol of the child handle.
160 Status
= gBS
->ReinstallProtocolInterface (
161 Private
->Ip6Nic
->Controller
,
162 &gEfiDevicePathProtocolGuid
,
163 Private
->Ip6Nic
->DevicePath
,
166 if (EFI_ERROR (Status
)) {
169 FreePool (Private
->Ip6Nic
->DevicePath
);
170 Private
->Ip6Nic
->DevicePath
= NewDevicePath
;
177 Parse the boot file URI information from the selected Dhcp4 offer packet.
179 @param[in] Private The pointer to the driver's private data.
181 @retval EFI_SUCCESS Successfully parsed out all the boot information.
182 @retval Others Failed to parse out the boot information.
186 HttpBootDhcp4ExtractUriInfo (
187 IN HTTP_BOOT_PRIVATE_DATA
*Private
190 HTTP_BOOT_DHCP4_PACKET_CACHE
*SelectOffer
;
191 HTTP_BOOT_DHCP4_PACKET_CACHE
*HttpOffer
;
194 UINT32 DnsServerIndex
;
195 EFI_DHCP4_PACKET_OPTION
*Option
;
198 ASSERT (Private
!= NULL
);
199 ASSERT (Private
->SelectIndex
!= 0);
200 SelectIndex
= Private
->SelectIndex
- 1;
201 ASSERT (SelectIndex
< HTTP_BOOT_OFFER_MAX_NUM
);
205 Status
= EFI_SUCCESS
;
208 // SelectOffer contains the IP address configuration and name server configuration.
209 // HttpOffer contains the boot file URL.
211 SelectOffer
= &Private
->OfferBuffer
[SelectIndex
].Dhcp4
;
212 if (Private
->FilePathUri
== NULL
) {
214 // In Corporate environment, we need a HttpOffer.
216 if ((SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUri
) ||
217 (SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUriDns
) ||
218 (SelectOffer
->OfferType
== HttpOfferTypeDhcpNameUriDns
)) {
219 HttpOffer
= SelectOffer
;
221 ASSERT (Private
->SelectProxyType
!= HttpOfferTypeMax
);
222 ProxyIndex
= Private
->OfferIndex
[Private
->SelectProxyType
][0];
223 HttpOffer
= &Private
->OfferBuffer
[ProxyIndex
].Dhcp4
;
225 Private
->BootFileUriParser
= HttpOffer
->UriParser
;
226 Private
->BootFileUri
= (CHAR8
*) HttpOffer
->OptList
[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE
]->Data
;
229 // In Home environment the BootFileUri comes from the FilePath.
231 Private
->BootFileUriParser
= Private
->FilePathUriParser
;
232 Private
->BootFileUri
= Private
->FilePathUri
;
236 // Check the URI scheme.
238 Status
= HttpBootCheckUriScheme (Private
->BootFileUri
);
239 if (EFI_ERROR (Status
)) {
240 DEBUG ((EFI_D_ERROR
, "HttpBootDhcp4ExtractUriInfo: %r.\n", Status
));
244 if ((SelectOffer
->OfferType
== HttpOfferTypeDhcpNameUriDns
) ||
245 (SelectOffer
->OfferType
== HttpOfferTypeDhcpDns
) ||
246 (SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUriDns
)) {
247 Option
= SelectOffer
->OptList
[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER
];
248 ASSERT (Option
!= NULL
);
251 // Record the Dns Server address list.
253 Private
->DnsServerCount
= (Option
->Length
) / sizeof (EFI_IPv4_ADDRESS
);
255 Private
->DnsServerIp
= AllocateZeroPool (Private
->DnsServerCount
* sizeof (EFI_IP_ADDRESS
));
256 if (Private
->DnsServerIp
== NULL
) {
257 return EFI_OUT_OF_RESOURCES
;
260 for (DnsServerIndex
= 0; DnsServerIndex
< Private
->DnsServerCount
; DnsServerIndex
++) {
261 CopyMem (&(Private
->DnsServerIp
[DnsServerIndex
].v4
), &(((EFI_IPv4_ADDRESS
*) Option
->Data
)[DnsServerIndex
]), sizeof (EFI_IPv4_ADDRESS
));
265 // Configure the default DNS server if server assigned.
267 Status
= HttpBootRegisterIp4Dns (
272 if (EFI_ERROR (Status
)) {
273 FreePool (Private
->DnsServerIp
);
274 Private
->DnsServerIp
= NULL
;
280 // Extract the port from URL, and use default HTTP port 80 if not provided.
282 Status
= HttpUrlGetPort (
283 Private
->BootFileUri
,
284 Private
->BootFileUriParser
,
287 if (EFI_ERROR (Status
) || Private
->Port
== 0) {
292 // All boot informations are valid here.
296 // Update the device path to include the boot resource information.
298 Status
= HttpBootUpdateDevicePath (Private
);
299 if (EFI_ERROR (Status
) && Private
->DnsServerIp
!= NULL
) {
300 FreePool (Private
->DnsServerIp
);
301 Private
->DnsServerIp
= NULL
;
308 Parse the boot file URI information from the selected Dhcp6 offer packet.
310 @param[in] Private The pointer to the driver's private data.
312 @retval EFI_SUCCESS Successfully parsed out all the boot information.
313 @retval Others Failed to parse out the boot information.
317 HttpBootDhcp6ExtractUriInfo (
318 IN HTTP_BOOT_PRIVATE_DATA
*Private
321 HTTP_BOOT_DHCP6_PACKET_CACHE
*SelectOffer
;
322 HTTP_BOOT_DHCP6_PACKET_CACHE
*HttpOffer
;
325 UINT32 DnsServerIndex
;
326 EFI_DHCP6_PACKET_OPTION
*Option
;
327 EFI_IPv6_ADDRESS IpAddr
;
333 ASSERT (Private
!= NULL
);
334 ASSERT (Private
->SelectIndex
!= 0);
335 SelectIndex
= Private
->SelectIndex
- 1;
336 ASSERT (SelectIndex
< HTTP_BOOT_OFFER_MAX_NUM
);
340 Status
= EFI_SUCCESS
;
343 // SelectOffer contains the IP address configuration and name server configuration.
344 // HttpOffer contains the boot file URL.
346 SelectOffer
= &Private
->OfferBuffer
[SelectIndex
].Dhcp6
;
347 if (Private
->FilePathUri
== NULL
) {
349 // In Corporate environment, we need a HttpOffer.
351 if ((SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUri
) ||
352 (SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUriDns
) ||
353 (SelectOffer
->OfferType
== HttpOfferTypeDhcpNameUriDns
)) {
354 HttpOffer
= SelectOffer
;
356 ASSERT (Private
->SelectProxyType
!= HttpOfferTypeMax
);
357 ProxyIndex
= Private
->OfferIndex
[Private
->SelectProxyType
][0];
358 HttpOffer
= &Private
->OfferBuffer
[ProxyIndex
].Dhcp6
;
360 Private
->BootFileUriParser
= HttpOffer
->UriParser
;
361 Private
->BootFileUri
= (CHAR8
*) HttpOffer
->OptList
[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL
]->Data
;
364 // In Home environment the BootFileUri comes from the FilePath.
366 Private
->BootFileUriParser
= Private
->FilePathUriParser
;
367 Private
->BootFileUri
= Private
->FilePathUri
;
371 // Check the URI scheme.
373 Status
= HttpBootCheckUriScheme (Private
->BootFileUri
);
374 if (EFI_ERROR (Status
)) {
375 DEBUG ((EFI_D_ERROR
, "HttpBootDhcp6ExtractUriInfo: %r.\n", Status
));
380 // Set the Local station address to IP layer.
382 Status
= HttpBootSetIp6Address (Private
);
383 if (EFI_ERROR (Status
)) {
388 // Register the IPv6 gateway address to the network device.
390 Status
= HttpBootSetIp6Gateway (Private
);
391 if (EFI_ERROR (Status
)) {
395 if ((SelectOffer
->OfferType
== HttpOfferTypeDhcpNameUriDns
) ||
396 (SelectOffer
->OfferType
== HttpOfferTypeDhcpDns
) ||
397 (SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUriDns
)) {
398 Option
= SelectOffer
->OptList
[HTTP_BOOT_DHCP6_IDX_DNS_SERVER
];
399 ASSERT (Option
!= NULL
);
402 // Record the Dns Server address list.
404 Private
->DnsServerCount
= HTONS (Option
->OpLen
) / sizeof (EFI_IPv6_ADDRESS
);
406 Private
->DnsServerIp
= AllocateZeroPool (Private
->DnsServerCount
* sizeof (EFI_IP_ADDRESS
));
407 if (Private
->DnsServerIp
== NULL
) {
408 return EFI_OUT_OF_RESOURCES
;
411 for (DnsServerIndex
= 0; DnsServerIndex
< Private
->DnsServerCount
; DnsServerIndex
++) {
412 CopyMem (&(Private
->DnsServerIp
[DnsServerIndex
].v6
), &(((EFI_IPv6_ADDRESS
*) Option
->Data
)[DnsServerIndex
]), sizeof (EFI_IPv6_ADDRESS
));
416 // Configure the default DNS server if server assigned.
418 Status
= HttpBootSetIp6Dns (
420 HTONS (Option
->OpLen
),
423 if (EFI_ERROR (Status
)) {
429 // Extract the HTTP server Ip frome URL. This is used to Check route table
430 // whether can send message to HTTP Server Ip through the GateWay.
432 Status
= HttpUrlGetIp6 (
433 Private
->BootFileUri
,
434 Private
->BootFileUriParser
,
438 if (EFI_ERROR (Status
)) {
440 // The Http server address is expressed by Name Ip, so perform DNS resolution
442 Status
= HttpUrlGetHostName (
443 Private
->BootFileUri
,
444 Private
->BootFileUriParser
,
447 if (EFI_ERROR (Status
)) {
451 HostNameSize
= AsciiStrSize (HostName
);
452 HostNameStr
= AllocateZeroPool (HostNameSize
* sizeof (CHAR16
));
453 if (HostNameStr
== NULL
) {
454 Status
= EFI_OUT_OF_RESOURCES
;
458 AsciiStrToUnicodeStrS (HostName
, HostNameStr
, HostNameSize
);
460 if (HostName
!= NULL
) {
464 Status
= HttpBootDns (Private
, HostNameStr
, &IpAddr
);
465 FreePool (HostNameStr
);
466 if (EFI_ERROR (Status
)) {
471 CopyMem (&Private
->ServerIp
.v6
, &IpAddr
, sizeof (EFI_IPv6_ADDRESS
));
474 // Extract the port from URL, and use default HTTP port 80 if not provided.
476 Status
= HttpUrlGetPort (
477 Private
->BootFileUri
,
478 Private
->BootFileUriParser
,
481 if (EFI_ERROR (Status
) || Private
->Port
== 0) {
486 // All boot informations are valid here.
490 // Update the device path to include the boot resource information.
492 Status
= HttpBootUpdateDevicePath (Private
);
493 if (EFI_ERROR (Status
)) {
500 if (Private
->DnsServerIp
!= NULL
) {
501 FreePool (Private
->DnsServerIp
);
502 Private
->DnsServerIp
= NULL
;
510 Discover all the boot information for boot file.
512 @param[in, out] Private The pointer to the driver's private data.
514 @retval EFI_SUCCESS Successfully obtained all the boot information .
515 @retval Others Failed to retrieve the boot information.
519 HttpBootDiscoverBootInfo (
520 IN OUT HTTP_BOOT_PRIVATE_DATA
*Private
526 // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
527 // other Http boot information.
529 Status
= HttpBootDhcp (Private
);
530 if (EFI_ERROR (Status
)) {
534 if (!Private
->UsingIpv6
) {
535 Status
= HttpBootDhcp4ExtractUriInfo (Private
);
537 Status
= HttpBootDhcp6ExtractUriInfo (Private
);
544 HttpIo Callback function which will be invoked when specified HTTP_IO_CALLBACK_EVENT happened.
546 @param[in] EventType Indicate the Event type that occurs in the current callback.
547 @param[in] Message HTTP message which will be send to, or just received from HTTP server.
548 @param[in] Context The Callback Context pointer.
550 @retval EFI_SUCCESS Tells the HttpIo to continue the HTTP process.
551 @retval Others Tells the HttpIo to abort the current HTTP process.
555 HttpBootHttpIoCallback (
556 IN HTTP_IO_CALLBACK_EVENT EventType
,
557 IN EFI_HTTP_MESSAGE
*Message
,
561 HTTP_BOOT_PRIVATE_DATA
*Private
;
563 Private
= (HTTP_BOOT_PRIVATE_DATA
*) Context
;
564 if (Private
->HttpBootCallback
!= NULL
) {
565 Status
= Private
->HttpBootCallback
->Callback (
566 Private
->HttpBootCallback
,
567 EventType
== HttpIoRequest
? HttpBootHttpRequest
: HttpBootHttpResponse
,
568 EventType
== HttpIoRequest
? FALSE
: TRUE
,
569 sizeof (EFI_HTTP_MESSAGE
),
578 Create a HttpIo instance for the file download.
580 @param[in] Private The pointer to the driver's private data.
582 @retval EFI_SUCCESS Successfully created.
583 @retval Others Failed to create HttpIo.
587 HttpBootCreateHttpIo (
588 IN HTTP_BOOT_PRIVATE_DATA
*Private
591 HTTP_IO_CONFIG_DATA ConfigData
;
593 EFI_HANDLE ImageHandle
;
595 ASSERT (Private
!= NULL
);
597 ZeroMem (&ConfigData
, sizeof (HTTP_IO_CONFIG_DATA
));
598 if (!Private
->UsingIpv6
) {
599 ConfigData
.Config4
.HttpVersion
= HttpVersion11
;
600 ConfigData
.Config4
.RequestTimeOut
= HTTP_BOOT_REQUEST_TIMEOUT
;
601 IP4_COPY_ADDRESS (&ConfigData
.Config4
.LocalIp
, &Private
->StationIp
.v4
);
602 IP4_COPY_ADDRESS (&ConfigData
.Config4
.SubnetMask
, &Private
->SubnetMask
.v4
);
603 ImageHandle
= Private
->Ip4Nic
->ImageHandle
;
605 ConfigData
.Config6
.HttpVersion
= HttpVersion11
;
606 ConfigData
.Config6
.RequestTimeOut
= HTTP_BOOT_REQUEST_TIMEOUT
;
607 IP6_COPY_ADDRESS (&ConfigData
.Config6
.LocalIp
, &Private
->StationIp
.v6
);
608 ImageHandle
= Private
->Ip6Nic
->ImageHandle
;
611 Status
= HttpIoCreateIo (
614 Private
->UsingIpv6
? IP_VERSION_6
: IP_VERSION_4
,
616 HttpBootHttpIoCallback
,
620 if (EFI_ERROR (Status
)) {
624 Private
->HttpCreated
= TRUE
;
629 Release all the resource of a cache item.
631 @param[in] Cache The pointer to the cache item.
636 IN HTTP_BOOT_CACHE_CONTENT
*Cache
641 LIST_ENTRY
*NextEntry
;
642 HTTP_BOOT_ENTITY_DATA
*EntityData
;
646 // Free the request data
648 if (Cache
->RequestData
!= NULL
) {
649 if (Cache
->RequestData
->Url
!= NULL
) {
650 FreePool (Cache
->RequestData
->Url
);
652 FreePool (Cache
->RequestData
);
656 // Free the response header
658 if (Cache
->ResponseData
!= NULL
) {
659 if (Cache
->ResponseData
->Headers
!= NULL
) {
660 for (Index
= 0; Index
< Cache
->ResponseData
->HeaderCount
; Index
++) {
661 FreePool (Cache
->ResponseData
->Headers
[Index
].FieldName
);
662 FreePool (Cache
->ResponseData
->Headers
[Index
].FieldValue
);
664 FreePool (Cache
->ResponseData
->Headers
);
669 // Free the response body
671 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Cache
->EntityDataList
) {
672 EntityData
= NET_LIST_USER_STRUCT (Entry
, HTTP_BOOT_ENTITY_DATA
, Link
);
673 if (EntityData
->Block
!= NULL
) {
674 FreePool (EntityData
->Block
);
676 RemoveEntryList (&EntityData
->Link
);
677 FreePool (EntityData
);
685 Clean up all cached data.
687 @param[in] Private The pointer to the driver's private data.
691 HttpBootFreeCacheList (
692 IN HTTP_BOOT_PRIVATE_DATA
*Private
696 LIST_ENTRY
*NextEntry
;
697 HTTP_BOOT_CACHE_CONTENT
*Cache
;
699 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Private
->CacheList
) {
700 Cache
= NET_LIST_USER_STRUCT (Entry
, HTTP_BOOT_CACHE_CONTENT
, Link
);
701 RemoveEntryList (&Cache
->Link
);
702 HttpBootFreeCache (Cache
);
707 Get the file content from cached data.
709 @param[in] Private The pointer to the driver's private data.
710 @param[in] Uri Uri of the file to be retrieved from cache.
711 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
712 code of EFI_SUCCESS, the amount of data transferred to
713 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
714 the size of Buffer required to retrieve the requested file.
715 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
716 then the size of the requested file is returned in
718 @param[out] ImageType The image type of the downloaded file.
720 @retval EFI_SUCCESS Successfully created.
721 @retval Others Failed to create HttpIo.
725 HttpBootGetFileFromCache (
726 IN HTTP_BOOT_PRIVATE_DATA
*Private
,
728 IN OUT UINTN
*BufferSize
,
730 OUT HTTP_BOOT_IMAGE_TYPE
*ImageType
735 HTTP_BOOT_CACHE_CONTENT
*Cache
;
736 HTTP_BOOT_ENTITY_DATA
*EntityData
;
739 if (Uri
== NULL
|| BufferSize
== 0 || Buffer
== NULL
|| ImageType
== NULL
) {
740 return EFI_INVALID_PARAMETER
;
743 NET_LIST_FOR_EACH (Entry
, &Private
->CacheList
) {
744 Cache
= NET_LIST_USER_STRUCT (Entry
, HTTP_BOOT_CACHE_CONTENT
, Link
);
746 // Compare the URI to see whether we already have a cache for this file.
748 if ((Cache
->RequestData
!= NULL
) &&
749 (Cache
->RequestData
->Url
!= NULL
) &&
750 (StrCmp (Uri
, Cache
->RequestData
->Url
) == 0))
753 // Hit in cache, record image type.
755 *ImageType
= Cache
->ImageType
;
758 // Check buffer size.
760 if (*BufferSize
< Cache
->EntityLength
) {
761 *BufferSize
= Cache
->EntityLength
;
762 return EFI_BUFFER_TOO_SMALL
;
766 // Fill data to buffer.
769 NET_LIST_FOR_EACH (Entry2
, &Cache
->EntityDataList
) {
770 EntityData
= NET_LIST_USER_STRUCT (Entry2
, HTTP_BOOT_ENTITY_DATA
, Link
);
771 if (*BufferSize
> CopyedSize
) {
774 EntityData
->DataStart
,
775 MIN (EntityData
->DataLength
, *BufferSize
- CopyedSize
)
777 CopyedSize
+= MIN (EntityData
->DataLength
, *BufferSize
- CopyedSize
);
780 *BufferSize
= CopyedSize
;
785 return EFI_NOT_FOUND
;
789 A callback function to intercept events during message parser.
791 This function will be invoked during HttpParseMessageBody() with various events type. An error
792 return status of the callback function will cause the HttpParseMessageBody() aborted.
794 @param[in] EventType Event type of this callback call.
795 @param[in] Data A pointer to data buffer.
796 @param[in] Length Length in bytes of the Data.
797 @param[in] Context Callback context set by HttpInitMsgParser().
799 @retval EFI_SUCCESS Continue to parser the message body.
800 @retval Others Abort the parse.
805 HttpBootGetBootFileCallback (
806 IN HTTP_BODY_PARSE_EVENT EventType
,
812 HTTP_BOOT_CALLBACK_DATA
*CallbackData
;
813 HTTP_BOOT_ENTITY_DATA
*NewEntityData
;
815 EFI_HTTP_BOOT_CALLBACK_PROTOCOL
*HttpBootCallback
;
818 // We only care about the entity data.
820 if (EventType
!= BodyParseEventOnData
) {
824 CallbackData
= (HTTP_BOOT_CALLBACK_DATA
*) Context
;
825 HttpBootCallback
= CallbackData
->Private
->HttpBootCallback
;
826 if (HttpBootCallback
!= NULL
) {
827 Status
= HttpBootCallback
->Callback (
829 HttpBootHttpEntityBody
,
834 if (EFI_ERROR (Status
)) {
839 // Copy data if caller has provided a buffer.
841 if (CallbackData
->BufferSize
> CallbackData
->CopyedSize
) {
843 CallbackData
->Buffer
+ CallbackData
->CopyedSize
,
845 MIN (Length
, CallbackData
->BufferSize
- CallbackData
->CopyedSize
)
847 CallbackData
->CopyedSize
+= MIN (Length
, CallbackData
->BufferSize
- CallbackData
->CopyedSize
);
851 // The caller doesn't provide a buffer, save the block into cache list.
853 if (CallbackData
->Cache
!= NULL
) {
854 NewEntityData
= AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA
));
855 if (NewEntityData
== NULL
) {
856 return EFI_OUT_OF_RESOURCES
;
858 if (CallbackData
->NewBlock
) {
859 NewEntityData
->Block
= CallbackData
->Block
;
860 CallbackData
->Block
= NULL
;
862 NewEntityData
->DataLength
= Length
;
863 NewEntityData
->DataStart
= (UINT8
*) Data
;
864 InsertTailList (&CallbackData
->Cache
->EntityDataList
, &NewEntityData
->Link
);
870 This function download the boot file by using UEFI HTTP protocol.
872 @param[in] Private The pointer to the driver's private data.
873 @param[in] HeaderOnly Only request the response header, it could save a lot of time if
874 the caller only want to know the size of the requested file.
875 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
876 code of EFI_SUCCESS, the amount of data transferred to
877 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
878 the size of Buffer required to retrieve the requested file.
879 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
880 then the size of the requested file is returned in
882 @param[out] ImageType The image type of the downloaded file.
884 @retval EFI_SUCCESS The file was loaded.
885 @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.
886 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
887 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
888 BufferSize has been updated with the size needed to complete
890 @retval Others Unexpected error happened.
894 HttpBootGetBootFile (
895 IN HTTP_BOOT_PRIVATE_DATA
*Private
,
896 IN BOOLEAN HeaderOnly
,
897 IN OUT UINTN
*BufferSize
,
899 OUT HTTP_BOOT_IMAGE_TYPE
*ImageType
903 EFI_HTTP_STATUS_CODE StatusCode
;
905 EFI_HTTP_REQUEST_DATA
*RequestData
;
906 HTTP_IO_RESPONSE_DATA
*ResponseData
;
907 HTTP_IO_RESPONSE_DATA ResponseBody
;
909 HTTP_IO_HEADER
*HttpIoHeader
;
911 HTTP_BOOT_CALLBACK_DATA Context
;
913 HTTP_BOOT_CACHE_CONTENT
*Cache
;
917 BOOLEAN IdentityMode
;
920 ASSERT (Private
!= NULL
);
921 ASSERT (Private
->HttpCreated
);
923 if (BufferSize
== NULL
|| ImageType
== NULL
) {
924 return EFI_INVALID_PARAMETER
;
927 if (*BufferSize
!= 0 && Buffer
== NULL
) {
928 return EFI_INVALID_PARAMETER
;
932 // First, check whether we already cached the requested Uri.
934 UrlSize
= AsciiStrSize (Private
->BootFileUri
);
935 Url
= AllocatePool (UrlSize
* sizeof (CHAR16
));
937 return EFI_OUT_OF_RESOURCES
;
939 AsciiStrToUnicodeStrS (Private
->BootFileUri
, Url
, UrlSize
);
941 Status
= HttpBootGetFileFromCache (Private
, Url
, BufferSize
, Buffer
, ImageType
);
942 if (Status
!= EFI_NOT_FOUND
) {
949 // Not found in cache, try to download it through HTTP.
953 // 1. Create a temp cache item for the requested URI if caller doesn't provide buffer.
956 if ((!HeaderOnly
) && (*BufferSize
== 0)) {
957 Cache
= AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT
));
959 Status
= EFI_OUT_OF_RESOURCES
;
962 Cache
->ImageType
= ImageTypeMax
;
963 InitializeListHead (&Cache
->EntityDataList
);
967 // 2. Send HTTP request message.
971 // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:
976 HttpIoHeader
= HttpBootCreateHeader (3);
977 if (HttpIoHeader
== NULL
) {
978 Status
= EFI_OUT_OF_RESOURCES
;
983 // Add HTTP header field 1: Host
986 Status
= HttpUrlGetHostName (
987 Private
->BootFileUri
,
988 Private
->BootFileUriParser
,
991 if (EFI_ERROR (Status
)) {
994 Status
= HttpBootSetHeader (
1000 if (EFI_ERROR (Status
)) {
1005 // Add HTTP header field 2: Accept
1007 Status
= HttpBootSetHeader (
1012 if (EFI_ERROR (Status
)) {
1017 // Add HTTP header field 3: User-Agent
1019 Status
= HttpBootSetHeader (
1021 HTTP_HEADER_USER_AGENT
,
1022 HTTP_USER_AGENT_EFI_HTTP_BOOT
1024 if (EFI_ERROR (Status
)) {
1029 // 2.2 Build the rest of HTTP request info.
1031 RequestData
= AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA
));
1032 if (RequestData
== NULL
) {
1033 Status
= EFI_OUT_OF_RESOURCES
;
1036 RequestData
->Method
= HeaderOnly
? HttpMethodHead
: HttpMethodGet
;
1037 RequestData
->Url
= Url
;
1040 // 2.3 Record the request info in a temp cache item.
1042 if (Cache
!= NULL
) {
1043 Cache
->RequestData
= RequestData
;
1047 // 2.4 Send out the request to HTTP server.
1049 HttpIo
= &Private
->HttpIo
;
1050 Status
= HttpIoSendRequest (
1053 HttpIoHeader
->HeaderCount
,
1054 HttpIoHeader
->Headers
,
1058 if (EFI_ERROR (Status
)) {
1063 // 3. Receive HTTP response message.
1067 // 3.1 First step, use zero BodyLength to only receive the response headers.
1069 ResponseData
= AllocateZeroPool (sizeof(HTTP_IO_RESPONSE_DATA
));
1070 if (ResponseData
== NULL
) {
1071 Status
= EFI_OUT_OF_RESOURCES
;
1074 Status
= HttpIoRecvResponse (
1079 if (EFI_ERROR (Status
) || EFI_ERROR (ResponseData
->Status
)) {
1080 if (EFI_ERROR (ResponseData
->Status
)) {
1081 StatusCode
= HttpIo
->RspToken
.Message
->Data
.Response
->StatusCode
;
1082 HttpBootPrintErrorMessage (StatusCode
);
1083 Status
= ResponseData
->Status
;
1089 // Check the image type according to server's response.
1091 Status
= HttpBootCheckImageType (
1092 Private
->BootFileUri
,
1093 Private
->BootFileUriParser
,
1094 ResponseData
->HeaderCount
,
1095 ResponseData
->Headers
,
1098 if (EFI_ERROR (Status
)) {
1103 // 3.2 Cache the response header.
1105 if (Cache
!= NULL
) {
1106 Cache
->ResponseData
= ResponseData
;
1107 Cache
->ImageType
= *ImageType
;
1111 // 3.3 Init a message-body parser from the header information.
1114 Context
.NewBlock
= FALSE
;
1115 Context
.Block
= NULL
;
1116 Context
.CopyedSize
= 0;
1117 Context
.Buffer
= Buffer
;
1118 Context
.BufferSize
= *BufferSize
;
1119 Context
.Cache
= Cache
;
1120 Context
.Private
= Private
;
1121 Status
= HttpInitMsgParser (
1122 HeaderOnly
? HttpMethodHead
: HttpMethodGet
,
1123 ResponseData
->Response
.StatusCode
,
1124 ResponseData
->HeaderCount
,
1125 ResponseData
->Headers
,
1126 HttpBootGetBootFileCallback
,
1130 if (EFI_ERROR (Status
)) {
1135 // 3.4 Continue to receive and parse message-body if needed.
1140 // 3.4.1, check whether we are in identity transfer-coding.
1143 Status
= HttpGetEntityLength (Parser
, &ContentLength
);
1144 if (!EFI_ERROR (Status
)) {
1145 IdentityMode
= TRUE
;
1147 IdentityMode
= FALSE
;
1151 // 3.4.2, start the message-body download, the identity and chunked transfer-coding
1152 // is handled in different path here.
1154 ZeroMem (&ResponseBody
, sizeof (HTTP_IO_RESPONSE_DATA
));
1157 // In identity transfer-coding there is no need to parse the message body,
1158 // just download the message body to the user provided buffer directly.
1161 while (ReceivedSize
< ContentLength
) {
1162 ResponseBody
.Body
= (CHAR8
*) Buffer
+ ReceivedSize
;
1163 ResponseBody
.BodyLength
= *BufferSize
- ReceivedSize
;
1164 Status
= HttpIoRecvResponse (
1169 if (EFI_ERROR (Status
) || EFI_ERROR (ResponseBody
.Status
)) {
1170 if (EFI_ERROR (ResponseBody
.Status
)) {
1171 Status
= ResponseBody
.Status
;
1175 ReceivedSize
+= ResponseBody
.BodyLength
;
1176 if (Private
->HttpBootCallback
!= NULL
) {
1177 Status
= Private
->HttpBootCallback
->Callback (
1178 Private
->HttpBootCallback
,
1179 HttpBootHttpEntityBody
,
1181 (UINT32
)ResponseBody
.BodyLength
,
1184 if (EFI_ERROR (Status
)) {
1191 // In "chunked" transfer-coding mode, so we need to parse the received
1192 // data to get the real entity content.
1195 while (!HttpIsMessageComplete (Parser
)) {
1197 // Allocate a buffer in Block to hold the message-body.
1198 // If caller provides a buffer, this Block will be reused in every HttpIoRecvResponse().
1199 // Otherwise a buffer, the buffer in Block will be cached and we should allocate a new before
1200 // every HttpIoRecvResponse().
1202 if (Block
== NULL
|| Context
.BufferSize
== 0) {
1203 Block
= AllocatePool (HTTP_BOOT_BLOCK_SIZE
);
1204 if (Block
== NULL
) {
1205 Status
= EFI_OUT_OF_RESOURCES
;
1208 Context
.NewBlock
= TRUE
;
1209 Context
.Block
= Block
;
1211 Context
.NewBlock
= FALSE
;
1214 ResponseBody
.Body
= (CHAR8
*) Block
;
1215 ResponseBody
.BodyLength
= HTTP_BOOT_BLOCK_SIZE
;
1216 Status
= HttpIoRecvResponse (
1221 if (EFI_ERROR (Status
) || EFI_ERROR (ResponseBody
.Status
)) {
1222 if (EFI_ERROR (ResponseBody
.Status
)) {
1223 Status
= ResponseBody
.Status
;
1229 // Parse the new received block of the message-body, the block will be saved in cache.
1231 Status
= HttpParseMessageBody (
1233 ResponseBody
.BodyLength
,
1236 if (EFI_ERROR (Status
)) {
1244 // 3.5 Message-body receive & parse is completed, we should be able to get the file size now.
1246 Status
= HttpGetEntityLength (Parser
, &ContentLength
);
1247 if (EFI_ERROR (Status
)) {
1251 if (*BufferSize
< ContentLength
) {
1252 Status
= EFI_BUFFER_TOO_SMALL
;
1254 Status
= EFI_SUCCESS
;
1256 *BufferSize
= ContentLength
;
1259 // 4. Save the cache item to driver's cache list and return.
1261 if (Cache
!= NULL
) {
1262 Cache
->EntityLength
= ContentLength
;
1263 InsertTailList (&Private
->CacheList
, &Cache
->Link
);
1266 if (Parser
!= NULL
) {
1267 HttpFreeMsgParser (Parser
);
1273 if (Parser
!= NULL
) {
1274 HttpFreeMsgParser (Parser
);
1276 if (Context
.Block
!= NULL
) {
1277 FreePool (Context
.Block
);
1279 HttpBootFreeCache (Cache
);
1282 if (ResponseData
!= NULL
) {
1283 FreePool (ResponseData
);
1286 if (RequestData
!= NULL
) {
1287 FreePool (RequestData
);
1290 HttpBootFreeHeader (HttpIoHeader
);
1292 if (Cache
!= NULL
) {