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 @param[in] Private The pointer to the driver's private data.
21 @param[in] UsingIpv6 Specifies the type of IP addresses that are to be
22 used during the session that is being started.
23 Set to TRUE for IPv6, and FALSE for IPv4.
24 @param[in] FilePath The device specific path of the file to load.
26 @retval EFI_SUCCESS HTTP boot was successfully enabled.
27 @retval EFI_INVALID_PARAMETER Private is NULL.
28 @retval EFI_ALREADY_STARTED The driver is already in started state.
29 @retval EFI_OUT_OF_RESOURCES There are not enough resources.
34 IN HTTP_BOOT_PRIVATE_DATA
*Private
,
36 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
42 if (Private
== NULL
|| FilePath
== NULL
) {
43 return EFI_INVALID_PARAMETER
;
46 if (Private
->Started
) {
47 return EFI_ALREADY_STARTED
;
51 // Detect whether using ipv6 or not, and set it to the private data.
53 if (UsingIpv6
&& Private
->Ip6Nic
!= NULL
) {
54 Private
->UsingIpv6
= TRUE
;
55 } else if (!UsingIpv6
&& Private
->Ip4Nic
!= NULL
) {
56 Private
->UsingIpv6
= FALSE
;
58 return EFI_UNSUPPORTED
;
62 // Check whether the URI address is specified.
64 Status
= HttpBootParseFilePath (FilePath
, &Private
->FilePathUri
);
65 if (EFI_ERROR (Status
)) {
66 return EFI_INVALID_PARAMETER
;
69 if (Private
->FilePathUri
!= NULL
) {
70 Status
= HttpParseUrl (
72 (UINT32
) AsciiStrLen (Private
->FilePathUri
),
74 &Private
->FilePathUriParser
76 if (EFI_ERROR (Status
)) {
82 // Init the content of cached DHCP offer list.
84 ZeroMem (Private
->OfferBuffer
, sizeof (Private
->OfferBuffer
));
85 if (!Private
->UsingIpv6
) {
86 for (Index
= 0; Index
< HTTP_BOOT_OFFER_MAX_NUM
; Index
++) {
87 Private
->OfferBuffer
[Index
].Dhcp4
.Packet
.Offer
.Size
= HTTP_BOOT_DHCP4_PACKET_MAX_SIZE
;
90 for (Index
= 0; Index
< HTTP_BOOT_OFFER_MAX_NUM
; Index
++) {
91 Private
->OfferBuffer
[Index
].Dhcp6
.Packet
.Offer
.Size
= HTTP_BOOT_DHCP6_PACKET_MAX_SIZE
;
95 if (Private
->UsingIpv6
) {
97 // Set Ip6 policy to Automatic to start the Ip6 router discovery.
99 Status
= HttpBootSetIp6Policy (Private
);
100 if (EFI_ERROR (Status
)) {
104 Private
->Started
= TRUE
;
110 Attempt to complete a DHCPv4 D.O.R.A or DHCPv6 S.R.A.A sequence to retrieve the boot resource information.
112 @param[in] Private The pointer to the driver's private data.
114 @retval EFI_SUCCESS Boot info was successfully retrieved.
115 @retval EFI_INVALID_PARAMETER Private is NULL.
116 @retval EFI_NOT_STARTED The driver is in stopped state.
117 @retval EFI_DEVICE_ERROR An unexpected network error occurred.
118 @retval Others Other errors as indicated.
123 IN HTTP_BOOT_PRIVATE_DATA
*Private
128 if (Private
== NULL
) {
129 return EFI_INVALID_PARAMETER
;
132 if (!Private
->Started
) {
133 return EFI_NOT_STARTED
;
136 Status
= EFI_DEVICE_ERROR
;
138 if (!Private
->UsingIpv6
) {
140 // Start D.O.R.A process to get a IPv4 address and other boot information.
142 Status
= HttpBootDhcp4Dora (Private
);
145 // Start S.A.R.R process to get a IPv6 address and other boot information.
147 Status
= HttpBootDhcp6Sarr (Private
);
154 Attempt to download the boot file through HTTP message exchange.
156 @param[in] Private The pointer to the driver's private data.
157 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
158 code of EFI_SUCCESS, the amount of data transferred to
159 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
160 the size of Buffer required to retrieve the requested file.
161 @param[in] Buffer The memory buffer to transfer the file to. If Buffer is NULL,
162 then the size of the requested file is returned in
165 @retval EFI_SUCCESS Boot file was loaded successfully.
166 @retval EFI_INVALID_PARAMETER Private is NULL.
167 @retval EFI_NOT_STARTED The driver is in stopped state.
168 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the boot file. BufferSize has
169 been updated with the size needed to complete the request.
170 @retval EFI_DEVICE_ERROR An unexpected network error occurred.
171 @retval Others Other errors as indicated.
176 IN HTTP_BOOT_PRIVATE_DATA
*Private
,
177 IN OUT UINTN
*BufferSize
,
178 IN VOID
*Buffer OPTIONAL
183 if (Private
== NULL
) {
184 return EFI_INVALID_PARAMETER
;
187 if (!Private
->Started
) {
188 return EFI_NOT_STARTED
;
191 Status
= EFI_DEVICE_ERROR
;
193 if (Private
->BootFileUri
== NULL
) {
195 // Parse the cached offer to get the boot file URL first.
197 Status
= HttpBootDiscoverBootInfo (Private
);
198 if (EFI_ERROR (Status
)) {
203 if (!Private
->HttpCreated
) {
205 // Create HTTP child.
207 Status
= HttpBootCreateHttpIo (Private
);
208 if (EFI_ERROR (Status
)) {
213 if (Private
->BootFileSize
== 0) {
215 // Discover the information about the bootfile if we haven't.
219 // Try to use HTTP HEAD method.
221 Status
= HttpBootGetBootFile (
224 &Private
->BootFileSize
,
227 if (EFI_ERROR (Status
) && Status
!= EFI_BUFFER_TOO_SMALL
) {
229 // Failed to get file size by HEAD method, may be trunked encoding, try HTTP GET method.
231 ASSERT (Private
->BootFileSize
== 0);
232 Status
= HttpBootGetBootFile (
235 &Private
->BootFileSize
,
238 if (EFI_ERROR (Status
) && Status
!= EFI_BUFFER_TOO_SMALL
) {
244 if (*BufferSize
< Private
->BootFileSize
) {
245 *BufferSize
= Private
->BootFileSize
;
246 return EFI_BUFFER_TOO_SMALL
;
250 // Load the boot file into Buffer
252 return HttpBootGetBootFile (
261 Disable the use of UEFI HTTP boot function.
263 @param[in] Private The pointer to the driver's private data.
265 @retval EFI_SUCCESS HTTP boot was successfully disabled.
266 @retval EFI_NOT_STARTED The driver is already in stopped state.
267 @retval EFI_INVALID_PARAMETER Private is NULL.
268 @retval Others Unexpected error when stop the function.
273 IN HTTP_BOOT_PRIVATE_DATA
*Private
278 if (Private
== NULL
) {
279 return EFI_INVALID_PARAMETER
;
282 if (!Private
->Started
) {
283 return EFI_NOT_STARTED
;
286 if (Private
->HttpCreated
) {
287 HttpIoDestroyIo (&Private
->HttpIo
);
288 Private
->HttpCreated
= FALSE
;
291 Private
->Started
= FALSE
;
292 ZeroMem (&Private
->StationIp
, sizeof (EFI_IP_ADDRESS
));
293 ZeroMem (&Private
->SubnetMask
, sizeof (EFI_IP_ADDRESS
));
294 ZeroMem (&Private
->GatewayIp
, sizeof (EFI_IP_ADDRESS
));
296 Private
->BootFileUri
= NULL
;
297 Private
->BootFileUriParser
= NULL
;
298 Private
->BootFileSize
= 0;
299 Private
->SelectIndex
= 0;
300 Private
->SelectProxyType
= HttpOfferTypeMax
;
302 if (!Private
->UsingIpv6
) {
304 // Stop and release the DHCP4 child.
306 Private
->Dhcp4
->Stop (Private
->Dhcp4
);
307 Private
->Dhcp4
->Configure (Private
->Dhcp4
, NULL
);
309 for (Index
= 0; Index
< HTTP_BOOT_OFFER_MAX_NUM
; Index
++) {
310 if (Private
->OfferBuffer
[Index
].Dhcp4
.UriParser
) {
311 HttpUrlFreeParser (Private
->OfferBuffer
[Index
].Dhcp4
.UriParser
);
316 // Stop and release the DHCP6 child.
318 Private
->Dhcp6
->Stop (Private
->Dhcp6
);
319 Private
->Dhcp6
->Configure (Private
->Dhcp6
, NULL
);
321 for (Index
= 0; Index
< HTTP_BOOT_OFFER_MAX_NUM
; Index
++) {
322 if (Private
->OfferBuffer
[Index
].Dhcp6
.UriParser
) {
323 HttpUrlFreeParser (Private
->OfferBuffer
[Index
].Dhcp6
.UriParser
);
328 if (Private
->FilePathUri
!= NULL
) {
329 FreePool (Private
->FilePathUri
);
330 HttpUrlFreeParser (Private
->FilePathUriParser
);
331 Private
->FilePathUri
= NULL
;
332 Private
->FilePathUriParser
= NULL
;
335 ZeroMem (Private
->OfferBuffer
, sizeof (Private
->OfferBuffer
));
336 Private
->OfferNum
= 0;
337 ZeroMem (Private
->OfferCount
, sizeof (Private
->OfferCount
));
338 ZeroMem (Private
->OfferIndex
, sizeof (Private
->OfferIndex
));
340 HttpBootFreeCacheList (Private
);
346 Causes the driver to load a specified file.
348 @param This Protocol instance pointer.
349 @param FilePath The device specific path of the file to load.
350 @param BootPolicy If TRUE, indicates that the request originates from the
351 boot manager is attempting to load FilePath as a boot
352 selection. If FALSE, then FilePath must match as exact file
354 @param BufferSize On input the size of Buffer in bytes. On output with a return
355 code of EFI_SUCCESS, the amount of data transferred to
356 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
357 the size of Buffer required to retrieve the requested file.
358 @param Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
359 then the size of the requested file is returned in
362 @retval EFI_SUCCESS The file was loaded.
363 @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy
364 @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
366 @retval EFI_NO_MEDIA No medium was present to load the file.
367 @retval EFI_DEVICE_ERROR The file was not loaded due to a device error.
368 @retval EFI_NO_RESPONSE The remote system did not respond.
369 @retval EFI_NOT_FOUND The file was not found.
370 @retval EFI_ABORTED The file load process was manually cancelled.
371 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
372 BufferSize has been updated with the size needed to complete
378 HttpBootDxeLoadFile (
379 IN EFI_LOAD_FILE_PROTOCOL
*This
,
380 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
,
381 IN BOOLEAN BootPolicy
,
382 IN OUT UINTN
*BufferSize
,
383 IN VOID
*Buffer OPTIONAL
386 HTTP_BOOT_PRIVATE_DATA
*Private
;
387 HTTP_BOOT_VIRTUAL_NIC
*VirtualNic
;
388 BOOLEAN MediaPresent
;
392 if (This
== NULL
|| BufferSize
== NULL
|| FilePath
== NULL
) {
393 return EFI_INVALID_PARAMETER
;
397 // Only support BootPolicy
400 return EFI_UNSUPPORTED
;
403 VirtualNic
= HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE (This
);
404 Private
= VirtualNic
->Private
;
407 // Check media status before HTTP boot start
410 NetLibDetectMedia (Private
->Controller
, &MediaPresent
);
416 // Check whether the virtual nic is using IPv6 or not.
419 if (VirtualNic
== Private
->Ip6Nic
) {
424 // Initialize HTTP boot.
426 Status
= HttpBootStart (Private
, UsingIpv6
, FilePath
);
427 if (Status
== EFI_ALREADY_STARTED
) {
429 // Restart the HTTP boot driver in 2 cases:
430 // 1. Http boot Driver has already been started but not on the required IP version.
431 // 2. The required boot FilePath is different with the one we produced in the device path
434 if ((UsingIpv6
!= Private
->UsingIpv6
) || !IsDevicePathEnd(FilePath
)) {
435 Status
= HttpBootStop (Private
);
436 if (!EFI_ERROR (Status
)) {
437 Status
= HttpBootStart (Private
, UsingIpv6
, FilePath
);
443 // Load the boot file.
445 if (Status
== EFI_SUCCESS
|| Status
== EFI_ALREADY_STARTED
) {
446 Status
= HttpBootLoadFile (Private
, BufferSize
, Buffer
);
449 if (Status
!= EFI_SUCCESS
&& Status
!= EFI_BUFFER_TOO_SMALL
) {
450 HttpBootStop (Private
);
452 if (!Private
->UsingIpv6
) {
454 // Stop and release the DHCP4 child.
456 Private
->Dhcp4
->Stop (Private
->Dhcp4
);
457 Private
->Dhcp4
->Configure (Private
->Dhcp4
, NULL
);
460 // Stop and release the DHCP6 child.
462 Private
->Dhcp6
->Stop (Private
->Dhcp6
);
463 Private
->Dhcp6
->Configure (Private
->Dhcp6
, NULL
);
471 /// Load File Protocol instance
473 GLOBAL_REMOVE_IF_UNREFERENCED
474 EFI_LOAD_FILE_PROTOCOL gHttpBootDxeLoadFile
= {