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 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
));
241 if (Status
== EFI_INVALID_PARAMETER
) {
242 AsciiPrint ("\n Error: Invalid URI address.\n");
243 } else if (Status
== EFI_ACCESS_DENIED
) {
244 AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n");
249 if ((SelectOffer
->OfferType
== HttpOfferTypeDhcpNameUriDns
) ||
250 (SelectOffer
->OfferType
== HttpOfferTypeDhcpDns
) ||
251 (SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUriDns
)) {
252 Option
= SelectOffer
->OptList
[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER
];
253 ASSERT (Option
!= NULL
);
256 // Record the Dns Server address list.
258 Private
->DnsServerCount
= (Option
->Length
) / sizeof (EFI_IPv4_ADDRESS
);
260 Private
->DnsServerIp
= AllocateZeroPool (Private
->DnsServerCount
* sizeof (EFI_IP_ADDRESS
));
261 if (Private
->DnsServerIp
== NULL
) {
262 return EFI_OUT_OF_RESOURCES
;
265 for (DnsServerIndex
= 0; DnsServerIndex
< Private
->DnsServerCount
; DnsServerIndex
++) {
266 CopyMem (&(Private
->DnsServerIp
[DnsServerIndex
].v4
), &(((EFI_IPv4_ADDRESS
*) Option
->Data
)[DnsServerIndex
]), sizeof (EFI_IPv4_ADDRESS
));
270 // Configure the default DNS server if server assigned.
272 Status
= HttpBootRegisterIp4Dns (
277 if (EFI_ERROR (Status
)) {
278 FreePool (Private
->DnsServerIp
);
279 Private
->DnsServerIp
= NULL
;
285 // Extract the port from URL, and use default HTTP port 80 if not provided.
287 Status
= HttpUrlGetPort (
288 Private
->BootFileUri
,
289 Private
->BootFileUriParser
,
292 if (EFI_ERROR (Status
) || Private
->Port
== 0) {
297 // All boot informations are valid here.
301 // Update the device path to include the boot resource information.
303 Status
= HttpBootUpdateDevicePath (Private
);
304 if (EFI_ERROR (Status
) && Private
->DnsServerIp
!= NULL
) {
305 FreePool (Private
->DnsServerIp
);
306 Private
->DnsServerIp
= NULL
;
313 Parse the boot file URI information from the selected Dhcp6 offer packet.
315 @param[in] Private The pointer to the driver's private data.
317 @retval EFI_SUCCESS Successfully parsed out all the boot information.
318 @retval Others Failed to parse out the boot information.
322 HttpBootDhcp6ExtractUriInfo (
323 IN HTTP_BOOT_PRIVATE_DATA
*Private
326 HTTP_BOOT_DHCP6_PACKET_CACHE
*SelectOffer
;
327 HTTP_BOOT_DHCP6_PACKET_CACHE
*HttpOffer
;
330 UINT32 DnsServerIndex
;
331 EFI_DHCP6_PACKET_OPTION
*Option
;
332 EFI_IPv6_ADDRESS IpAddr
;
338 ASSERT (Private
!= NULL
);
339 ASSERT (Private
->SelectIndex
!= 0);
340 SelectIndex
= Private
->SelectIndex
- 1;
341 ASSERT (SelectIndex
< HTTP_BOOT_OFFER_MAX_NUM
);
345 Status
= EFI_SUCCESS
;
348 // SelectOffer contains the IP address configuration and name server configuration.
349 // HttpOffer contains the boot file URL.
351 SelectOffer
= &Private
->OfferBuffer
[SelectIndex
].Dhcp6
;
352 if (Private
->FilePathUri
== NULL
) {
354 // In Corporate environment, we need a HttpOffer.
356 if ((SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUri
) ||
357 (SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUriDns
) ||
358 (SelectOffer
->OfferType
== HttpOfferTypeDhcpNameUriDns
)) {
359 HttpOffer
= SelectOffer
;
361 ASSERT (Private
->SelectProxyType
!= HttpOfferTypeMax
);
362 ProxyIndex
= Private
->OfferIndex
[Private
->SelectProxyType
][0];
363 HttpOffer
= &Private
->OfferBuffer
[ProxyIndex
].Dhcp6
;
365 Private
->BootFileUriParser
= HttpOffer
->UriParser
;
366 Private
->BootFileUri
= (CHAR8
*) HttpOffer
->OptList
[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL
]->Data
;
369 // In Home environment the BootFileUri comes from the FilePath.
371 Private
->BootFileUriParser
= Private
->FilePathUriParser
;
372 Private
->BootFileUri
= Private
->FilePathUri
;
376 // Check the URI scheme.
378 Status
= HttpBootCheckUriScheme (Private
->BootFileUri
);
379 if (EFI_ERROR (Status
)) {
380 DEBUG ((EFI_D_ERROR
, "HttpBootDhcp6ExtractUriInfo: %r.\n", Status
));
381 if (Status
== EFI_INVALID_PARAMETER
) {
382 AsciiPrint ("\n Error: Invalid URI address.\n");
383 } else if (Status
== EFI_ACCESS_DENIED
) {
384 AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n");
390 // Set the Local station address to IP layer.
392 Status
= HttpBootSetIp6Address (Private
);
393 if (EFI_ERROR (Status
)) {
398 // Register the IPv6 gateway address to the network device.
400 Status
= HttpBootSetIp6Gateway (Private
);
401 if (EFI_ERROR (Status
)) {
405 if ((SelectOffer
->OfferType
== HttpOfferTypeDhcpNameUriDns
) ||
406 (SelectOffer
->OfferType
== HttpOfferTypeDhcpDns
) ||
407 (SelectOffer
->OfferType
== HttpOfferTypeDhcpIpUriDns
)) {
408 Option
= SelectOffer
->OptList
[HTTP_BOOT_DHCP6_IDX_DNS_SERVER
];
409 ASSERT (Option
!= NULL
);
412 // Record the Dns Server address list.
414 Private
->DnsServerCount
= HTONS (Option
->OpLen
) / sizeof (EFI_IPv6_ADDRESS
);
416 Private
->DnsServerIp
= AllocateZeroPool (Private
->DnsServerCount
* sizeof (EFI_IP_ADDRESS
));
417 if (Private
->DnsServerIp
== NULL
) {
418 return EFI_OUT_OF_RESOURCES
;
421 for (DnsServerIndex
= 0; DnsServerIndex
< Private
->DnsServerCount
; DnsServerIndex
++) {
422 CopyMem (&(Private
->DnsServerIp
[DnsServerIndex
].v6
), &(((EFI_IPv6_ADDRESS
*) Option
->Data
)[DnsServerIndex
]), sizeof (EFI_IPv6_ADDRESS
));
426 // Configure the default DNS server if server assigned.
428 Status
= HttpBootSetIp6Dns (
430 HTONS (Option
->OpLen
),
433 if (EFI_ERROR (Status
)) {
439 // Extract the HTTP server Ip from URL. This is used to Check route table
440 // whether can send message to HTTP Server Ip through the GateWay.
442 Status
= HttpUrlGetIp6 (
443 Private
->BootFileUri
,
444 Private
->BootFileUriParser
,
448 if (EFI_ERROR (Status
)) {
450 // The Http server address is expressed by Name Ip, so perform DNS resolution
452 Status
= HttpUrlGetHostName (
453 Private
->BootFileUri
,
454 Private
->BootFileUriParser
,
457 if (EFI_ERROR (Status
)) {
461 HostNameSize
= AsciiStrSize (HostName
);
462 HostNameStr
= AllocateZeroPool (HostNameSize
* sizeof (CHAR16
));
463 if (HostNameStr
== NULL
) {
464 Status
= EFI_OUT_OF_RESOURCES
;
468 AsciiStrToUnicodeStrS (HostName
, HostNameStr
, HostNameSize
);
470 if (HostName
!= NULL
) {
474 Status
= HttpBootDns (Private
, HostNameStr
, &IpAddr
);
475 FreePool (HostNameStr
);
476 if (EFI_ERROR (Status
)) {
477 AsciiPrint ("\n Error: Could not retrieve the host address from DNS server.\n");
482 CopyMem (&Private
->ServerIp
.v6
, &IpAddr
, sizeof (EFI_IPv6_ADDRESS
));
485 // Extract the port from URL, and use default HTTP port 80 if not provided.
487 Status
= HttpUrlGetPort (
488 Private
->BootFileUri
,
489 Private
->BootFileUriParser
,
492 if (EFI_ERROR (Status
) || Private
->Port
== 0) {
497 // All boot informations are valid here.
501 // Update the device path to include the boot resource information.
503 Status
= HttpBootUpdateDevicePath (Private
);
504 if (EFI_ERROR (Status
)) {
511 if (Private
->DnsServerIp
!= NULL
) {
512 FreePool (Private
->DnsServerIp
);
513 Private
->DnsServerIp
= NULL
;
521 Discover all the boot information for boot file.
523 @param[in, out] Private The pointer to the driver's private data.
525 @retval EFI_SUCCESS Successfully obtained all the boot information .
526 @retval Others Failed to retrieve the boot information.
530 HttpBootDiscoverBootInfo (
531 IN OUT HTTP_BOOT_PRIVATE_DATA
*Private
537 // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
538 // other Http boot information.
540 Status
= HttpBootDhcp (Private
);
541 if (EFI_ERROR (Status
)) {
545 if (!Private
->UsingIpv6
) {
546 Status
= HttpBootDhcp4ExtractUriInfo (Private
);
548 Status
= HttpBootDhcp6ExtractUriInfo (Private
);
555 HttpIo Callback function which will be invoked when specified HTTP_IO_CALLBACK_EVENT happened.
557 @param[in] EventType Indicate the Event type that occurs in the current callback.
558 @param[in] Message HTTP message which will be send to, or just received from HTTP server.
559 @param[in] Context The Callback Context pointer.
561 @retval EFI_SUCCESS Tells the HttpIo to continue the HTTP process.
562 @retval Others Tells the HttpIo to abort the current HTTP process.
566 HttpBootHttpIoCallback (
567 IN HTTP_IO_CALLBACK_EVENT EventType
,
568 IN EFI_HTTP_MESSAGE
*Message
,
572 HTTP_BOOT_PRIVATE_DATA
*Private
;
574 Private
= (HTTP_BOOT_PRIVATE_DATA
*) Context
;
575 if (Private
->HttpBootCallback
!= NULL
) {
576 Status
= Private
->HttpBootCallback
->Callback (
577 Private
->HttpBootCallback
,
578 EventType
== HttpIoRequest
? HttpBootHttpRequest
: HttpBootHttpResponse
,
579 EventType
== HttpIoRequest
? FALSE
: TRUE
,
580 sizeof (EFI_HTTP_MESSAGE
),
589 Create a HttpIo instance for the file download.
591 @param[in] Private The pointer to the driver's private data.
593 @retval EFI_SUCCESS Successfully created.
594 @retval Others Failed to create HttpIo.
598 HttpBootCreateHttpIo (
599 IN HTTP_BOOT_PRIVATE_DATA
*Private
602 HTTP_IO_CONFIG_DATA ConfigData
;
604 EFI_HANDLE ImageHandle
;
606 ASSERT (Private
!= NULL
);
608 ZeroMem (&ConfigData
, sizeof (HTTP_IO_CONFIG_DATA
));
609 if (!Private
->UsingIpv6
) {
610 ConfigData
.Config4
.HttpVersion
= HttpVersion11
;
611 ConfigData
.Config4
.RequestTimeOut
= HTTP_BOOT_REQUEST_TIMEOUT
;
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
= HTTP_BOOT_REQUEST_TIMEOUT
;
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
= HttpBootCreateHeader (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
= HttpBootSetHeader (
1009 FreePool (HostName
);
1010 if (EFI_ERROR (Status
)) {
1015 // Add HTTP header field 2: Accept
1017 Status
= HttpBootSetHeader (
1022 if (EFI_ERROR (Status
)) {
1027 // Add HTTP header field 3: User-Agent
1029 Status
= HttpBootSetHeader (
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 HttpBootFreeHeader (HttpIoHeader
);
1302 if (Cache
!= NULL
) {