2 Implementation of the boot file download function.
4 Copyright (c) 2015 - 2018, 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 ((EFI_D_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 ((EFI_D_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
;
600 ASSERT (Private
!= NULL
);
602 ZeroMem (&ConfigData
, sizeof (HTTP_IO_CONFIG_DATA
));
603 if (!Private
->UsingIpv6
) {
604 ConfigData
.Config4
.HttpVersion
= HttpVersion11
;
605 ConfigData
.Config4
.RequestTimeOut
= HTTP_BOOT_REQUEST_TIMEOUT
;
606 IP4_COPY_ADDRESS (&ConfigData
.Config4
.LocalIp
, &Private
->StationIp
.v4
);
607 IP4_COPY_ADDRESS (&ConfigData
.Config4
.SubnetMask
, &Private
->SubnetMask
.v4
);
608 ImageHandle
= Private
->Ip4Nic
->ImageHandle
;
610 ConfigData
.Config6
.HttpVersion
= HttpVersion11
;
611 ConfigData
.Config6
.RequestTimeOut
= HTTP_BOOT_REQUEST_TIMEOUT
;
612 IP6_COPY_ADDRESS (&ConfigData
.Config6
.LocalIp
, &Private
->StationIp
.v6
);
613 ImageHandle
= Private
->Ip6Nic
->ImageHandle
;
616 Status
= HttpIoCreateIo (
619 Private
->UsingIpv6
? IP_VERSION_6
: IP_VERSION_4
,
621 HttpBootHttpIoCallback
,
625 if (EFI_ERROR (Status
)) {
629 Private
->HttpCreated
= TRUE
;
634 Release all the resource of a cache item.
636 @param[in] Cache The pointer to the cache item.
641 IN HTTP_BOOT_CACHE_CONTENT
*Cache
646 LIST_ENTRY
*NextEntry
;
647 HTTP_BOOT_ENTITY_DATA
*EntityData
;
651 // Free the request data
653 if (Cache
->RequestData
!= NULL
) {
654 if (Cache
->RequestData
->Url
!= NULL
) {
655 FreePool (Cache
->RequestData
->Url
);
657 FreePool (Cache
->RequestData
);
661 // Free the response header
663 if (Cache
->ResponseData
!= NULL
) {
664 if (Cache
->ResponseData
->Headers
!= NULL
) {
665 for (Index
= 0; Index
< Cache
->ResponseData
->HeaderCount
; Index
++) {
666 FreePool (Cache
->ResponseData
->Headers
[Index
].FieldName
);
667 FreePool (Cache
->ResponseData
->Headers
[Index
].FieldValue
);
669 FreePool (Cache
->ResponseData
->Headers
);
674 // Free the response body
676 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Cache
->EntityDataList
) {
677 EntityData
= NET_LIST_USER_STRUCT (Entry
, HTTP_BOOT_ENTITY_DATA
, Link
);
678 if (EntityData
->Block
!= NULL
) {
679 FreePool (EntityData
->Block
);
681 RemoveEntryList (&EntityData
->Link
);
682 FreePool (EntityData
);
690 Clean up all cached data.
692 @param[in] Private The pointer to the driver's private data.
696 HttpBootFreeCacheList (
697 IN HTTP_BOOT_PRIVATE_DATA
*Private
701 LIST_ENTRY
*NextEntry
;
702 HTTP_BOOT_CACHE_CONTENT
*Cache
;
704 NET_LIST_FOR_EACH_SAFE (Entry
, NextEntry
, &Private
->CacheList
) {
705 Cache
= NET_LIST_USER_STRUCT (Entry
, HTTP_BOOT_CACHE_CONTENT
, Link
);
706 RemoveEntryList (&Cache
->Link
);
707 HttpBootFreeCache (Cache
);
712 Get the file content from cached data.
714 @param[in] Private The pointer to the driver's private data.
715 @param[in] Uri Uri of the file to be retrieved from cache.
716 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
717 code of EFI_SUCCESS, the amount of data transferred to
718 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
719 the size of Buffer required to retrieve the requested file.
720 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
721 then the size of the requested file is returned in
723 @param[out] ImageType The image type of the downloaded file.
725 @retval EFI_SUCCESS Successfully created.
726 @retval Others Failed to create HttpIo.
730 HttpBootGetFileFromCache (
731 IN HTTP_BOOT_PRIVATE_DATA
*Private
,
733 IN OUT UINTN
*BufferSize
,
735 OUT HTTP_BOOT_IMAGE_TYPE
*ImageType
740 HTTP_BOOT_CACHE_CONTENT
*Cache
;
741 HTTP_BOOT_ENTITY_DATA
*EntityData
;
744 if (Uri
== NULL
|| BufferSize
== NULL
|| Buffer
== NULL
|| ImageType
== NULL
) {
745 return EFI_INVALID_PARAMETER
;
748 NET_LIST_FOR_EACH (Entry
, &Private
->CacheList
) {
749 Cache
= NET_LIST_USER_STRUCT (Entry
, HTTP_BOOT_CACHE_CONTENT
, Link
);
751 // Compare the URI to see whether we already have a cache for this file.
753 if ((Cache
->RequestData
!= NULL
) &&
754 (Cache
->RequestData
->Url
!= NULL
) &&
755 (StrCmp (Uri
, Cache
->RequestData
->Url
) == 0)) {
757 // Hit in cache, record image type.
759 *ImageType
= Cache
->ImageType
;
762 // Check buffer size.
764 if (*BufferSize
< Cache
->EntityLength
) {
765 *BufferSize
= Cache
->EntityLength
;
766 return EFI_BUFFER_TOO_SMALL
;
770 // Fill data to buffer.
773 NET_LIST_FOR_EACH (Entry2
, &Cache
->EntityDataList
) {
774 EntityData
= NET_LIST_USER_STRUCT (Entry2
, HTTP_BOOT_ENTITY_DATA
, Link
);
775 if (*BufferSize
> CopyedSize
) {
778 EntityData
->DataStart
,
779 MIN (EntityData
->DataLength
, *BufferSize
- CopyedSize
)
781 CopyedSize
+= MIN (EntityData
->DataLength
, *BufferSize
- CopyedSize
);
784 *BufferSize
= CopyedSize
;
789 return EFI_NOT_FOUND
;
793 A callback function to intercept events during message parser.
795 This function will be invoked during HttpParseMessageBody() with various events type. An error
796 return status of the callback function will cause the HttpParseMessageBody() aborted.
798 @param[in] EventType Event type of this callback call.
799 @param[in] Data A pointer to data buffer.
800 @param[in] Length Length in bytes of the Data.
801 @param[in] Context Callback context set by HttpInitMsgParser().
803 @retval EFI_SUCCESS Continue to parser the message body.
804 @retval Others Abort the parse.
809 HttpBootGetBootFileCallback (
810 IN HTTP_BODY_PARSE_EVENT EventType
,
816 HTTP_BOOT_CALLBACK_DATA
*CallbackData
;
817 HTTP_BOOT_ENTITY_DATA
*NewEntityData
;
819 EFI_HTTP_BOOT_CALLBACK_PROTOCOL
*HttpBootCallback
;
822 // We only care about the entity data.
824 if (EventType
!= BodyParseEventOnData
) {
828 CallbackData
= (HTTP_BOOT_CALLBACK_DATA
*) Context
;
829 HttpBootCallback
= CallbackData
->Private
->HttpBootCallback
;
830 if (HttpBootCallback
!= NULL
) {
831 Status
= HttpBootCallback
->Callback (
833 HttpBootHttpEntityBody
,
838 if (EFI_ERROR (Status
)) {
843 // Copy data if caller has provided a buffer.
845 if (CallbackData
->BufferSize
> CallbackData
->CopyedSize
) {
847 CallbackData
->Buffer
+ CallbackData
->CopyedSize
,
849 MIN (Length
, CallbackData
->BufferSize
- CallbackData
->CopyedSize
)
851 CallbackData
->CopyedSize
+= MIN (Length
, CallbackData
->BufferSize
- CallbackData
->CopyedSize
);
855 // The caller doesn't provide a buffer, save the block into cache list.
857 if (CallbackData
->Cache
!= NULL
) {
858 NewEntityData
= AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA
));
859 if (NewEntityData
== NULL
) {
860 return EFI_OUT_OF_RESOURCES
;
862 if (CallbackData
->NewBlock
) {
863 NewEntityData
->Block
= CallbackData
->Block
;
864 CallbackData
->Block
= NULL
;
866 NewEntityData
->DataLength
= Length
;
867 NewEntityData
->DataStart
= (UINT8
*) Data
;
868 InsertTailList (&CallbackData
->Cache
->EntityDataList
, &NewEntityData
->Link
);
874 This function download the boot file by using UEFI HTTP protocol.
876 @param[in] Private The pointer to the driver's private data.
877 @param[in] HeaderOnly Only request the response header, it could save a lot of time if
878 the caller only want to know the size of the requested file.
879 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
880 code of EFI_SUCCESS, the amount of data transferred to
881 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
882 the size of Buffer required to retrieve the requested file.
883 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
884 then the size of the requested file is returned in
886 @param[out] ImageType The image type of the downloaded file.
888 @retval EFI_SUCCESS The file was loaded.
889 @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.
890 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
891 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
892 BufferSize has been updated with the size needed to complete
894 @retval Others Unexpected error happened.
898 HttpBootGetBootFile (
899 IN HTTP_BOOT_PRIVATE_DATA
*Private
,
900 IN BOOLEAN HeaderOnly
,
901 IN OUT UINTN
*BufferSize
,
903 OUT HTTP_BOOT_IMAGE_TYPE
*ImageType
907 EFI_HTTP_STATUS_CODE StatusCode
;
909 EFI_HTTP_REQUEST_DATA
*RequestData
;
910 HTTP_IO_RESPONSE_DATA
*ResponseData
;
911 HTTP_IO_RESPONSE_DATA ResponseBody
;
913 HTTP_IO_HEADER
*HttpIoHeader
;
915 HTTP_BOOT_CALLBACK_DATA Context
;
917 HTTP_BOOT_CACHE_CONTENT
*Cache
;
921 BOOLEAN IdentityMode
;
924 ASSERT (Private
!= NULL
);
925 ASSERT (Private
->HttpCreated
);
927 if (BufferSize
== NULL
|| ImageType
== NULL
) {
928 return EFI_INVALID_PARAMETER
;
931 if (*BufferSize
!= 0 && Buffer
== NULL
) {
932 return EFI_INVALID_PARAMETER
;
936 // First, check whether we already cached the requested Uri.
938 UrlSize
= AsciiStrSize (Private
->BootFileUri
);
939 Url
= AllocatePool (UrlSize
* sizeof (CHAR16
));
941 return EFI_OUT_OF_RESOURCES
;
943 AsciiStrToUnicodeStrS (Private
->BootFileUri
, Url
, UrlSize
);
944 if (!HeaderOnly
&& Buffer
!= NULL
) {
945 Status
= HttpBootGetFileFromCache (Private
, Url
, BufferSize
, Buffer
, ImageType
);
946 if (Status
!= EFI_NOT_FOUND
) {
953 // Not found in cache, try to download it through HTTP.
957 // 1. Create a temp cache item for the requested URI if caller doesn't provide buffer.
960 if ((!HeaderOnly
) && (*BufferSize
== 0)) {
961 Cache
= AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT
));
963 Status
= EFI_OUT_OF_RESOURCES
;
966 Cache
->ImageType
= ImageTypeMax
;
967 InitializeListHead (&Cache
->EntityDataList
);
971 // 2. Send HTTP request message.
975 // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:
980 HttpIoHeader
= HttpBootCreateHeader (3);
981 if (HttpIoHeader
== NULL
) {
982 Status
= EFI_OUT_OF_RESOURCES
;
987 // Add HTTP header field 1: Host
990 Status
= HttpUrlGetHostName (
991 Private
->BootFileUri
,
992 Private
->BootFileUriParser
,
995 if (EFI_ERROR (Status
)) {
998 Status
= HttpBootSetHeader (
1003 FreePool (HostName
);
1004 if (EFI_ERROR (Status
)) {
1009 // Add HTTP header field 2: Accept
1011 Status
= HttpBootSetHeader (
1016 if (EFI_ERROR (Status
)) {
1021 // Add HTTP header field 3: User-Agent
1023 Status
= HttpBootSetHeader (
1025 HTTP_HEADER_USER_AGENT
,
1026 HTTP_USER_AGENT_EFI_HTTP_BOOT
1028 if (EFI_ERROR (Status
)) {
1033 // 2.2 Build the rest of HTTP request info.
1035 RequestData
= AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA
));
1036 if (RequestData
== NULL
) {
1037 Status
= EFI_OUT_OF_RESOURCES
;
1040 RequestData
->Method
= HeaderOnly
? HttpMethodHead
: HttpMethodGet
;
1041 RequestData
->Url
= Url
;
1044 // 2.3 Record the request info in a temp cache item.
1046 if (Cache
!= NULL
) {
1047 Cache
->RequestData
= RequestData
;
1051 // 2.4 Send out the request to HTTP server.
1053 HttpIo
= &Private
->HttpIo
;
1054 Status
= HttpIoSendRequest (
1057 HttpIoHeader
->HeaderCount
,
1058 HttpIoHeader
->Headers
,
1062 if (EFI_ERROR (Status
)) {
1067 // 3. Receive HTTP response message.
1071 // 3.1 First step, use zero BodyLength to only receive the response headers.
1073 ResponseData
= AllocateZeroPool (sizeof(HTTP_IO_RESPONSE_DATA
));
1074 if (ResponseData
== NULL
) {
1075 Status
= EFI_OUT_OF_RESOURCES
;
1078 Status
= HttpIoRecvResponse (
1083 if (EFI_ERROR (Status
) || EFI_ERROR (ResponseData
->Status
)) {
1084 if (EFI_ERROR (ResponseData
->Status
)) {
1085 StatusCode
= HttpIo
->RspToken
.Message
->Data
.Response
->StatusCode
;
1086 HttpBootPrintErrorMessage (StatusCode
);
1087 Status
= ResponseData
->Status
;
1093 // Check the image type according to server's response.
1095 Status
= HttpBootCheckImageType (
1096 Private
->BootFileUri
,
1097 Private
->BootFileUriParser
,
1098 ResponseData
->HeaderCount
,
1099 ResponseData
->Headers
,
1102 if (EFI_ERROR (Status
)) {
1107 // 3.2 Cache the response header.
1109 if (Cache
!= NULL
) {
1110 Cache
->ResponseData
= ResponseData
;
1111 Cache
->ImageType
= *ImageType
;
1115 // 3.3 Init a message-body parser from the header information.
1118 Context
.NewBlock
= FALSE
;
1119 Context
.Block
= NULL
;
1120 Context
.CopyedSize
= 0;
1121 Context
.Buffer
= Buffer
;
1122 Context
.BufferSize
= *BufferSize
;
1123 Context
.Cache
= Cache
;
1124 Context
.Private
= Private
;
1125 Status
= HttpInitMsgParser (
1126 HeaderOnly
? HttpMethodHead
: HttpMethodGet
,
1127 ResponseData
->Response
.StatusCode
,
1128 ResponseData
->HeaderCount
,
1129 ResponseData
->Headers
,
1130 HttpBootGetBootFileCallback
,
1134 if (EFI_ERROR (Status
)) {
1139 // 3.4 Continue to receive and parse message-body if needed.
1144 // 3.4.1, check whether we are in identity transfer-coding.
1147 Status
= HttpGetEntityLength (Parser
, &ContentLength
);
1148 if (!EFI_ERROR (Status
)) {
1149 IdentityMode
= TRUE
;
1151 IdentityMode
= FALSE
;
1155 // 3.4.2, start the message-body download, the identity and chunked transfer-coding
1156 // is handled in different path here.
1158 ZeroMem (&ResponseBody
, sizeof (HTTP_IO_RESPONSE_DATA
));
1161 // In identity transfer-coding there is no need to parse the message body,
1162 // just download the message body to the user provided buffer directly.
1165 while (ReceivedSize
< ContentLength
) {
1166 ResponseBody
.Body
= (CHAR8
*) Buffer
+ ReceivedSize
;
1167 ResponseBody
.BodyLength
= *BufferSize
- ReceivedSize
;
1168 Status
= HttpIoRecvResponse (
1173 if (EFI_ERROR (Status
) || EFI_ERROR (ResponseBody
.Status
)) {
1174 if (EFI_ERROR (ResponseBody
.Status
)) {
1175 Status
= ResponseBody
.Status
;
1179 ReceivedSize
+= ResponseBody
.BodyLength
;
1180 if (Private
->HttpBootCallback
!= NULL
) {
1181 Status
= Private
->HttpBootCallback
->Callback (
1182 Private
->HttpBootCallback
,
1183 HttpBootHttpEntityBody
,
1185 (UINT32
)ResponseBody
.BodyLength
,
1188 if (EFI_ERROR (Status
)) {
1195 // In "chunked" transfer-coding mode, so we need to parse the received
1196 // data to get the real entity content.
1199 while (!HttpIsMessageComplete (Parser
)) {
1201 // Allocate a buffer in Block to hold the message-body.
1202 // If caller provides a buffer, this Block will be reused in every HttpIoRecvResponse().
1203 // Otherwise a buffer, the buffer in Block will be cached and we should allocate a new before
1204 // every HttpIoRecvResponse().
1206 if (Block
== NULL
|| Context
.BufferSize
== 0) {
1207 Block
= AllocatePool (HTTP_BOOT_BLOCK_SIZE
);
1208 if (Block
== NULL
) {
1209 Status
= EFI_OUT_OF_RESOURCES
;
1212 Context
.NewBlock
= TRUE
;
1213 Context
.Block
= Block
;
1215 Context
.NewBlock
= FALSE
;
1218 ResponseBody
.Body
= (CHAR8
*) Block
;
1219 ResponseBody
.BodyLength
= HTTP_BOOT_BLOCK_SIZE
;
1220 Status
= HttpIoRecvResponse (
1225 if (EFI_ERROR (Status
) || EFI_ERROR (ResponseBody
.Status
)) {
1226 if (EFI_ERROR (ResponseBody
.Status
)) {
1227 Status
= ResponseBody
.Status
;
1233 // Parse the new received block of the message-body, the block will be saved in cache.
1235 Status
= HttpParseMessageBody (
1237 ResponseBody
.BodyLength
,
1240 if (EFI_ERROR (Status
)) {
1248 // 3.5 Message-body receive & parse is completed, we should be able to get the file size now.
1250 Status
= HttpGetEntityLength (Parser
, &ContentLength
);
1251 if (EFI_ERROR (Status
)) {
1255 if (*BufferSize
< ContentLength
) {
1256 Status
= EFI_BUFFER_TOO_SMALL
;
1258 Status
= EFI_SUCCESS
;
1260 *BufferSize
= ContentLength
;
1263 // 4. Save the cache item to driver's cache list and return.
1265 if (Cache
!= NULL
) {
1266 Cache
->EntityLength
= ContentLength
;
1267 InsertTailList (&Private
->CacheList
, &Cache
->Link
);
1270 if (Parser
!= NULL
) {
1271 HttpFreeMsgParser (Parser
);
1277 if (Parser
!= NULL
) {
1278 HttpFreeMsgParser (Parser
);
1280 if (Context
.Block
!= NULL
) {
1281 FreePool (Context
.Block
);
1283 HttpBootFreeCache (Cache
);
1286 if (ResponseData
!= NULL
) {
1287 FreePool (ResponseData
);
1290 if (RequestData
!= NULL
) {
1291 FreePool (RequestData
);
1294 HttpBootFreeHeader (HttpIoHeader
);
1296 if (Cache
!= NULL
) {