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