2 Implementation of the boot file download function.
4 Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include "HttpBootDxe.h"
13 Update the device path node to include the boot resource information.
15 @param[in] Private The pointer to the driver's private data.
17 @retval EFI_SUCCESS Device patch successfully updated.
18 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
19 @retval Others Unexpected error happened.
23 HttpBootUpdateDevicePath (
24 IN HTTP_BOOT_PRIVATE_DATA
*Private
28 EFI_DEVICE_PATH_PROTOCOL
*TmpIpDevicePath
;
29 EFI_DEVICE_PATH_PROTOCOL
*TmpDnsDevicePath
;
30 EFI_DEVICE_PATH_PROTOCOL
*NewDevicePath
;
34 TmpIpDevicePath
= NULL
;
35 TmpDnsDevicePath
= NULL
;
38 // Update the IP node with DHCP assigned information.
40 if (!Private
->UsingIpv6
) {
41 Node
= AllocateZeroPool (sizeof (IPv4_DEVICE_PATH
));
43 return EFI_OUT_OF_RESOURCES
;
45 Node
->Ipv4
.Header
.Type
= MESSAGING_DEVICE_PATH
;
46 Node
->Ipv4
.Header
.SubType
= MSG_IPv4_DP
;
47 SetDevicePathNodeLength (Node
, sizeof (IPv4_DEVICE_PATH
));
48 CopyMem (&Node
->Ipv4
.LocalIpAddress
, &Private
->StationIp
, sizeof (EFI_IPv4_ADDRESS
));
49 Node
->Ipv4
.RemotePort
= Private
->Port
;
50 Node
->Ipv4
.Protocol
= EFI_IP_PROTO_TCP
;
51 Node
->Ipv4
.StaticIpAddress
= FALSE
;
52 CopyMem (&Node
->Ipv4
.GatewayIpAddress
, &Private
->GatewayIp
, sizeof (EFI_IPv4_ADDRESS
));
53 CopyMem (&Node
->Ipv4
.SubnetMask
, &Private
->SubnetMask
, sizeof (EFI_IPv4_ADDRESS
));
55 Node
= AllocateZeroPool (sizeof (IPv6_DEVICE_PATH
));
57 return EFI_OUT_OF_RESOURCES
;
59 Node
->Ipv6
.Header
.Type
= MESSAGING_DEVICE_PATH
;
60 Node
->Ipv6
.Header
.SubType
= MSG_IPv6_DP
;
61 SetDevicePathNodeLength (Node
, sizeof (IPv6_DEVICE_PATH
));
62 Node
->Ipv6
.PrefixLength
= IP6_PREFIX_LENGTH
;
63 Node
->Ipv6
.RemotePort
= Private
->Port
;
64 Node
->Ipv6
.Protocol
= EFI_IP_PROTO_TCP
;
65 Node
->Ipv6
.IpAddressOrigin
= 0;
66 CopyMem (&Node
->Ipv6
.LocalIpAddress
, &Private
->StationIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
67 CopyMem (&Node
->Ipv6
.RemoteIpAddress
, &Private
->ServerIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
68 CopyMem (&Node
->Ipv6
.GatewayIpAddress
, &Private
->GatewayIp
.v6
, sizeof (EFI_IPv6_ADDRESS
));
71 TmpIpDevicePath
= AppendDevicePathNode (Private
->ParentDevicePath
, (EFI_DEVICE_PATH_PROTOCOL
*) Node
);
73 if (TmpIpDevicePath
== NULL
) {
74 return EFI_OUT_OF_RESOURCES
;
78 // Update the DNS node with DNS server IP list if existed.
80 if (Private
->DnsServerIp
!= NULL
) {
81 Length
= sizeof (EFI_DEVICE_PATH_PROTOCOL
) + sizeof (Node
->Dns
.IsIPv6
) + Private
->DnsServerCount
* sizeof (EFI_IP_ADDRESS
);
82 Node
= AllocatePool (Length
);
84 FreePool (TmpIpDevicePath
);
85 return EFI_OUT_OF_RESOURCES
;
87 Node
->DevPath
.Type
= MESSAGING_DEVICE_PATH
;
88 Node
->DevPath
.SubType
= MSG_DNS_DP
;
89 SetDevicePathNodeLength (Node
, Length
);
90 Node
->Dns
.IsIPv6
= Private
->UsingIpv6
? 0x01 : 0x00;
91 CopyMem ((UINT8
*) Node
+ sizeof (EFI_DEVICE_PATH_PROTOCOL
) + sizeof (Node
->Dns
.IsIPv6
), Private
->DnsServerIp
, Private
->DnsServerCount
* sizeof (EFI_IP_ADDRESS
));
93 TmpDnsDevicePath
= AppendDevicePathNode (TmpIpDevicePath
, (EFI_DEVICE_PATH_PROTOCOL
*) Node
);
95 FreePool (TmpIpDevicePath
);
96 TmpIpDevicePath
= NULL
;
97 if (TmpDnsDevicePath
== NULL
) {
98 return EFI_OUT_OF_RESOURCES
;
103 // Update the URI node with the boot file URI.
105 Length
= sizeof (EFI_DEVICE_PATH_PROTOCOL
) + AsciiStrSize (Private
->BootFileUri
);
106 Node
= AllocatePool (Length
);
108 if (TmpIpDevicePath
!= NULL
) {
109 FreePool (TmpIpDevicePath
);
111 if (TmpDnsDevicePath
!= NULL
) {
112 FreePool (TmpDnsDevicePath
);
114 return EFI_OUT_OF_RESOURCES
;
116 Node
->DevPath
.Type
= MESSAGING_DEVICE_PATH
;
117 Node
->DevPath
.SubType
= MSG_URI_DP
;
118 SetDevicePathNodeLength (Node
, Length
);
119 CopyMem ((UINT8
*) Node
+ sizeof (EFI_DEVICE_PATH_PROTOCOL
), Private
->BootFileUri
, AsciiStrSize (Private
->BootFileUri
));
121 if (TmpDnsDevicePath
!= NULL
) {
122 NewDevicePath
= AppendDevicePathNode (TmpDnsDevicePath
, (EFI_DEVICE_PATH_PROTOCOL
*) Node
);
123 FreePool (TmpDnsDevicePath
);
125 ASSERT (TmpIpDevicePath
!= NULL
);
126 NewDevicePath
= AppendDevicePathNode (TmpIpDevicePath
, (EFI_DEVICE_PATH_PROTOCOL
*) Node
);
127 FreePool (TmpIpDevicePath
);
130 if (NewDevicePath
== NULL
) {
131 return EFI_OUT_OF_RESOURCES
;
134 if (!Private
->UsingIpv6
) {
136 // Reinstall the device path protocol of the child handle.
138 Status
= gBS
->ReinstallProtocolInterface (
139 Private
->Ip4Nic
->Controller
,
140 &gEfiDevicePathProtocolGuid
,
141 Private
->Ip4Nic
->DevicePath
,
144 if (EFI_ERROR (Status
)) {
148 FreePool (Private
->Ip4Nic
->DevicePath
);
149 Private
->Ip4Nic
->DevicePath
= NewDevicePath
;
152 // Reinstall the device path protocol of the child handle.
154 Status
= gBS
->ReinstallProtocolInterface (
155 Private
->Ip6Nic
->Controller
,
156 &gEfiDevicePathProtocolGuid
,
157 Private
->Ip6Nic
->DevicePath
,
160 if (EFI_ERROR (Status
)) {
163 FreePool (Private
->Ip6Nic
->DevicePath
);
164 Private
->Ip6Nic
->DevicePath
= NewDevicePath
;
171 Parse the boot file URI information from the selected Dhcp4 offer packet.
173 @param[in] Private The pointer to the driver's private data.
175 @retval EFI_SUCCESS Successfully parsed out all the boot information.
176 @retval Others Failed to parse out the boot information.
180 HttpBootDhcp4ExtractUriInfo (
181 IN HTTP_BOOT_PRIVATE_DATA
*Private
184 HTTP_BOOT_DHCP4_PACKET_CACHE
*SelectOffer
;
185 HTTP_BOOT_DHCP4_PACKET_CACHE
*HttpOffer
;
188 UINT32 DnsServerIndex
;
189 EFI_DHCP4_PACKET_OPTION
*Option
;
192 ASSERT (Private
!= NULL
);
193 ASSERT (Private
->SelectIndex
!= 0);
194 SelectIndex
= Private
->SelectIndex
- 1;
195 ASSERT (SelectIndex
< HTTP_BOOT_OFFER_MAX_NUM
);
199 Status
= EFI_SUCCESS
;
202 // SelectOffer contains the IP address configuration and name server configuration.
203 // HttpOffer contains the boot file URL.
205 SelectOffer
= &Private
->OfferBuffer
[SelectIndex
].Dhcp4
;
206 if (Private
->FilePathUri
== NULL
) {
208 // In Corporate environment, we need a HttpOffer.
210 if ((SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUri
) ||
211 (SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUriDns
) ||
212 (SelectOffer
->OfferType
== HttpOfferTypeDhcpNameUriDns
)) {
213 HttpOffer
= SelectOffer
;
215 ASSERT (Private
->SelectProxyType
!= HttpOfferTypeMax
);
216 ProxyIndex
= Private
->OfferIndex
[Private
->SelectProxyType
][0];
217 HttpOffer
= &Private
->OfferBuffer
[ProxyIndex
].Dhcp4
;
219 Private
->BootFileUriParser
= HttpOffer
->UriParser
;
220 Private
->BootFileUri
= (CHAR8
*) HttpOffer
->OptList
[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE
]->Data
;
223 // In Home environment the BootFileUri comes from the FilePath.
225 Private
->BootFileUriParser
= Private
->FilePathUriParser
;
226 Private
->BootFileUri
= Private
->FilePathUri
;
230 // Check the URI scheme.
232 Status
= HttpBootCheckUriScheme (Private
->BootFileUri
);
233 if (EFI_ERROR (Status
)) {
234 DEBUG ((DEBUG_ERROR
, "HttpBootDhcp4ExtractUriInfo: %r.\n", Status
));
235 if (Status
== EFI_INVALID_PARAMETER
) {
236 AsciiPrint ("\n Error: Invalid URI address.\n");
237 } else if (Status
== EFI_ACCESS_DENIED
) {
238 AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n");
243 if ((SelectOffer
->OfferType
== HttpOfferTypeDhcpNameUriDns
) ||
244 (SelectOffer
->OfferType
== HttpOfferTypeDhcpDns
) ||
245 (SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUriDns
)) {
246 Option
= SelectOffer
->OptList
[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER
];
247 ASSERT (Option
!= NULL
);
250 // Record the Dns Server address list.
252 Private
->DnsServerCount
= (Option
->Length
) / sizeof (EFI_IPv4_ADDRESS
);
254 Private
->DnsServerIp
= AllocateZeroPool (Private
->DnsServerCount
* sizeof (EFI_IP_ADDRESS
));
255 if (Private
->DnsServerIp
== NULL
) {
256 return EFI_OUT_OF_RESOURCES
;
259 for (DnsServerIndex
= 0; DnsServerIndex
< Private
->DnsServerCount
; DnsServerIndex
++) {
260 CopyMem (&(Private
->DnsServerIp
[DnsServerIndex
].v4
), &(((EFI_IPv4_ADDRESS
*) Option
->Data
)[DnsServerIndex
]), sizeof (EFI_IPv4_ADDRESS
));
264 // Configure the default DNS server if server assigned.
266 Status
= HttpBootRegisterIp4Dns (
271 if (EFI_ERROR (Status
)) {
272 FreePool (Private
->DnsServerIp
);
273 Private
->DnsServerIp
= NULL
;
279 // Extract the port from URL, and use default HTTP port 80 if not provided.
281 Status
= HttpUrlGetPort (
282 Private
->BootFileUri
,
283 Private
->BootFileUriParser
,
286 if (EFI_ERROR (Status
) || Private
->Port
== 0) {
291 // All boot informations are valid here.
295 // Update the device path to include the boot resource information.
297 Status
= HttpBootUpdateDevicePath (Private
);
298 if (EFI_ERROR (Status
) && Private
->DnsServerIp
!= NULL
) {
299 FreePool (Private
->DnsServerIp
);
300 Private
->DnsServerIp
= NULL
;
307 Parse the boot file URI information from the selected Dhcp6 offer packet.
309 @param[in] Private The pointer to the driver's private data.
311 @retval EFI_SUCCESS Successfully parsed out all the boot information.
312 @retval Others Failed to parse out the boot information.
316 HttpBootDhcp6ExtractUriInfo (
317 IN HTTP_BOOT_PRIVATE_DATA
*Private
320 HTTP_BOOT_DHCP6_PACKET_CACHE
*SelectOffer
;
321 HTTP_BOOT_DHCP6_PACKET_CACHE
*HttpOffer
;
324 UINT32 DnsServerIndex
;
325 EFI_DHCP6_PACKET_OPTION
*Option
;
326 EFI_IPv6_ADDRESS IpAddr
;
332 ASSERT (Private
!= NULL
);
333 ASSERT (Private
->SelectIndex
!= 0);
334 SelectIndex
= Private
->SelectIndex
- 1;
335 ASSERT (SelectIndex
< HTTP_BOOT_OFFER_MAX_NUM
);
339 Status
= EFI_SUCCESS
;
342 // SelectOffer contains the IP address configuration and name server configuration.
343 // HttpOffer contains the boot file URL.
345 SelectOffer
= &Private
->OfferBuffer
[SelectIndex
].Dhcp6
;
346 if (Private
->FilePathUri
== NULL
) {
348 // In Corporate environment, we need a HttpOffer.
350 if ((SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUri
) ||
351 (SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUriDns
) ||
352 (SelectOffer
->OfferType
== HttpOfferTypeDhcpNameUriDns
)) {
353 HttpOffer
= SelectOffer
;
355 ASSERT (Private
->SelectProxyType
!= HttpOfferTypeMax
);
356 ProxyIndex
= Private
->OfferIndex
[Private
->SelectProxyType
][0];
357 HttpOffer
= &Private
->OfferBuffer
[ProxyIndex
].Dhcp6
;
359 Private
->BootFileUriParser
= HttpOffer
->UriParser
;
360 Private
->BootFileUri
= (CHAR8
*) HttpOffer
->OptList
[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL
]->Data
;
363 // In Home environment the BootFileUri comes from the FilePath.
365 Private
->BootFileUriParser
= Private
->FilePathUriParser
;
366 Private
->BootFileUri
= Private
->FilePathUri
;
370 // Check the URI scheme.
372 Status
= HttpBootCheckUriScheme (Private
->BootFileUri
);
373 if (EFI_ERROR (Status
)) {
374 DEBUG ((DEBUG_ERROR
, "HttpBootDhcp6ExtractUriInfo: %r.\n", Status
));
375 if (Status
== EFI_INVALID_PARAMETER
) {
376 AsciiPrint ("\n Error: Invalid URI address.\n");
377 } else if (Status
== EFI_ACCESS_DENIED
) {
378 AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n");
384 // Set the Local station address to IP layer.
386 Status
= HttpBootSetIp6Address (Private
);
387 if (EFI_ERROR (Status
)) {
392 // Register the IPv6 gateway address to the network device.
394 Status
= HttpBootSetIp6Gateway (Private
);
395 if (EFI_ERROR (Status
)) {
399 if ((SelectOffer
->OfferType
== HttpOfferTypeDhcpNameUriDns
) ||
400 (SelectOffer
->OfferType
== HttpOfferTypeDhcpDns
) ||
401 (SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUriDns
)) {
402 Option
= SelectOffer
->OptList
[HTTP_BOOT_DHCP6_IDX_DNS_SERVER
];
403 ASSERT (Option
!= NULL
);
406 // Record the Dns Server address list.
408 Private
->DnsServerCount
= HTONS (Option
->OpLen
) / sizeof (EFI_IPv6_ADDRESS
);
410 Private
->DnsServerIp
= AllocateZeroPool (Private
->DnsServerCount
* sizeof (EFI_IP_ADDRESS
));
411 if (Private
->DnsServerIp
== NULL
) {
412 return EFI_OUT_OF_RESOURCES
;
415 for (DnsServerIndex
= 0; DnsServerIndex
< Private
->DnsServerCount
; DnsServerIndex
++) {
416 CopyMem (&(Private
->DnsServerIp
[DnsServerIndex
].v6
), &(((EFI_IPv6_ADDRESS
*) Option
->Data
)[DnsServerIndex
]), sizeof (EFI_IPv6_ADDRESS
));
420 // Configure the default DNS server if server assigned.
422 Status
= HttpBootSetIp6Dns (
424 HTONS (Option
->OpLen
),
427 if (EFI_ERROR (Status
)) {
433 // Extract the HTTP server Ip from URL. This is used to Check route table
434 // whether can send message to HTTP Server Ip through the GateWay.
436 Status
= HttpUrlGetIp6 (
437 Private
->BootFileUri
,
438 Private
->BootFileUriParser
,
442 if (EFI_ERROR (Status
)) {
444 // The Http server address is expressed by Name Ip, so perform DNS resolution
446 Status
= HttpUrlGetHostName (
447 Private
->BootFileUri
,
448 Private
->BootFileUriParser
,
451 if (EFI_ERROR (Status
)) {
455 HostNameSize
= AsciiStrSize (HostName
);
456 HostNameStr
= AllocateZeroPool (HostNameSize
* sizeof (CHAR16
));
457 if (HostNameStr
== NULL
) {
458 Status
= EFI_OUT_OF_RESOURCES
;
462 AsciiStrToUnicodeStrS (HostName
, HostNameStr
, HostNameSize
);
464 if (HostName
!= NULL
) {
468 Status
= HttpBootDns (Private
, HostNameStr
, &IpAddr
);
469 FreePool (HostNameStr
);
470 if (EFI_ERROR (Status
)) {
471 AsciiPrint ("\n Error: Could not retrieve the host address from DNS server.\n");
476 CopyMem (&Private
->ServerIp
.v6
, &IpAddr
, sizeof (EFI_IPv6_ADDRESS
));
479 // Extract the port from URL, and use default HTTP port 80 if not provided.
481 Status
= HttpUrlGetPort (
482 Private
->BootFileUri
,
483 Private
->BootFileUriParser
,
486 if (EFI_ERROR (Status
) || Private
->Port
== 0) {
491 // All boot informations are valid here.
495 // Update the device path to include the boot resource information.
497 Status
= HttpBootUpdateDevicePath (Private
);
498 if (EFI_ERROR (Status
)) {
505 if (Private
->DnsServerIp
!= NULL
) {
506 FreePool (Private
->DnsServerIp
);
507 Private
->DnsServerIp
= NULL
;
515 Discover all the boot information for boot file.
517 @param[in, out] Private The pointer to the driver's private data.
519 @retval EFI_SUCCESS Successfully obtained all the boot information .
520 @retval Others Failed to retrieve the boot information.
524 HttpBootDiscoverBootInfo (
525 IN OUT HTTP_BOOT_PRIVATE_DATA
*Private
531 // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
532 // other Http boot information.
534 Status
= HttpBootDhcp (Private
);
535 if (EFI_ERROR (Status
)) {
539 if (!Private
->UsingIpv6
) {
540 Status
= HttpBootDhcp4ExtractUriInfo (Private
);
542 Status
= HttpBootDhcp6ExtractUriInfo (Private
);
549 HttpIo Callback function which will be invoked when specified HTTP_IO_CALLBACK_EVENT happened.
551 @param[in] EventType Indicate the Event type that occurs in the current callback.
552 @param[in] Message HTTP message which will be send to, or just received from HTTP server.
553 @param[in] Context The Callback Context pointer.
555 @retval EFI_SUCCESS Tells the HttpIo to continue the HTTP process.
556 @retval Others Tells the HttpIo to abort the current HTTP process.
560 HttpBootHttpIoCallback (
561 IN HTTP_IO_CALLBACK_EVENT EventType
,
562 IN EFI_HTTP_MESSAGE
*Message
,
566 HTTP_BOOT_PRIVATE_DATA
*Private
;
568 Private
= (HTTP_BOOT_PRIVATE_DATA
*) Context
;
569 if (Private
->HttpBootCallback
!= NULL
) {
570 Status
= Private
->HttpBootCallback
->Callback (
571 Private
->HttpBootCallback
,
572 EventType
== HttpIoRequest
? HttpBootHttpRequest
: HttpBootHttpResponse
,
573 EventType
== HttpIoRequest
? FALSE
: TRUE
,
574 sizeof (EFI_HTTP_MESSAGE
),
583 Create a HttpIo instance for the file download.
585 @param[in] Private The pointer to the driver's private data.
587 @retval EFI_SUCCESS Successfully created.
588 @retval Others Failed to create HttpIo.
592 HttpBootCreateHttpIo (
593 IN HTTP_BOOT_PRIVATE_DATA
*Private
596 HTTP_IO_CONFIG_DATA ConfigData
;
598 EFI_HANDLE ImageHandle
;
601 ASSERT (Private
!= NULL
);
604 // Get HTTP timeout value
606 TimeoutValue
= PcdGet32 (PcdHttpIoTimeout
);
608 ZeroMem (&ConfigData
, sizeof (HTTP_IO_CONFIG_DATA
));
609 if (!Private
->UsingIpv6
) {
610 ConfigData
.Config4
.HttpVersion
= HttpVersion11
;
611 ConfigData
.Config4
.RequestTimeOut
= TimeoutValue
;
612 IP4_COPY_ADDRESS (&ConfigData
.Config4
.LocalIp
, &Private
->StationIp
.v4
);
613 IP4_COPY_ADDRESS (&ConfigData
.Config4
.SubnetMask
, &Private
->SubnetMask
.v4
);
614 ImageHandle
= Private
->Ip4Nic
->ImageHandle
;
616 ConfigData
.Config6
.HttpVersion
= HttpVersion11
;
617 ConfigData
.Config6
.RequestTimeOut
= TimeoutValue
;
618 IP6_COPY_ADDRESS (&ConfigData
.Config6
.LocalIp
, &Private
->StationIp
.v6
);
619 ImageHandle
= Private
->Ip6Nic
->ImageHandle
;
622 Status
= HttpIoCreateIo (
625 Private
->UsingIpv6
? IP_VERSION_6
: IP_VERSION_4
,
627 HttpBootHttpIoCallback
,
631 if (EFI_ERROR (Status
)) {
635 Private
->HttpCreated
= TRUE
;
640 Release all the resource of a cache item.
642 @param[in] Cache The pointer to the cache item.
647 IN HTTP_BOOT_CACHE_CONTENT
*Cache
652 LIST_ENTRY
*NextEntry
;
653 HTTP_BOOT_ENTITY_DATA
*EntityData
;
657 // Free the request data
659 if (Cache
->RequestData
!= NULL
) {
660 if (Cache
->RequestData
->Url
!= NULL
) {
661 FreePool (Cache
->RequestData
->Url
);
663 FreePool (Cache
->RequestData
);
667 // Free the response header
669 if (Cache
->ResponseData
!= NULL
) {
670 if (Cache
->ResponseData
->Headers
!= NULL
) {
671 for (Index
= 0; Index
< Cache
->ResponseData
->HeaderCount
; Index
++) {
672 FreePool (Cache
->ResponseData
->Headers
[Index
].FieldName
);
673 FreePool (Cache
->ResponseData
->Headers
[Index
].FieldValue
);
675 FreePool (Cache
->ResponseData
->Headers
);
680 // Free the response body
682 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Cache
->EntityDataList
) {
683 EntityData
= NET_LIST_USER_STRUCT (Entry
, HTTP_BOOT_ENTITY_DATA
, Link
);
684 if (EntityData
->Block
!= NULL
) {
685 FreePool (EntityData
->Block
);
687 RemoveEntryList (&EntityData
->Link
);
688 FreePool (EntityData
);
696 Clean up all cached data.
698 @param[in] Private The pointer to the driver's private data.
702 HttpBootFreeCacheList (
703 IN HTTP_BOOT_PRIVATE_DATA
*Private
707 LIST_ENTRY
*NextEntry
;
708 HTTP_BOOT_CACHE_CONTENT
*Cache
;
710 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Private
->CacheList
) {
711 Cache
= NET_LIST_USER_STRUCT (Entry
, HTTP_BOOT_CACHE_CONTENT
, Link
);
712 RemoveEntryList (&Cache
->Link
);
713 HttpBootFreeCache (Cache
);
718 Get the file content from cached data.
720 @param[in] Private The pointer to the driver's private data.
721 @param[in] Uri Uri of the file to be retrieved from cache.
722 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
723 code of EFI_SUCCESS, the amount of data transferred to
724 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
725 the size of Buffer required to retrieve the requested file.
726 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
727 then the size of the requested file is returned in
729 @param[out] ImageType The image type of the downloaded file.
731 @retval EFI_SUCCESS Successfully created.
732 @retval Others Failed to create HttpIo.
736 HttpBootGetFileFromCache (
737 IN HTTP_BOOT_PRIVATE_DATA
*Private
,
739 IN OUT UINTN
*BufferSize
,
741 OUT HTTP_BOOT_IMAGE_TYPE
*ImageType
746 HTTP_BOOT_CACHE_CONTENT
*Cache
;
747 HTTP_BOOT_ENTITY_DATA
*EntityData
;
750 if (Uri
== NULL
|| BufferSize
== NULL
|| Buffer
== NULL
|| ImageType
== NULL
) {
751 return EFI_INVALID_PARAMETER
;
754 NET_LIST_FOR_EACH (Entry
, &Private
->CacheList
) {
755 Cache
= NET_LIST_USER_STRUCT (Entry
, HTTP_BOOT_CACHE_CONTENT
, Link
);
757 // Compare the URI to see whether we already have a cache for this file.
759 if ((Cache
->RequestData
!= NULL
) &&
760 (Cache
->RequestData
->Url
!= NULL
) &&
761 (StrCmp (Uri
, Cache
->RequestData
->Url
) == 0)) {
763 // Hit in cache, record image type.
765 *ImageType
= Cache
->ImageType
;
768 // Check buffer size.
770 if (*BufferSize
< Cache
->EntityLength
) {
771 *BufferSize
= Cache
->EntityLength
;
772 return EFI_BUFFER_TOO_SMALL
;
776 // Fill data to buffer.
779 NET_LIST_FOR_EACH (Entry2
, &Cache
->EntityDataList
) {
780 EntityData
= NET_LIST_USER_STRUCT (Entry2
, HTTP_BOOT_ENTITY_DATA
, Link
);
781 if (*BufferSize
> CopyedSize
) {
784 EntityData
->DataStart
,
785 MIN (EntityData
->DataLength
, *BufferSize
- CopyedSize
)
787 CopyedSize
+= MIN (EntityData
->DataLength
, *BufferSize
- CopyedSize
);
790 *BufferSize
= CopyedSize
;
795 return EFI_NOT_FOUND
;
799 A callback function to intercept events during message parser.
801 This function will be invoked during HttpParseMessageBody() with various events type. An error
802 return status of the callback function will cause the HttpParseMessageBody() aborted.
804 @param[in] EventType Event type of this callback call.
805 @param[in] Data A pointer to data buffer.
806 @param[in] Length Length in bytes of the Data.
807 @param[in] Context Callback context set by HttpInitMsgParser().
809 @retval EFI_SUCCESS Continue to parser the message body.
810 @retval Others Abort the parse.
815 HttpBootGetBootFileCallback (
816 IN HTTP_BODY_PARSE_EVENT EventType
,
822 HTTP_BOOT_CALLBACK_DATA
*CallbackData
;
823 HTTP_BOOT_ENTITY_DATA
*NewEntityData
;
825 EFI_HTTP_BOOT_CALLBACK_PROTOCOL
*HttpBootCallback
;
828 // We only care about the entity data.
830 if (EventType
!= BodyParseEventOnData
) {
834 CallbackData
= (HTTP_BOOT_CALLBACK_DATA
*) Context
;
835 HttpBootCallback
= CallbackData
->Private
->HttpBootCallback
;
836 if (HttpBootCallback
!= NULL
) {
837 Status
= HttpBootCallback
->Callback (
839 HttpBootHttpEntityBody
,
844 if (EFI_ERROR (Status
)) {
849 // Copy data if caller has provided a buffer.
851 if (CallbackData
->BufferSize
> CallbackData
->CopyedSize
) {
853 CallbackData
->Buffer
+ CallbackData
->CopyedSize
,
855 MIN (Length
, CallbackData
->BufferSize
- CallbackData
->CopyedSize
)
857 CallbackData
->CopyedSize
+= MIN (Length
, CallbackData
->BufferSize
- CallbackData
->CopyedSize
);
861 // The caller doesn't provide a buffer, save the block into cache list.
863 if (CallbackData
->Cache
!= NULL
) {
864 NewEntityData
= AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA
));
865 if (NewEntityData
== NULL
) {
866 return EFI_OUT_OF_RESOURCES
;
868 if (CallbackData
->NewBlock
) {
869 NewEntityData
->Block
= CallbackData
->Block
;
870 CallbackData
->Block
= NULL
;
872 NewEntityData
->DataLength
= Length
;
873 NewEntityData
->DataStart
= (UINT8
*) Data
;
874 InsertTailList (&CallbackData
->Cache
->EntityDataList
, &NewEntityData
->Link
);
880 This function download the boot file by using UEFI HTTP protocol.
882 @param[in] Private The pointer to the driver's private data.
883 @param[in] HeaderOnly Only request the response header, it could save a lot of time if
884 the caller only want to know the size of the requested file.
885 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
886 code of EFI_SUCCESS, the amount of data transferred to
887 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
888 the size of Buffer required to retrieve the requested file.
889 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
890 then the size of the requested file is returned in
892 @param[out] ImageType The image type of the downloaded file.
894 @retval EFI_SUCCESS The file was loaded.
895 @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.
896 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
897 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
898 BufferSize has been updated with the size needed to complete
900 @retval Others Unexpected error happened.
904 HttpBootGetBootFile (
905 IN HTTP_BOOT_PRIVATE_DATA
*Private
,
906 IN BOOLEAN HeaderOnly
,
907 IN OUT UINTN
*BufferSize
,
909 OUT HTTP_BOOT_IMAGE_TYPE
*ImageType
913 EFI_HTTP_STATUS_CODE StatusCode
;
915 EFI_HTTP_REQUEST_DATA
*RequestData
;
916 HTTP_IO_RESPONSE_DATA
*ResponseData
;
917 HTTP_IO_RESPONSE_DATA ResponseBody
;
919 HTTP_IO_HEADER
*HttpIoHeader
;
921 HTTP_BOOT_CALLBACK_DATA Context
;
923 HTTP_BOOT_CACHE_CONTENT
*Cache
;
927 BOOLEAN IdentityMode
;
930 ASSERT (Private
!= NULL
);
931 ASSERT (Private
->HttpCreated
);
933 if (BufferSize
== NULL
|| ImageType
== NULL
) {
934 return EFI_INVALID_PARAMETER
;
937 if (*BufferSize
!= 0 && Buffer
== NULL
) {
938 return EFI_INVALID_PARAMETER
;
942 // First, check whether we already cached the requested Uri.
944 UrlSize
= AsciiStrSize (Private
->BootFileUri
);
945 Url
= AllocatePool (UrlSize
* sizeof (CHAR16
));
947 return EFI_OUT_OF_RESOURCES
;
949 AsciiStrToUnicodeStrS (Private
->BootFileUri
, Url
, UrlSize
);
950 if (!HeaderOnly
&& Buffer
!= NULL
) {
951 Status
= HttpBootGetFileFromCache (Private
, Url
, BufferSize
, Buffer
, ImageType
);
952 if (Status
!= EFI_NOT_FOUND
) {
959 // Not found in cache, try to download it through HTTP.
963 // 1. Create a temp cache item for the requested URI if caller doesn't provide buffer.
966 if ((!HeaderOnly
) && (*BufferSize
== 0)) {
967 Cache
= AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT
));
969 Status
= EFI_OUT_OF_RESOURCES
;
972 Cache
->ImageType
= ImageTypeMax
;
973 InitializeListHead (&Cache
->EntityDataList
);
977 // 2. Send HTTP request message.
981 // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:
986 HttpIoHeader
= HttpIoCreateHeader (3);
987 if (HttpIoHeader
== NULL
) {
988 Status
= EFI_OUT_OF_RESOURCES
;
993 // Add HTTP header field 1: Host
996 Status
= HttpUrlGetHostName (
997 Private
->BootFileUri
,
998 Private
->BootFileUriParser
,
1001 if (EFI_ERROR (Status
)) {
1004 Status
= HttpIoSetHeader (
1009 FreePool (HostName
);
1010 if (EFI_ERROR (Status
)) {
1015 // Add HTTP header field 2: Accept
1017 Status
= HttpIoSetHeader (
1022 if (EFI_ERROR (Status
)) {
1027 // Add HTTP header field 3: User-Agent
1029 Status
= HttpIoSetHeader (
1031 HTTP_HEADER_USER_AGENT
,
1032 HTTP_USER_AGENT_EFI_HTTP_BOOT
1034 if (EFI_ERROR (Status
)) {
1039 // 2.2 Build the rest of HTTP request info.
1041 RequestData
= AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA
));
1042 if (RequestData
== NULL
) {
1043 Status
= EFI_OUT_OF_RESOURCES
;
1046 RequestData
->Method
= HeaderOnly
? HttpMethodHead
: HttpMethodGet
;
1047 RequestData
->Url
= Url
;
1050 // 2.3 Record the request info in a temp cache item.
1052 if (Cache
!= NULL
) {
1053 Cache
->RequestData
= RequestData
;
1057 // 2.4 Send out the request to HTTP server.
1059 HttpIo
= &Private
->HttpIo
;
1060 Status
= HttpIoSendRequest (
1063 HttpIoHeader
->HeaderCount
,
1064 HttpIoHeader
->Headers
,
1068 if (EFI_ERROR (Status
)) {
1073 // 3. Receive HTTP response message.
1077 // 3.1 First step, use zero BodyLength to only receive the response headers.
1079 ResponseData
= AllocateZeroPool (sizeof(HTTP_IO_RESPONSE_DATA
));
1080 if (ResponseData
== NULL
) {
1081 Status
= EFI_OUT_OF_RESOURCES
;
1084 Status
= HttpIoRecvResponse (
1089 if (EFI_ERROR (Status
) || EFI_ERROR (ResponseData
->Status
)) {
1090 if (EFI_ERROR (ResponseData
->Status
)) {
1091 StatusCode
= HttpIo
->RspToken
.Message
->Data
.Response
->StatusCode
;
1092 HttpBootPrintErrorMessage (StatusCode
);
1093 Status
= ResponseData
->Status
;
1099 // Check the image type according to server's response.
1101 Status
= HttpBootCheckImageType (
1102 Private
->BootFileUri
,
1103 Private
->BootFileUriParser
,
1104 ResponseData
->HeaderCount
,
1105 ResponseData
->Headers
,
1108 if (EFI_ERROR (Status
)) {
1113 // 3.2 Cache the response header.
1115 if (Cache
!= NULL
) {
1116 Cache
->ResponseData
= ResponseData
;
1117 Cache
->ImageType
= *ImageType
;
1121 // 3.3 Init a message-body parser from the header information.
1124 Context
.NewBlock
= FALSE
;
1125 Context
.Block
= NULL
;
1126 Context
.CopyedSize
= 0;
1127 Context
.Buffer
= Buffer
;
1128 Context
.BufferSize
= *BufferSize
;
1129 Context
.Cache
= Cache
;
1130 Context
.Private
= Private
;
1131 Status
= HttpInitMsgParser (
1132 HeaderOnly
? HttpMethodHead
: HttpMethodGet
,
1133 ResponseData
->Response
.StatusCode
,
1134 ResponseData
->HeaderCount
,
1135 ResponseData
->Headers
,
1136 HttpBootGetBootFileCallback
,
1140 if (EFI_ERROR (Status
)) {
1145 // 3.4 Continue to receive and parse message-body if needed.
1150 // 3.4.1, check whether we are in identity transfer-coding.
1153 Status
= HttpGetEntityLength (Parser
, &ContentLength
);
1154 if (!EFI_ERROR (Status
)) {
1155 IdentityMode
= TRUE
;
1157 IdentityMode
= FALSE
;
1161 // 3.4.2, start the message-body download, the identity and chunked transfer-coding
1162 // is handled in different path here.
1164 ZeroMem (&ResponseBody
, sizeof (HTTP_IO_RESPONSE_DATA
));
1167 // In identity transfer-coding there is no need to parse the message body,
1168 // just download the message body to the user provided buffer directly.
1171 while (ReceivedSize
< ContentLength
) {
1172 ResponseBody
.Body
= (CHAR8
*) Buffer
+ ReceivedSize
;
1173 ResponseBody
.BodyLength
= *BufferSize
- ReceivedSize
;
1174 Status
= HttpIoRecvResponse (
1179 if (EFI_ERROR (Status
) || EFI_ERROR (ResponseBody
.Status
)) {
1180 if (EFI_ERROR (ResponseBody
.Status
)) {
1181 Status
= ResponseBody
.Status
;
1185 ReceivedSize
+= ResponseBody
.BodyLength
;
1186 if (Private
->HttpBootCallback
!= NULL
) {
1187 Status
= Private
->HttpBootCallback
->Callback (
1188 Private
->HttpBootCallback
,
1189 HttpBootHttpEntityBody
,
1191 (UINT32
)ResponseBody
.BodyLength
,
1194 if (EFI_ERROR (Status
)) {
1201 // In "chunked" transfer-coding mode, so we need to parse the received
1202 // data to get the real entity content.
1205 while (!HttpIsMessageComplete (Parser
)) {
1207 // Allocate a buffer in Block to hold the message-body.
1208 // If caller provides a buffer, this Block will be reused in every HttpIoRecvResponse().
1209 // Otherwise a buffer, the buffer in Block will be cached and we should allocate a new before
1210 // every HttpIoRecvResponse().
1212 if (Block
== NULL
|| Context
.BufferSize
== 0) {
1213 Block
= AllocatePool (HTTP_BOOT_BLOCK_SIZE
);
1214 if (Block
== NULL
) {
1215 Status
= EFI_OUT_OF_RESOURCES
;
1218 Context
.NewBlock
= TRUE
;
1219 Context
.Block
= Block
;
1221 Context
.NewBlock
= FALSE
;
1224 ResponseBody
.Body
= (CHAR8
*) Block
;
1225 ResponseBody
.BodyLength
= HTTP_BOOT_BLOCK_SIZE
;
1226 Status
= HttpIoRecvResponse (
1231 if (EFI_ERROR (Status
) || EFI_ERROR (ResponseBody
.Status
)) {
1232 if (EFI_ERROR (ResponseBody
.Status
)) {
1233 Status
= ResponseBody
.Status
;
1239 // Parse the new received block of the message-body, the block will be saved in cache.
1241 Status
= HttpParseMessageBody (
1243 ResponseBody
.BodyLength
,
1246 if (EFI_ERROR (Status
)) {
1254 // 3.5 Message-body receive & parse is completed, we should be able to get the file size now.
1256 Status
= HttpGetEntityLength (Parser
, &ContentLength
);
1257 if (EFI_ERROR (Status
)) {
1261 if (*BufferSize
< ContentLength
) {
1262 Status
= EFI_BUFFER_TOO_SMALL
;
1264 Status
= EFI_SUCCESS
;
1266 *BufferSize
= ContentLength
;
1269 // 4. Save the cache item to driver's cache list and return.
1271 if (Cache
!= NULL
) {
1272 Cache
->EntityLength
= ContentLength
;
1273 InsertTailList (&Private
->CacheList
, &Cache
->Link
);
1276 if (Parser
!= NULL
) {
1277 HttpFreeMsgParser (Parser
);
1283 if (Parser
!= NULL
) {
1284 HttpFreeMsgParser (Parser
);
1286 if (Context
.Block
!= NULL
) {
1287 FreePool (Context
.Block
);
1289 HttpBootFreeCache (Cache
);
1292 if (ResponseData
!= NULL
) {
1293 FreePool (ResponseData
);
1296 if (RequestData
!= NULL
) {
1297 FreePool (RequestData
);
1300 HttpIoFreeHeader (HttpIoHeader
);
1302 if (Cache
!= NULL
) {