]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/HttpBootDxe/HttpBootImpl.c
IntelFsp2Pkg-Tools: GenCfgOpt.py shouldn't include specific UPD name
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootImpl.c
1 /** @file
2 The implementation of EFI_LOAD_FILE_PROTOCOL for UEFI HTTP boot.
3
4 Copyright (c) 2015 - 2017, 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.
10
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.
13
14 **/
15
16 #include "HttpBootDxe.h"
17
18 /**
19 Install HTTP Boot Callback Protocol if not installed before.
20
21 @param[in] Private Pointer to HTTP Boot private data.
22
23 @retval EFI_SUCCESS HTTP Boot Callback Protocol installed succesfully.
24 @retval Others Failed to install HTTP Boot Callback Protocol.
25
26 **/
27 EFI_STATUS
28 HttpBootInstallCallback (
29 IN HTTP_BOOT_PRIVATE_DATA *Private
30 )
31 {
32 EFI_STATUS Status;
33 EFI_HANDLE ControllerHandle;
34
35 if (!Private->UsingIpv6) {
36 ControllerHandle = Private->Ip4Nic->Controller;
37 } else {
38 ControllerHandle = Private->Ip6Nic->Controller;
39 }
40
41 //
42 // Check whether gEfiHttpBootCallbackProtocolGuid already installed.
43 //
44 Status = gBS->HandleProtocol (
45 ControllerHandle,
46 &gEfiHttpBootCallbackProtocolGuid,
47 (VOID **) &Private->HttpBootCallback
48 );
49 if (Status == EFI_UNSUPPORTED) {
50
51 CopyMem (
52 &Private->LoadFileCallback,
53 &gHttpBootDxeHttpBootCallback,
54 sizeof (EFI_HTTP_BOOT_CALLBACK_PROTOCOL)
55 );
56
57 //
58 // Install a default callback if user didn't offer one.
59 //
60 Status = gBS->InstallProtocolInterface (
61 &ControllerHandle,
62 &gEfiHttpBootCallbackProtocolGuid,
63 EFI_NATIVE_INTERFACE,
64 &Private->LoadFileCallback
65 );
66 if (EFI_ERROR (Status)) {
67 return Status;
68 }
69 Private->HttpBootCallback = &Private->LoadFileCallback;
70 }
71
72 return EFI_SUCCESS;
73 }
74
75 /**
76 Uninstall HTTP Boot Callback Protocol if it's installed by this driver.
77
78 @param[in] Private Pointer to HTTP Boot private data.
79
80 **/
81 VOID
82 HttpBootUninstallCallback (
83 IN HTTP_BOOT_PRIVATE_DATA *Private
84 )
85 {
86 if (Private->HttpBootCallback == &Private->LoadFileCallback) {
87 gBS->UninstallProtocolInterface (
88 Private->Controller,
89 &gEfiHttpBootCallbackProtocolGuid,
90 &Private->HttpBootCallback
91 );
92 Private->HttpBootCallback = NULL;
93 }
94 }
95
96 /**
97 Enable the use of UEFI HTTP boot function.
98
99 If the driver has already been started but not satisfy the requirement (IP stack and
100 specified boot file path), this function will stop the driver and start it again.
101
102 @param[in] Private The pointer to the driver's private data.
103 @param[in] UsingIpv6 Specifies the type of IP addresses that are to be
104 used during the session that is being started.
105 Set to TRUE for IPv6, and FALSE for IPv4.
106 @param[in] FilePath The device specific path of the file to load.
107
108 @retval EFI_SUCCESS HTTP boot was successfully enabled.
109 @retval EFI_INVALID_PARAMETER Private is NULL or FilePath is NULL.
110 @retval EFI_INVALID_PARAMETER The FilePath doesn't contain a valid URI device path node.
111 @retval EFI_ALREADY_STARTED The driver is already in started state.
112 @retval EFI_OUT_OF_RESOURCES There are not enough resources.
113
114 **/
115 EFI_STATUS
116 HttpBootStart (
117 IN HTTP_BOOT_PRIVATE_DATA *Private,
118 IN BOOLEAN UsingIpv6,
119 IN EFI_DEVICE_PATH_PROTOCOL *FilePath
120 )
121 {
122 UINTN Index;
123 EFI_STATUS Status;
124 CHAR8 *Uri;
125
126
127 if (Private == NULL || FilePath == NULL) {
128 return EFI_INVALID_PARAMETER;
129 }
130
131 //
132 // Check the URI in the input FilePath, in order to see whether it is
133 // required to boot from a new specified boot file.
134 //
135 Status = HttpBootParseFilePath (FilePath, &Uri);
136 if (EFI_ERROR (Status)) {
137 return EFI_INVALID_PARAMETER;
138 }
139
140 //
141 // Check whether we need to stop and restart the HTTP boot driver.
142 //
143 if (Private->Started) {
144 //
145 // Restart is needed in 2 cases:
146 // 1. Http boot driver has already been started but not on the required IP stack.
147 // 2. The specified boot file URI in FilePath is different with the one we have
148 // recorded before.
149 //
150 if ((UsingIpv6 != Private->UsingIpv6) ||
151 ((Uri != NULL) && (AsciiStrCmp (Private->BootFileUri, Uri) != 0))) {
152 //
153 // Restart is required, first stop then continue this start function.
154 //
155 Status = HttpBootStop (Private);
156 if (EFI_ERROR (Status)) {
157 return Status;
158 }
159 } else {
160 //
161 // Restart is not required.
162 //
163 if (Uri != NULL) {
164 FreePool (Uri);
165 }
166 return EFI_ALREADY_STARTED;
167 }
168 }
169
170 //
171 // Detect whether using ipv6 or not, and set it to the private data.
172 //
173 if (UsingIpv6 && Private->Ip6Nic != NULL) {
174 Private->UsingIpv6 = TRUE;
175 } else if (!UsingIpv6 && Private->Ip4Nic != NULL) {
176 Private->UsingIpv6 = FALSE;
177 } else {
178 if (Uri != NULL) {
179 FreePool (Uri);
180 }
181 return EFI_UNSUPPORTED;
182 }
183
184 //
185 // Record the specified URI and prepare the URI parser if needed.
186 //
187 Private->FilePathUri = Uri;
188 if (Private->FilePathUri != NULL) {
189 Status = HttpParseUrl (
190 Private->FilePathUri,
191 (UINT32) AsciiStrLen (Private->FilePathUri),
192 FALSE,
193 &Private->FilePathUriParser
194 );
195 if (EFI_ERROR (Status)) {
196 FreePool (Private->FilePathUri);
197 return Status;
198 }
199 }
200
201 //
202 // Init the content of cached DHCP offer list.
203 //
204 ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));
205 if (!Private->UsingIpv6) {
206 for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
207 Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = HTTP_CACHED_DHCP4_PACKET_MAX_SIZE;
208 }
209 } else {
210 for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
211 Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = HTTP_CACHED_DHCP6_PACKET_MAX_SIZE;
212 }
213 }
214
215 if (Private->UsingIpv6) {
216 //
217 // Set Ip6 policy to Automatic to start the Ip6 router discovery.
218 //
219 Status = HttpBootSetIp6Policy (Private);
220 if (EFI_ERROR (Status)) {
221 return Status;
222 }
223 }
224 Private->Started = TRUE;
225 Print (L"\n>>Start HTTP Boot over IPv%d", Private->UsingIpv6 ? 6 : 4);
226
227 return EFI_SUCCESS;
228 }
229
230 /**
231 Attempt to complete a DHCPv4 D.O.R.A or DHCPv6 S.R.A.A sequence to retrieve the boot resource information.
232
233 @param[in] Private The pointer to the driver's private data.
234
235 @retval EFI_SUCCESS Boot info was successfully retrieved.
236 @retval EFI_INVALID_PARAMETER Private is NULL.
237 @retval EFI_NOT_STARTED The driver is in stopped state.
238 @retval EFI_DEVICE_ERROR An unexpected network error occurred.
239 @retval Others Other errors as indicated.
240
241 **/
242 EFI_STATUS
243 HttpBootDhcp (
244 IN HTTP_BOOT_PRIVATE_DATA *Private
245 )
246 {
247 EFI_STATUS Status;
248
249 if (Private == NULL) {
250 return EFI_INVALID_PARAMETER;
251 }
252
253 if (!Private->Started) {
254 return EFI_NOT_STARTED;
255 }
256
257 Status = EFI_DEVICE_ERROR;
258
259 if (!Private->UsingIpv6) {
260 //
261 // Start D.O.R.A process to get a IPv4 address and other boot information.
262 //
263 Status = HttpBootDhcp4Dora (Private);
264 } else {
265 //
266 // Start S.A.R.R process to get a IPv6 address and other boot information.
267 //
268 Status = HttpBootDhcp6Sarr (Private);
269 }
270
271 return Status;
272 }
273
274 /**
275 Attempt to download the boot file through HTTP message exchange.
276
277 @param[in] Private The pointer to the driver's private data.
278 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
279 code of EFI_SUCCESS, the amount of data transferred to
280 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
281 the size of Buffer required to retrieve the requested file.
282 @param[in] Buffer The memory buffer to transfer the file to. If Buffer is NULL,
283 then the size of the requested file is returned in
284 BufferSize.
285 @param[out] ImageType The image type of the downloaded file.
286
287 @retval EFI_SUCCESS Boot file was loaded successfully.
288 @retval EFI_INVALID_PARAMETER Private is NULL, or ImageType is NULL, or BufferSize is NULL.
289 @retval EFI_INVALID_PARAMETER *BufferSize is not zero, and Buffer is NULL.
290 @retval EFI_NOT_STARTED The driver is in stopped state.
291 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the boot file. BufferSize has
292 been updated with the size needed to complete the request.
293 @retval EFI_DEVICE_ERROR An unexpected network error occurred.
294 @retval Others Other errors as indicated.
295
296 **/
297 EFI_STATUS
298 HttpBootLoadFile (
299 IN HTTP_BOOT_PRIVATE_DATA *Private,
300 IN OUT UINTN *BufferSize,
301 IN VOID *Buffer, OPTIONAL
302 OUT HTTP_BOOT_IMAGE_TYPE *ImageType
303 )
304 {
305 EFI_STATUS Status;
306
307 if (Private == NULL || ImageType == NULL || BufferSize == NULL ) {
308 return EFI_INVALID_PARAMETER;
309 }
310
311 if (*BufferSize != 0 && Buffer == NULL) {
312 return EFI_INVALID_PARAMETER;
313 }
314
315 if (!Private->Started) {
316 return EFI_NOT_STARTED;
317 }
318
319 Status = HttpBootInstallCallback (Private);
320 if (EFI_ERROR(Status)) {
321 goto ON_EXIT;
322 }
323
324 if (Private->BootFileUri == NULL) {
325 //
326 // Parse the cached offer to get the boot file URL first.
327 //
328 Status = HttpBootDiscoverBootInfo (Private);
329 if (EFI_ERROR (Status)) {
330 goto ON_EXIT;
331 }
332 }
333
334 if (!Private->HttpCreated) {
335 //
336 // Create HTTP child.
337 //
338 Status = HttpBootCreateHttpIo (Private);
339 if (EFI_ERROR (Status)) {
340 goto ON_EXIT;
341 }
342 }
343
344 if (Private->BootFileSize == 0) {
345 //
346 // Discover the information about the bootfile if we haven't.
347 //
348
349 //
350 // Try to use HTTP HEAD method.
351 //
352 Status = HttpBootGetBootFile (
353 Private,
354 TRUE,
355 &Private->BootFileSize,
356 NULL,
357 &Private->ImageType
358 );
359 if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
360 //
361 // Failed to get file size by HEAD method, may be trunked encoding, try HTTP GET method.
362 //
363 ASSERT (Private->BootFileSize == 0);
364 Status = HttpBootGetBootFile (
365 Private,
366 FALSE,
367 &Private->BootFileSize,
368 NULL,
369 &Private->ImageType
370 );
371 if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
372 goto ON_EXIT;
373 }
374 }
375 }
376
377 if (*BufferSize < Private->BootFileSize) {
378 *BufferSize = Private->BootFileSize;
379 *ImageType = Private->ImageType;
380 Status = EFI_BUFFER_TOO_SMALL;
381 goto ON_EXIT;
382 }
383
384 //
385 // Load the boot file into Buffer
386 //
387 Status = HttpBootGetBootFile (
388 Private,
389 FALSE,
390 BufferSize,
391 Buffer,
392 ImageType
393 );
394
395 ON_EXIT:
396 HttpBootUninstallCallback (Private);
397 return Status;
398 }
399
400 /**
401 Disable the use of UEFI HTTP boot function.
402
403 @param[in] Private The pointer to the driver's private data.
404
405 @retval EFI_SUCCESS HTTP boot was successfully disabled.
406 @retval EFI_NOT_STARTED The driver is already in stopped state.
407 @retval EFI_INVALID_PARAMETER Private is NULL.
408 @retval Others Unexpected error when stop the function.
409
410 **/
411 EFI_STATUS
412 HttpBootStop (
413 IN HTTP_BOOT_PRIVATE_DATA *Private
414 )
415 {
416 UINTN Index;
417
418 if (Private == NULL) {
419 return EFI_INVALID_PARAMETER;
420 }
421
422 if (!Private->Started) {
423 return EFI_NOT_STARTED;
424 }
425
426 if (Private->HttpCreated) {
427 HttpIoDestroyIo (&Private->HttpIo);
428 Private->HttpCreated = FALSE;
429 }
430
431 Private->Started = FALSE;
432 ZeroMem (&Private->StationIp, sizeof (EFI_IP_ADDRESS));
433 ZeroMem (&Private->SubnetMask, sizeof (EFI_IP_ADDRESS));
434 ZeroMem (&Private->GatewayIp, sizeof (EFI_IP_ADDRESS));
435 Private->Port = 0;
436 Private->BootFileUri = NULL;
437 Private->BootFileUriParser = NULL;
438 Private->BootFileSize = 0;
439 Private->SelectIndex = 0;
440 Private->SelectProxyType = HttpOfferTypeMax;
441
442 if (!Private->UsingIpv6) {
443 //
444 // Stop and release the DHCP4 child.
445 //
446 Private->Dhcp4->Stop (Private->Dhcp4);
447 Private->Dhcp4->Configure (Private->Dhcp4, NULL);
448
449 for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
450 if (Private->OfferBuffer[Index].Dhcp4.UriParser) {
451 HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp4.UriParser);
452 }
453 }
454 } else {
455 //
456 // Stop and release the DHCP6 child.
457 //
458 Private->Dhcp6->Stop (Private->Dhcp6);
459 Private->Dhcp6->Configure (Private->Dhcp6, NULL);
460
461 for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
462 if (Private->OfferBuffer[Index].Dhcp6.UriParser) {
463 HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp6.UriParser);
464 }
465 }
466 }
467
468 if (Private->DnsServerIp != NULL) {
469 FreePool (Private->DnsServerIp);
470 Private->DnsServerIp = NULL;
471 }
472
473 if (Private->FilePathUri!= NULL) {
474 FreePool (Private->FilePathUri);
475 HttpUrlFreeParser (Private->FilePathUriParser);
476 Private->FilePathUri = NULL;
477 Private->FilePathUriParser = NULL;
478 }
479
480 ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));
481 Private->OfferNum = 0;
482 ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
483 ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
484
485 HttpBootFreeCacheList (Private);
486
487 return EFI_SUCCESS;
488 }
489
490 /**
491 Causes the driver to load a specified file.
492
493 @param This Protocol instance pointer.
494 @param FilePath The device specific path of the file to load.
495 @param BootPolicy If TRUE, indicates that the request originates from the
496 boot manager is attempting to load FilePath as a boot
497 selection. If FALSE, then FilePath must match as exact file
498 to be loaded.
499 @param BufferSize On input the size of Buffer in bytes. On output with a return
500 code of EFI_SUCCESS, the amount of data transferred to
501 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
502 the size of Buffer required to retrieve the requested file.
503 @param Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
504 then the size of the requested file is returned in
505 BufferSize.
506
507 @retval EFI_SUCCESS The file was loaded.
508 @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy
509 @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
510 BufferSize is NULL.
511 @retval EFI_NO_MEDIA No medium was present to load the file.
512 @retval EFI_DEVICE_ERROR The file was not loaded due to a device error.
513 @retval EFI_NO_RESPONSE The remote system did not respond.
514 @retval EFI_NOT_FOUND The file was not found.
515 @retval EFI_ABORTED The file load process was manually cancelled.
516 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
517 BufferSize has been updated with the size needed to complete
518 the request.
519
520 **/
521 EFI_STATUS
522 EFIAPI
523 HttpBootDxeLoadFile (
524 IN EFI_LOAD_FILE_PROTOCOL *This,
525 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
526 IN BOOLEAN BootPolicy,
527 IN OUT UINTN *BufferSize,
528 IN VOID *Buffer OPTIONAL
529 )
530 {
531 HTTP_BOOT_PRIVATE_DATA *Private;
532 HTTP_BOOT_VIRTUAL_NIC *VirtualNic;
533 BOOLEAN MediaPresent;
534 BOOLEAN UsingIpv6;
535 EFI_STATUS Status;
536 HTTP_BOOT_IMAGE_TYPE ImageType;
537
538 if (This == NULL || BufferSize == NULL || FilePath == NULL) {
539 return EFI_INVALID_PARAMETER;
540 }
541
542 //
543 // Only support BootPolicy
544 //
545 if (!BootPolicy) {
546 return EFI_UNSUPPORTED;
547 }
548
549 VirtualNic = HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE (This);
550 Private = VirtualNic->Private;
551
552 //
553 // Check media status before HTTP boot start
554 //
555 MediaPresent = TRUE;
556 NetLibDetectMedia (Private->Controller, &MediaPresent);
557 if (!MediaPresent) {
558 return EFI_NO_MEDIA;
559 }
560
561 //
562 // Check whether the virtual nic is using IPv6 or not.
563 //
564 UsingIpv6 = FALSE;
565 if (VirtualNic == Private->Ip6Nic) {
566 UsingIpv6 = TRUE;
567 }
568
569 //
570 // Initialize HTTP boot.
571 //
572 Status = HttpBootStart (Private, UsingIpv6, FilePath);
573 if (Status != EFI_SUCCESS && Status != EFI_ALREADY_STARTED) {
574 return Status;
575 }
576
577 //
578 // Load the boot file.
579 //
580 ImageType = ImageTypeMax;
581 Status = HttpBootLoadFile (Private, BufferSize, Buffer, &ImageType);
582 if (EFI_ERROR (Status)) {
583 if (Status == EFI_BUFFER_TOO_SMALL && (ImageType == ImageTypeVirtualCd || ImageType == ImageTypeVirtualDisk)) {
584 Status = EFI_WARN_FILE_SYSTEM;
585 } else if (Status != EFI_BUFFER_TOO_SMALL) {
586 HttpBootStop (Private);
587 }
588 return Status;
589 }
590
591 //
592 // Register the RAM Disk to the system if needed.
593 //
594 if (ImageType == ImageTypeVirtualCd || ImageType == ImageTypeVirtualDisk) {
595 Status = HttpBootRegisterRamDisk (Private, *BufferSize, Buffer, ImageType);
596 if (!EFI_ERROR (Status)) {
597 Status = EFI_WARN_FILE_SYSTEM;
598 }
599 }
600
601 //
602 // Stop the HTTP Boot service after the boot image is downloaded.
603 //
604 HttpBootStop (Private);
605 return Status;
606 }
607
608 ///
609 /// Load File Protocol instance
610 ///
611 GLOBAL_REMOVE_IF_UNREFERENCED
612 EFI_LOAD_FILE_PROTOCOL gHttpBootDxeLoadFile = {
613 HttpBootDxeLoadFile
614 };
615
616 /**
617 Callback function that is invoked when the HTTP Boot driver is about to transmit or has received a
618 packet.
619
620 This function is invoked when the HTTP Boot driver is about to transmit or has received packet.
621 Parameters DataType and Received specify the type of event and the format of the buffer pointed
622 to by Data. Due to the polling nature of UEFI device drivers, this callback function should not
623 execute for more than 5 ms.
624 The returned status code determines the behavior of the HTTP Boot driver.
625
626 @param[in] This Pointer to the EFI_HTTP_BOOT_CALLBACK_PROTOCOL instance.
627 @param[in] DataType The event that occurs in the current state.
628 @param[in] Received TRUE if the callback is being invoked due to a receive event.
629 FALSE if the callback is being invoked due to a transmit event.
630 @param[in] DataLength The length in bytes of the buffer pointed to by Data.
631 @param[in] Data A pointer to the buffer of data, the data type is specified by
632 DataType.
633
634 @retval EFI_SUCCESS Tells the HTTP Boot driver to continue the HTTP Boot process.
635 @retval EFI_ABORTED Tells the HTTP Boot driver to abort the current HTTP Boot process.
636 **/
637 EFI_STATUS
638 EFIAPI
639 HttpBootCallback (
640 IN EFI_HTTP_BOOT_CALLBACK_PROTOCOL *This,
641 IN EFI_HTTP_BOOT_CALLBACK_DATA_TYPE DataType,
642 IN BOOLEAN Received,
643 IN UINT32 DataLength,
644 IN VOID *Data OPTIONAL
645 )
646 {
647 EFI_HTTP_MESSAGE *HttpMessage;
648 EFI_HTTP_HEADER *HttpHeader;
649 HTTP_BOOT_PRIVATE_DATA *Private;
650 UINT32 Percentage;
651
652 Private = HTTP_BOOT_PRIVATE_DATA_FROM_CALLBACK_PROTOCOL(This);
653
654 switch (DataType) {
655 case HttpBootDhcp4:
656 case HttpBootDhcp6:
657 Print (L".");
658 break;
659
660 case HttpBootHttpRequest:
661 if (Data != NULL) {
662 HttpMessage = (EFI_HTTP_MESSAGE *) Data;
663 if (HttpMessage->Data.Request->Method == HttpMethodGet &&
664 HttpMessage->Data.Request->Url != NULL) {
665 Print (L"\n URI: %s\n", HttpMessage->Data.Request->Url);
666 }
667 }
668 break;
669
670 case HttpBootHttpResponse:
671 if (Data != NULL) {
672 HttpMessage = (EFI_HTTP_MESSAGE *) Data;
673
674 if (HttpMessage->Data.Response != NULL) {
675 if (HttpBootIsHttpRedirectStatusCode (HttpMessage->Data.Response->StatusCode)) {
676 //
677 // Server indicates the resource has been redirected to a different URL
678 // according to the section 6.4 of RFC7231 and the RFC 7538.
679 // Display the redirect information on the screen.
680 //
681 HttpHeader = HttpFindHeader (
682 HttpMessage->HeaderCount,
683 HttpMessage->Headers,
684 HTTP_HEADER_LOCATION
685 );
686 if (HttpHeader != NULL) {
687 Print (L"\n HTTP ERROR: Resource Redirected.\n New Location: %a\n", HttpHeader->FieldValue);
688 }
689 }
690 }
691
692 HttpHeader = HttpFindHeader (
693 HttpMessage->HeaderCount,
694 HttpMessage->Headers,
695 HTTP_HEADER_CONTENT_LENGTH
696 );
697 if (HttpHeader != NULL) {
698 Private->FileSize = AsciiStrDecimalToUintn (HttpHeader->FieldValue);
699 Private->ReceivedSize = 0;
700 Private->Percentage = 0;
701 }
702 }
703 break;
704
705 case HttpBootHttpEntityBody:
706 if (DataLength != 0) {
707 if (Private->FileSize != 0) {
708 //
709 // We already know the file size, print in percentage format.
710 //
711 if (Private->ReceivedSize == 0) {
712 Print (L" File Size: %lu Bytes\n", Private->FileSize);
713 }
714 Private->ReceivedSize += DataLength;
715 Percentage = (UINT32) DivU64x64Remainder (MultU64x32 (Private->ReceivedSize, 100), Private->FileSize, NULL);
716 if (Private->Percentage != Percentage) {
717 Private->Percentage = Percentage;
718 Print (L"\r Downloading...%d%%", Percentage);
719 }
720 } else {
721 //
722 // In some case we couldn't get the file size from the HTTP header, so we
723 // just print the downloaded file size.
724 //
725 Private->ReceivedSize += DataLength;
726 Print (L"\r Downloading...%lu Bytes", Private->ReceivedSize);
727 }
728 }
729 break;
730
731 default:
732 break;
733 };
734
735 return EFI_SUCCESS;
736 }
737
738 ///
739 /// HTTP Boot Callback Protocol instance
740 ///
741 GLOBAL_REMOVE_IF_UNREFERENCED
742 EFI_HTTP_BOOT_CALLBACK_PROTOCOL gHttpBootDxeHttpBootCallback = {
743 HttpBootCallback
744 };