VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/NetworkPkg/HttpBootDxe/HttpBootImpl.c

Last change on this file was 108794, checked in by vboxsync, 2 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette