2 The implementation of EFI_LOAD_FILE_PROTOCOL for UEFI HTTP boot.
4 Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials are licensed and made available under
6 the terms and conditions of the BSD License that accompanies this distribution.
7 The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php.
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #include "HttpBootDxe.h"
18 Enable the use of UEFI HTTP boot function.
20 If the driver has already been started but not satisfy the requirement (IP stack and
21 specified boot file path), this function will stop the driver and start it again.
23 @param[in] Private The pointer to the driver's private data.
24 @param[in] UsingIpv6 Specifies the type of IP addresses that are to be
25 used during the session that is being started.
26 Set to TRUE for IPv6, and FALSE for IPv4.
27 @param[in] FilePath The device specific path of the file to load.
29 @retval EFI_SUCCESS HTTP boot was successfully enabled.
30 @retval EFI_INVALID_PARAMETER Private is NULL or FilePath is NULL.
31 @retval EFI_INVALID_PARAMETER The FilePath doesn't contain a valid URI device path node.
32 @retval EFI_ALREADY_STARTED The driver is already in started state.
33 @retval EFI_OUT_OF_RESOURCES There are not enough resources.
38 IN HTTP_BOOT_PRIVATE_DATA
*Private
,
40 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
48 if (Private
== NULL
|| FilePath
== NULL
) {
49 return EFI_INVALID_PARAMETER
;
53 // Check the URI in the input FilePath, in order to see whether it is
54 // required to boot from a new specified boot file.
56 Status
= HttpBootParseFilePath (FilePath
, &Uri
);
57 if (EFI_ERROR (Status
)) {
58 return EFI_INVALID_PARAMETER
;
62 // Check whether we need to stop and restart the HTTP boot driver.
64 if (Private
->Started
) {
66 // Restart is needed in 2 cases:
67 // 1. Http boot driver has already been started but not on the required IP stack.
68 // 2. The specified boot file URI in FilePath is different with the one we have
71 if ((UsingIpv6
!= Private
->UsingIpv6
) ||
72 ((Uri
!= NULL
) && (AsciiStrCmp (Private
->BootFileUri
, Uri
) != 0))) {
74 // Restart is required, first stop then continue this start function.
76 Status
= HttpBootStop (Private
);
77 if (EFI_ERROR (Status
)) {
82 // Restart is not required.
87 return EFI_ALREADY_STARTED
;
92 // Detect whether using ipv6 or not, and set it to the private data.
94 if (UsingIpv6
&& Private
->Ip6Nic
!= NULL
) {
95 Private
->UsingIpv6
= TRUE
;
96 } else if (!UsingIpv6
&& Private
->Ip4Nic
!= NULL
) {
97 Private
->UsingIpv6
= FALSE
;
102 return EFI_UNSUPPORTED
;
106 // Record the specified URI and prepare the URI parser if needed.
108 Private
->FilePathUri
= Uri
;
109 if (Private
->FilePathUri
!= NULL
) {
110 Status
= HttpParseUrl (
111 Private
->FilePathUri
,
112 (UINT32
) AsciiStrLen (Private
->FilePathUri
),
114 &Private
->FilePathUriParser
116 if (EFI_ERROR (Status
)) {
117 FreePool (Private
->FilePathUri
);
123 // Init the content of cached DHCP offer list.
125 ZeroMem (Private
->OfferBuffer
, sizeof (Private
->OfferBuffer
));
126 if (!Private
->UsingIpv6
) {
127 for (Index
= 0; Index
< HTTP_BOOT_OFFER_MAX_NUM
; Index
++) {
128 Private
->OfferBuffer
[Index
].Dhcp4
.Packet
.Offer
.Size
= HTTP_BOOT_DHCP4_PACKET_MAX_SIZE
;
131 for (Index
= 0; Index
< HTTP_BOOT_OFFER_MAX_NUM
; Index
++) {
132 Private
->OfferBuffer
[Index
].Dhcp6
.Packet
.Offer
.Size
= HTTP_BOOT_DHCP6_PACKET_MAX_SIZE
;
136 if (Private
->UsingIpv6
) {
138 // Set Ip6 policy to Automatic to start the Ip6 router discovery.
140 Status
= HttpBootSetIp6Policy (Private
);
141 if (EFI_ERROR (Status
)) {
145 Private
->Started
= TRUE
;
151 Attempt to complete a DHCPv4 D.O.R.A or DHCPv6 S.R.A.A sequence to retrieve the boot resource information.
153 @param[in] Private The pointer to the driver's private data.
155 @retval EFI_SUCCESS Boot info was successfully retrieved.
156 @retval EFI_INVALID_PARAMETER Private is NULL.
157 @retval EFI_NOT_STARTED The driver is in stopped state.
158 @retval EFI_DEVICE_ERROR An unexpected network error occurred.
159 @retval Others Other errors as indicated.
164 IN HTTP_BOOT_PRIVATE_DATA
*Private
169 if (Private
== NULL
) {
170 return EFI_INVALID_PARAMETER
;
173 if (!Private
->Started
) {
174 return EFI_NOT_STARTED
;
177 Status
= EFI_DEVICE_ERROR
;
179 if (!Private
->UsingIpv6
) {
181 // Start D.O.R.A process to get a IPv4 address and other boot information.
183 Status
= HttpBootDhcp4Dora (Private
);
186 // Start S.A.R.R process to get a IPv6 address and other boot information.
188 Status
= HttpBootDhcp6Sarr (Private
);
195 Attempt to download the boot file through HTTP message exchange.
197 @param[in] Private The pointer to the driver's private data.
198 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
199 code of EFI_SUCCESS, the amount of data transferred to
200 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
201 the size of Buffer required to retrieve the requested file.
202 @param[in] Buffer The memory buffer to transfer the file to. If Buffer is NULL,
203 then the size of the requested file is returned in
205 @param[out] ImageType The image type of the downloaded file.
207 @retval EFI_SUCCESS Boot file was loaded successfully.
208 @retval EFI_INVALID_PARAMETER Private is NULL, or ImageType is NULL, or BufferSize is NULL.
209 @retval EFI_INVALID_PARAMETER *BufferSize is not zero, and Buffer is NULL.
210 @retval EFI_NOT_STARTED The driver is in stopped state.
211 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the boot file. BufferSize has
212 been updated with the size needed to complete the request.
213 @retval EFI_DEVICE_ERROR An unexpected network error occurred.
214 @retval Others Other errors as indicated.
219 IN HTTP_BOOT_PRIVATE_DATA
*Private
,
220 IN OUT UINTN
*BufferSize
,
221 IN VOID
*Buffer
, OPTIONAL
222 OUT HTTP_BOOT_IMAGE_TYPE
*ImageType
227 if (Private
== NULL
|| ImageType
== NULL
|| BufferSize
== NULL
) {
228 return EFI_INVALID_PARAMETER
;
231 if (*BufferSize
!= 0 && Buffer
== NULL
) {
232 return EFI_INVALID_PARAMETER
;
235 if (!Private
->Started
) {
236 return EFI_NOT_STARTED
;
239 Status
= EFI_DEVICE_ERROR
;
241 if (Private
->BootFileUri
== NULL
) {
243 // Parse the cached offer to get the boot file URL first.
245 Status
= HttpBootDiscoverBootInfo (Private
);
246 if (EFI_ERROR (Status
)) {
251 if (!Private
->HttpCreated
) {
253 // Create HTTP child.
255 Status
= HttpBootCreateHttpIo (Private
);
256 if (EFI_ERROR (Status
)) {
261 if (Private
->BootFileSize
== 0) {
263 // Discover the information about the bootfile if we haven't.
267 // Try to use HTTP HEAD method.
269 Status
= HttpBootGetBootFile (
272 &Private
->BootFileSize
,
276 if (EFI_ERROR (Status
) && Status
!= EFI_BUFFER_TOO_SMALL
) {
278 // Failed to get file size by HEAD method, may be trunked encoding, try HTTP GET method.
280 ASSERT (Private
->BootFileSize
== 0);
281 Status
= HttpBootGetBootFile (
284 &Private
->BootFileSize
,
288 if (EFI_ERROR (Status
) && Status
!= EFI_BUFFER_TOO_SMALL
) {
294 if (*BufferSize
< Private
->BootFileSize
) {
295 *BufferSize
= Private
->BootFileSize
;
296 return EFI_BUFFER_TOO_SMALL
;
300 // Load the boot file into Buffer
302 return HttpBootGetBootFile (
312 Disable the use of UEFI HTTP boot function.
314 @param[in] Private The pointer to the driver's private data.
316 @retval EFI_SUCCESS HTTP boot was successfully disabled.
317 @retval EFI_NOT_STARTED The driver is already in stopped state.
318 @retval EFI_INVALID_PARAMETER Private is NULL.
319 @retval Others Unexpected error when stop the function.
324 IN HTTP_BOOT_PRIVATE_DATA
*Private
329 if (Private
== NULL
) {
330 return EFI_INVALID_PARAMETER
;
333 if (!Private
->Started
) {
334 return EFI_NOT_STARTED
;
337 if (Private
->HttpCreated
) {
338 HttpIoDestroyIo (&Private
->HttpIo
);
339 Private
->HttpCreated
= FALSE
;
342 Private
->Started
= FALSE
;
343 ZeroMem (&Private
->StationIp
, sizeof (EFI_IP_ADDRESS
));
344 ZeroMem (&Private
->SubnetMask
, sizeof (EFI_IP_ADDRESS
));
345 ZeroMem (&Private
->GatewayIp
, sizeof (EFI_IP_ADDRESS
));
347 Private
->BootFileUri
= NULL
;
348 Private
->BootFileUriParser
= NULL
;
349 Private
->BootFileSize
= 0;
350 Private
->SelectIndex
= 0;
351 Private
->SelectProxyType
= HttpOfferTypeMax
;
353 if (!Private
->UsingIpv6
) {
355 // Stop and release the DHCP4 child.
357 Private
->Dhcp4
->Stop (Private
->Dhcp4
);
358 Private
->Dhcp4
->Configure (Private
->Dhcp4
, NULL
);
360 for (Index
= 0; Index
< HTTP_BOOT_OFFER_MAX_NUM
; Index
++) {
361 if (Private
->OfferBuffer
[Index
].Dhcp4
.UriParser
) {
362 HttpUrlFreeParser (Private
->OfferBuffer
[Index
].Dhcp4
.UriParser
);
367 // Stop and release the DHCP6 child.
369 Private
->Dhcp6
->Stop (Private
->Dhcp6
);
370 Private
->Dhcp6
->Configure (Private
->Dhcp6
, NULL
);
372 for (Index
= 0; Index
< HTTP_BOOT_OFFER_MAX_NUM
; Index
++) {
373 if (Private
->OfferBuffer
[Index
].Dhcp6
.UriParser
) {
374 HttpUrlFreeParser (Private
->OfferBuffer
[Index
].Dhcp6
.UriParser
);
379 if (Private
->FilePathUri
!= NULL
) {
380 FreePool (Private
->FilePathUri
);
381 HttpUrlFreeParser (Private
->FilePathUriParser
);
382 Private
->FilePathUri
= NULL
;
383 Private
->FilePathUriParser
= NULL
;
386 ZeroMem (Private
->OfferBuffer
, sizeof (Private
->OfferBuffer
));
387 Private
->OfferNum
= 0;
388 ZeroMem (Private
->OfferCount
, sizeof (Private
->OfferCount
));
389 ZeroMem (Private
->OfferIndex
, sizeof (Private
->OfferIndex
));
391 HttpBootFreeCacheList (Private
);
397 Causes the driver to load a specified file.
399 @param This Protocol instance pointer.
400 @param FilePath The device specific path of the file to load.
401 @param BootPolicy If TRUE, indicates that the request originates from the
402 boot manager is attempting to load FilePath as a boot
403 selection. If FALSE, then FilePath must match as exact file
405 @param BufferSize On input the size of Buffer in bytes. On output with a return
406 code of EFI_SUCCESS, the amount of data transferred to
407 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
408 the size of Buffer required to retrieve the requested file.
409 @param Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
410 then the size of the requested file is returned in
413 @retval EFI_SUCCESS The file was loaded.
414 @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy
415 @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
417 @retval EFI_NO_MEDIA No medium was present to load the file.
418 @retval EFI_DEVICE_ERROR The file was not loaded due to a device error.
419 @retval EFI_NO_RESPONSE The remote system did not respond.
420 @retval EFI_NOT_FOUND The file was not found.
421 @retval EFI_ABORTED The file load process was manually cancelled.
422 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
423 BufferSize has been updated with the size needed to complete
429 HttpBootDxeLoadFile (
430 IN EFI_LOAD_FILE_PROTOCOL
*This
,
431 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
,
432 IN BOOLEAN BootPolicy
,
433 IN OUT UINTN
*BufferSize
,
434 IN VOID
*Buffer OPTIONAL
437 HTTP_BOOT_PRIVATE_DATA
*Private
;
438 HTTP_BOOT_VIRTUAL_NIC
*VirtualNic
;
439 BOOLEAN MediaPresent
;
442 HTTP_BOOT_IMAGE_TYPE ImageType
;
444 if (This
== NULL
|| BufferSize
== NULL
|| FilePath
== NULL
) {
445 return EFI_INVALID_PARAMETER
;
449 // Only support BootPolicy
452 return EFI_UNSUPPORTED
;
455 VirtualNic
= HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE (This
);
456 Private
= VirtualNic
->Private
;
459 // Check media status before HTTP boot start
462 NetLibDetectMedia (Private
->Controller
, &MediaPresent
);
468 // Check whether the virtual nic is using IPv6 or not.
471 if (VirtualNic
== Private
->Ip6Nic
) {
476 // Initialize HTTP boot.
478 Status
= HttpBootStart (Private
, UsingIpv6
, FilePath
);
479 if (Status
!= EFI_SUCCESS
&& Status
!= EFI_ALREADY_STARTED
) {
484 // Load the boot file.
486 ImageType
= ImageTypeMax
;
487 Status
= HttpBootLoadFile (Private
, BufferSize
, Buffer
, &ImageType
);
488 if (EFI_ERROR (Status
)) {
489 if (Status
== EFI_BUFFER_TOO_SMALL
&& (ImageType
== ImageTypeVirtualCd
|| ImageType
== ImageTypeVirtualDisk
)) {
490 Status
= EFI_WARN_FILE_SYSTEM
;
491 } else if (Status
!= EFI_BUFFER_TOO_SMALL
) {
492 HttpBootStop (Private
);
498 // Register the RAM Disk to the system if needed.
500 if (ImageType
== ImageTypeVirtualCd
|| ImageType
== ImageTypeVirtualDisk
) {
501 Status
= HttpBootRegisterRamDisk (Private
, *BufferSize
, Buffer
, ImageType
);
502 if (!EFI_ERROR (Status
)) {
503 Status
= EFI_WARN_FILE_SYSTEM
;
511 /// Load File Protocol instance
513 GLOBAL_REMOVE_IF_UNREFERENCED
514 EFI_LOAD_FILE_PROTOCOL gHttpBootDxeLoadFile
= {