VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c@ 108794

Last change on this file since 108794 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: 83.7 KB
Line 
1/** @file
2 Library functions which relates with booting.
3
4Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
5Copyright (c) 2011 - 2021, Intel Corporation. All rights reserved.<BR>
6(C) Copyright 2015-2021 Hewlett Packard Enterprise Development LP<BR>
7SPDX-License-Identifier: BSD-2-Clause-Patent
8
9**/
10
11#include "InternalBm.h"
12
13EFI_RAM_DISK_PROTOCOL *mRamDisk = NULL;
14
15EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION mBmRefreshLegacyBootOption = NULL;
16EFI_BOOT_MANAGER_LEGACY_BOOT mBmLegacyBoot = NULL;
17
18///
19/// This GUID is used for an EFI Variable that stores the front device pathes
20/// for a partial device path that starts with the HD node.
21///
22EFI_GUID mBmHardDriveBootVariableGuid = {
23 0xfab7e9e1, 0x39dd, 0x4f2b, { 0x84, 0x08, 0xe2, 0x0e, 0x90, 0x6c, 0xb6, 0xde }
24};
25EFI_GUID mBmAutoCreateBootOptionGuid = {
26 0x8108ac4e, 0x9f11, 0x4d59, { 0x85, 0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2 }
27};
28
29/**
30
31 End Perf entry of BDS
32
33 @param Event The triggered event.
34 @param Context Context for this event.
35
36**/
37VOID
38EFIAPI
39BmEndOfBdsPerfCode (
40 IN EFI_EVENT Event,
41 IN VOID *Context
42 )
43{
44 //
45 // Record the performance data for End of BDS
46 //
47 PERF_CROSSMODULE_END ("BDS");
48
49 return;
50}
51
52/**
53 The function registers the legacy boot support capabilities.
54
55 @param RefreshLegacyBootOption The function pointer to create all the legacy boot options.
56 @param LegacyBoot The function pointer to boot the legacy boot option.
57**/
58VOID
59EFIAPI
60EfiBootManagerRegisterLegacyBootSupport (
61 EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION RefreshLegacyBootOption,
62 EFI_BOOT_MANAGER_LEGACY_BOOT LegacyBoot
63 )
64{
65 mBmRefreshLegacyBootOption = RefreshLegacyBootOption;
66 mBmLegacyBoot = LegacyBoot;
67}
68
69/**
70 Return TRUE when the boot option is auto-created instead of manually added.
71
72 @param BootOption Pointer to the boot option to check.
73
74 @retval TRUE The boot option is auto-created.
75 @retval FALSE The boot option is manually added.
76**/
77BOOLEAN
78BmIsAutoCreateBootOption (
79 EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
80 )
81{
82 if ((BootOption->OptionalDataSize == sizeof (EFI_GUID)) &&
83 CompareGuid ((EFI_GUID *)BootOption->OptionalData, &mBmAutoCreateBootOptionGuid)
84 )
85 {
86 return TRUE;
87 } else {
88 return FALSE;
89 }
90}
91
92/**
93 Find the boot option in the NV storage and return the option number.
94
95 @param OptionToFind Boot option to be checked.
96
97 @return The option number of the found boot option.
98
99**/
100UINTN
101BmFindBootOptionInVariable (
102 IN EFI_BOOT_MANAGER_LOAD_OPTION *OptionToFind
103 )
104{
105 EFI_STATUS Status;
106 EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
107 UINTN OptionNumber;
108 CHAR16 OptionName[BM_OPTION_NAME_LEN];
109 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
110 UINTN BootOptionCount;
111 UINTN Index;
112
113 OptionNumber = LoadOptionNumberUnassigned;
114
115 //
116 // Try to match the variable exactly if the option number is assigned
117 //
118 if (OptionToFind->OptionNumber != LoadOptionNumberUnassigned) {
119 UnicodeSPrint (
120 OptionName,
121 sizeof (OptionName),
122 L"%s%04x",
123 mBmLoadOptionName[OptionToFind->OptionType],
124 OptionToFind->OptionNumber
125 );
126 Status = EfiBootManagerVariableToLoadOption (OptionName, &BootOption);
127
128 if (!EFI_ERROR (Status)) {
129 ASSERT (OptionToFind->OptionNumber == BootOption.OptionNumber);
130 if ((OptionToFind->Attributes == BootOption.Attributes) &&
131 (StrCmp (OptionToFind->Description, BootOption.Description) == 0) &&
132 (CompareMem (OptionToFind->FilePath, BootOption.FilePath, GetDevicePathSize (OptionToFind->FilePath)) == 0) &&
133 (OptionToFind->OptionalDataSize == BootOption.OptionalDataSize) &&
134 (CompareMem (OptionToFind->OptionalData, BootOption.OptionalData, OptionToFind->OptionalDataSize) == 0)
135 )
136 {
137 OptionNumber = OptionToFind->OptionNumber;
138 }
139
140 EfiBootManagerFreeLoadOption (&BootOption);
141 }
142 }
143
144 //
145 // The option number assigned is either incorrect or unassigned.
146 //
147 if (OptionNumber == LoadOptionNumberUnassigned) {
148 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
149
150 // Only assert if the BootOption is non-zero
151 if ((BootOptions == NULL) && (BootOptionCount > 0)) {
152 ASSERT (BootOptions != NULL);
153 return LoadOptionNumberUnassigned;
154 }
155
156 Index = EfiBootManagerFindLoadOption (OptionToFind, BootOptions, BootOptionCount);
157 if (Index != -1) {
158 OptionNumber = BootOptions[Index].OptionNumber;
159 }
160
161 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
162 }
163
164 return OptionNumber;
165}
166
167/**
168 Return the correct FV file path.
169 FV address may change across reboot. This routine promises the FV file device path is right.
170
171 @param FilePath The Memory Mapped Device Path to get the file buffer.
172
173 @return The updated FV Device Path pointint to the file.
174**/
175EFI_DEVICE_PATH_PROTOCOL *
176BmAdjustFvFilePath (
177 IN EFI_DEVICE_PATH_PROTOCOL *FilePath
178 )
179{
180 EFI_STATUS Status;
181 UINTN Index;
182 EFI_DEVICE_PATH_PROTOCOL *FvFileNode;
183 EFI_HANDLE FvHandle;
184 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
185 UINTN FvHandleCount;
186 EFI_HANDLE *FvHandles;
187 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
188 EFI_DEVICE_PATH_PROTOCOL *FullPath;
189
190 //
191 // Get the file buffer by using the exactly FilePath.
192 //
193 FvFileNode = FilePath;
194 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &FvFileNode, &FvHandle);
195 if (!EFI_ERROR (Status)) {
196 return DuplicateDevicePath (FilePath);
197 }
198
199 //
200 // Only wide match other FVs if it's a memory mapped FV file path.
201 //
202 if ((DevicePathType (FilePath) != HARDWARE_DEVICE_PATH) || (DevicePathSubType (FilePath) != HW_MEMMAP_DP)) {
203 return NULL;
204 }
205
206 FvFileNode = NextDevicePathNode (FilePath);
207
208 //
209 // Firstly find the FV file in current FV
210 //
211 gBS->HandleProtocol (
212 gImageHandle,
213 &gEfiLoadedImageProtocolGuid,
214 (VOID **)&LoadedImage
215 );
216 NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), FvFileNode);
217 FullPath = BmAdjustFvFilePath (NewDevicePath);
218 FreePool (NewDevicePath);
219 if (FullPath != NULL) {
220 return FullPath;
221 }
222
223 //
224 // Secondly find the FV file in all other FVs
225 //
226 gBS->LocateHandleBuffer (
227 ByProtocol,
228 &gEfiFirmwareVolume2ProtocolGuid,
229 NULL,
230 &FvHandleCount,
231 &FvHandles
232 );
233 for (Index = 0; Index < FvHandleCount; Index++) {
234 if (FvHandles[Index] == LoadedImage->DeviceHandle) {
235 //
236 // Skip current FV, it was handed in first step.
237 //
238 continue;
239 }
240
241 NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandles[Index]), FvFileNode);
242 FullPath = BmAdjustFvFilePath (NewDevicePath);
243 FreePool (NewDevicePath);
244 if (FullPath != NULL) {
245 break;
246 }
247 }
248
249 if (FvHandles != NULL) {
250 FreePool (FvHandles);
251 }
252
253 return FullPath;
254}
255
256/**
257 Check if it's a Device Path pointing to FV file.
258
259 The function doesn't garentee the device path points to existing FV file.
260
261 @param DevicePath Input device path.
262
263 @retval TRUE The device path is a FV File Device Path.
264 @retval FALSE The device path is NOT a FV File Device Path.
265**/
266BOOLEAN
267BmIsFvFilePath (
268 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
269 )
270{
271 EFI_STATUS Status;
272 EFI_HANDLE Handle;
273 EFI_DEVICE_PATH_PROTOCOL *Node;
274
275 Node = DevicePath;
276 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &Handle);
277 if (!EFI_ERROR (Status)) {
278 return TRUE;
279 }
280
281 if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) && (DevicePathSubType (DevicePath) == HW_MEMMAP_DP)) {
282 DevicePath = NextDevicePathNode (DevicePath);
283 if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) && (DevicePathSubType (DevicePath) == MEDIA_PIWG_FW_FILE_DP)) {
284 return IsDevicePathEnd (NextDevicePathNode (DevicePath));
285 }
286 }
287
288 return FALSE;
289}
290
291/**
292 Check whether a USB device match the specified USB Class device path. This
293 function follows "Load Option Processing" behavior in UEFI specification.
294
295 @param UsbIo USB I/O protocol associated with the USB device.
296 @param UsbClass The USB Class device path to match.
297
298 @retval TRUE The USB device match the USB Class device path.
299 @retval FALSE The USB device does not match the USB Class device path.
300
301**/
302BOOLEAN
303BmMatchUsbClass (
304 IN EFI_USB_IO_PROTOCOL *UsbIo,
305 IN USB_CLASS_DEVICE_PATH *UsbClass
306 )
307{
308 EFI_STATUS Status;
309 EFI_USB_DEVICE_DESCRIPTOR DevDesc;
310 EFI_USB_INTERFACE_DESCRIPTOR IfDesc;
311 UINT8 DeviceClass;
312 UINT8 DeviceSubClass;
313 UINT8 DeviceProtocol;
314
315 if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) ||
316 (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP))
317 {
318 return FALSE;
319 }
320
321 //
322 // Check Vendor Id and Product Id.
323 //
324 Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
325 if (EFI_ERROR (Status)) {
326 return FALSE;
327 }
328
329 if ((UsbClass->VendorId != 0xffff) &&
330 (UsbClass->VendorId != DevDesc.IdVendor))
331 {
332 return FALSE;
333 }
334
335 if ((UsbClass->ProductId != 0xffff) &&
336 (UsbClass->ProductId != DevDesc.IdProduct))
337 {
338 return FALSE;
339 }
340
341 DeviceClass = DevDesc.DeviceClass;
342 DeviceSubClass = DevDesc.DeviceSubClass;
343 DeviceProtocol = DevDesc.DeviceProtocol;
344 if (DeviceClass == 0) {
345 //
346 // If Class in Device Descriptor is set to 0, use the Class, SubClass and
347 // Protocol in Interface Descriptor instead.
348 //
349 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);
350 if (EFI_ERROR (Status)) {
351 return FALSE;
352 }
353
354 DeviceClass = IfDesc.InterfaceClass;
355 DeviceSubClass = IfDesc.InterfaceSubClass;
356 DeviceProtocol = IfDesc.InterfaceProtocol;
357 }
358
359 //
360 // Check Class, SubClass and Protocol.
361 //
362 if ((UsbClass->DeviceClass != 0xff) &&
363 (UsbClass->DeviceClass != DeviceClass))
364 {
365 return FALSE;
366 }
367
368 if ((UsbClass->DeviceSubClass != 0xff) &&
369 (UsbClass->DeviceSubClass != DeviceSubClass))
370 {
371 return FALSE;
372 }
373
374 if ((UsbClass->DeviceProtocol != 0xff) &&
375 (UsbClass->DeviceProtocol != DeviceProtocol))
376 {
377 return FALSE;
378 }
379
380 return TRUE;
381}
382
383/**
384 Check whether a USB device match the specified USB WWID device path. This
385 function follows "Load Option Processing" behavior in UEFI specification.
386
387 @param UsbIo USB I/O protocol associated with the USB device.
388 @param UsbWwid The USB WWID device path to match.
389
390 @retval TRUE The USB device match the USB WWID device path.
391 @retval FALSE The USB device does not match the USB WWID device path.
392
393**/
394BOOLEAN
395BmMatchUsbWwid (
396 IN EFI_USB_IO_PROTOCOL *UsbIo,
397 IN USB_WWID_DEVICE_PATH *UsbWwid
398 )
399{
400 EFI_STATUS Status;
401 EFI_USB_DEVICE_DESCRIPTOR DevDesc;
402 EFI_USB_INTERFACE_DESCRIPTOR IfDesc;
403 UINT16 *LangIdTable;
404 UINT16 TableSize;
405 UINT16 Index;
406 CHAR16 *CompareStr;
407 UINTN CompareLen;
408 CHAR16 *SerialNumberStr;
409 UINTN Length;
410
411 if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) ||
412 (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP))
413 {
414 return FALSE;
415 }
416
417 //
418 // Check Vendor Id and Product Id.
419 //
420 Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
421 if (EFI_ERROR (Status)) {
422 return FALSE;
423 }
424
425 if ((DevDesc.IdVendor != UsbWwid->VendorId) ||
426 (DevDesc.IdProduct != UsbWwid->ProductId))
427 {
428 return FALSE;
429 }
430
431 //
432 // Check Interface Number.
433 //
434 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);
435 if (EFI_ERROR (Status)) {
436 return FALSE;
437 }
438
439 if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) {
440 return FALSE;
441 }
442
443 //
444 // Check Serial Number.
445 //
446 if (DevDesc.StrSerialNumber == 0) {
447 return FALSE;
448 }
449
450 //
451 // Get all supported languages.
452 //
453 TableSize = 0;
454 LangIdTable = NULL;
455 Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize);
456 if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) {
457 return FALSE;
458 }
459
460 //
461 // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters.
462 //
463 CompareStr = (CHAR16 *)(UINTN)(UsbWwid + 1);
464 CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16);
465 if (CompareStr[CompareLen - 1] == L'\0') {
466 CompareLen--;
467 }
468
469 //
470 // Compare serial number in each supported language.
471 //
472 for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) {
473 SerialNumberStr = NULL;
474 Status = UsbIo->UsbGetStringDescriptor (
475 UsbIo,
476 LangIdTable[Index],
477 DevDesc.StrSerialNumber,
478 &SerialNumberStr
479 );
480 if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) {
481 continue;
482 }
483
484 Length = StrLen (SerialNumberStr);
485 if ((Length >= CompareLen) &&
486 (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0))
487 {
488 FreePool (SerialNumberStr);
489 return TRUE;
490 }
491
492 FreePool (SerialNumberStr);
493 }
494
495 return FALSE;
496}
497
498/**
499 Find a USB device which match the specified short-form device path start with
500 USB Class or USB WWID device path. If ParentDevicePath is NULL, this function
501 will search in all USB devices of the platform. If ParentDevicePath is not NULL,
502 this function will only search in its child devices.
503
504 @param DevicePath The device path that contains USB Class or USB WWID device path.
505 @param ParentDevicePathSize The length of the device path before the USB Class or
506 USB WWID device path.
507 @param UsbIoHandleCount A pointer to the count of the returned USB IO handles.
508
509 @retval NULL The matched USB IO handles cannot be found.
510 @retval other The matched USB IO handles.
511
512**/
513EFI_HANDLE *
514BmFindUsbDevice (
515 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
516 IN UINTN ParentDevicePathSize,
517 OUT UINTN *UsbIoHandleCount
518 )
519{
520 EFI_STATUS Status;
521 EFI_HANDLE *UsbIoHandles;
522 EFI_DEVICE_PATH_PROTOCOL *UsbIoDevicePath;
523 EFI_USB_IO_PROTOCOL *UsbIo;
524 UINTN Index;
525 BOOLEAN Matched;
526
527 ASSERT (UsbIoHandleCount != NULL);
528
529 //
530 // Get all UsbIo Handles.
531 //
532 Status = gBS->LocateHandleBuffer (
533 ByProtocol,
534 &gEfiUsbIoProtocolGuid,
535 NULL,
536 UsbIoHandleCount,
537 &UsbIoHandles
538 );
539 if (EFI_ERROR (Status)) {
540 *UsbIoHandleCount = 0;
541 UsbIoHandles = NULL;
542 }
543
544 for (Index = 0; Index < *UsbIoHandleCount; ) {
545 //
546 // Get the Usb IO interface.
547 //
548 Status = gBS->HandleProtocol (
549 UsbIoHandles[Index],
550 &gEfiUsbIoProtocolGuid,
551 (VOID **)&UsbIo
552 );
553 UsbIoDevicePath = DevicePathFromHandle (UsbIoHandles[Index]);
554 Matched = FALSE;
555 if (!EFI_ERROR (Status) && (UsbIoDevicePath != NULL)) {
556 //
557 // Compare starting part of UsbIoHandle's device path with ParentDevicePath.
558 //
559 if (CompareMem (UsbIoDevicePath, DevicePath, ParentDevicePathSize) == 0) {
560 if (BmMatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *)((UINTN)DevicePath + ParentDevicePathSize)) ||
561 BmMatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *)((UINTN)DevicePath + ParentDevicePathSize)))
562 {
563 Matched = TRUE;
564 }
565 }
566 }
567
568 if (!Matched) {
569 (*UsbIoHandleCount)--;
570 CopyMem (&UsbIoHandles[Index], &UsbIoHandles[Index + 1], (*UsbIoHandleCount - Index) * sizeof (EFI_HANDLE));
571 } else {
572 Index++;
573 }
574 }
575
576 return UsbIoHandles;
577}
578
579/**
580 Expand USB Class or USB WWID device path node to be full device path of a USB
581 device in platform.
582
583 This function support following 4 cases:
584 1) Boot Option device path starts with a USB Class or USB WWID device path,
585 and there is no Media FilePath device path in the end.
586 In this case, it will follow Removable Media Boot Behavior.
587 2) Boot Option device path starts with a USB Class or USB WWID device path,
588 and ended with Media FilePath device path.
589 3) Boot Option device path starts with a full device path to a USB Host Controller,
590 contains a USB Class or USB WWID device path node, while not ended with Media
591 FilePath device path. In this case, it will follow Removable Media Boot Behavior.
592 4) Boot Option device path starts with a full device path to a USB Host Controller,
593 contains a USB Class or USB WWID device path node, and ended with Media
594 FilePath device path.
595
596 @param FilePath The device path pointing to a load option.
597 It could be a short-form device path.
598 @param FullPath The full path returned by the routine in last call.
599 Set to NULL in first call.
600 @param ShortformNode Pointer to the USB short-form device path node in the FilePath buffer.
601
602 @return The next possible full path pointing to the load option.
603 Caller is responsible to free the memory.
604**/
605EFI_DEVICE_PATH_PROTOCOL *
606BmExpandUsbDevicePath (
607 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
608 IN EFI_DEVICE_PATH_PROTOCOL *FullPath,
609 IN EFI_DEVICE_PATH_PROTOCOL *ShortformNode
610 )
611{
612 UINTN ParentDevicePathSize;
613 EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;
614 EFI_DEVICE_PATH_PROTOCOL *NextFullPath;
615 EFI_HANDLE *Handles;
616 UINTN HandleCount;
617 UINTN Index;
618 BOOLEAN GetNext;
619
620 NextFullPath = NULL;
621 GetNext = (BOOLEAN)(FullPath == NULL);
622 ParentDevicePathSize = (UINTN)ShortformNode - (UINTN)FilePath;
623 RemainingDevicePath = NextDevicePathNode (ShortformNode);
624 Handles = BmFindUsbDevice (FilePath, ParentDevicePathSize, &HandleCount);
625
626 for (Index = 0; Index < HandleCount; Index++) {
627 FilePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath);
628 if (FilePath == NULL) {
629 //
630 // Out of memory.
631 //
632 continue;
633 }
634
635 NextFullPath = BmGetNextLoadOptionDevicePath (FilePath, NULL);
636 FreePool (FilePath);
637 if (NextFullPath == NULL) {
638 //
639 // No BlockIo or SimpleFileSystem under FilePath.
640 //
641 continue;
642 }
643
644 if (GetNext) {
645 break;
646 } else {
647 GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
648 FreePool (NextFullPath);
649 NextFullPath = NULL;
650 }
651 }
652
653 if (Handles != NULL) {
654 FreePool (Handles);
655 }
656
657 return NextFullPath;
658}
659
660/**
661 Expand File-path device path node to be full device path in platform.
662
663 @param FilePath The device path pointing to a load option.
664 It could be a short-form device path.
665 @param FullPath The full path returned by the routine in last call.
666 Set to NULL in first call.
667
668 @return The next possible full path pointing to the load option.
669 Caller is responsible to free the memory.
670**/
671EFI_DEVICE_PATH_PROTOCOL *
672BmExpandFileDevicePath (
673 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
674 IN EFI_DEVICE_PATH_PROTOCOL *FullPath
675 )
676{
677 EFI_STATUS Status;
678 UINTN Index;
679 UINTN HandleCount;
680 EFI_HANDLE *Handles;
681 EFI_BLOCK_IO_PROTOCOL *BlockIo;
682 UINTN MediaType;
683 EFI_DEVICE_PATH_PROTOCOL *NextFullPath;
684 BOOLEAN GetNext;
685
686 EfiBootManagerConnectAll ();
687 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &HandleCount, &Handles);
688 if (EFI_ERROR (Status)) {
689 HandleCount = 0;
690 Handles = NULL;
691 }
692
693 GetNext = (BOOLEAN)(FullPath == NULL);
694 NextFullPath = NULL;
695 //
696 // Enumerate all removable media devices followed by all fixed media devices,
697 // followed by media devices which don't layer on block io.
698 //
699 for (MediaType = 0; MediaType < 3; MediaType++) {
700 for (Index = 0; Index < HandleCount; Index++) {
701 Status = gBS->HandleProtocol (Handles[Index], &gEfiBlockIoProtocolGuid, (VOID *)&BlockIo);
702 if (EFI_ERROR (Status)) {
703 BlockIo = NULL;
704 }
705
706 if (((MediaType == 0) && (BlockIo != NULL) && BlockIo->Media->RemovableMedia) ||
707 ((MediaType == 1) && (BlockIo != NULL) && !BlockIo->Media->RemovableMedia) ||
708 ((MediaType == 2) && (BlockIo == NULL))
709 )
710 {
711 NextFullPath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), FilePath);
712 if (GetNext) {
713 break;
714 } else {
715 GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
716 FreePool (NextFullPath);
717 NextFullPath = NULL;
718 }
719 }
720 }
721
722 if (NextFullPath != NULL) {
723 break;
724 }
725 }
726
727 if (Handles != NULL) {
728 FreePool (Handles);
729 }
730
731 return NextFullPath;
732}
733
734/**
735 Expand URI device path node to be full device path in platform.
736
737 @param FilePath The device path pointing to a load option.
738 It could be a short-form device path.
739 @param FullPath The full path returned by the routine in last call.
740 Set to NULL in first call.
741
742 @return The next possible full path pointing to the load option.
743 Caller is responsible to free the memory.
744**/
745EFI_DEVICE_PATH_PROTOCOL *
746BmExpandUriDevicePath (
747 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
748 IN EFI_DEVICE_PATH_PROTOCOL *FullPath
749 )
750{
751 EFI_STATUS Status;
752 UINTN Index;
753 UINTN HandleCount;
754 EFI_HANDLE *Handles;
755 EFI_DEVICE_PATH_PROTOCOL *NextFullPath;
756 EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;
757 BOOLEAN GetNext;
758
759 EfiBootManagerConnectAll ();
760 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &HandleCount, &Handles);
761 if (EFI_ERROR (Status)) {
762 HandleCount = 0;
763 Handles = NULL;
764 }
765
766 NextFullPath = NULL;
767 GetNext = (BOOLEAN)(FullPath == NULL);
768 for (Index = 0; Index < HandleCount; Index++) {
769 NextFullPath = BmExpandLoadFile (Handles[Index], FilePath);
770
771 if (NextFullPath == NULL) {
772 continue;
773 }
774
775 if (GetNext) {
776 break;
777 } else {
778 GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
779 //
780 // Free the resource occupied by the RAM disk.
781 //
782 RamDiskDevicePath = BmGetRamDiskDevicePath (NextFullPath);
783 if (RamDiskDevicePath != NULL) {
784 BmDestroyRamDisk (RamDiskDevicePath);
785 FreePool (RamDiskDevicePath);
786 }
787
788 FreePool (NextFullPath);
789 NextFullPath = NULL;
790 }
791 }
792
793 if (Handles != NULL) {
794 FreePool (Handles);
795 }
796
797 return NextFullPath;
798}
799
800/**
801 Save the partition DevicePath to the CachedDevicePath as the first instance.
802
803 @param CachedDevicePath The device path cache.
804 @param DevicePath The partition device path to be cached.
805**/
806VOID
807BmCachePartitionDevicePath (
808 IN OUT EFI_DEVICE_PATH_PROTOCOL **CachedDevicePath,
809 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
810 )
811{
812 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
813 UINTN Count;
814
815 if (BmMatchDevicePaths (*CachedDevicePath, DevicePath)) {
816 TempDevicePath = *CachedDevicePath;
817 *CachedDevicePath = BmDelPartMatchInstance (*CachedDevicePath, DevicePath);
818 FreePool (TempDevicePath);
819 }
820
821 if (*CachedDevicePath == NULL) {
822 *CachedDevicePath = DuplicateDevicePath (DevicePath);
823 return;
824 }
825
826 TempDevicePath = *CachedDevicePath;
827 *CachedDevicePath = AppendDevicePathInstance (DevicePath, *CachedDevicePath);
828 if (TempDevicePath != NULL) {
829 FreePool (TempDevicePath);
830 }
831
832 //
833 // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller
834 // If the user try to boot many OS in different HDs or partitions, in theory, the 'HDDP' variable maybe become larger and larger.
835 //
836 Count = 0;
837 TempDevicePath = *CachedDevicePath;
838 while (!IsDevicePathEnd (TempDevicePath)) {
839 TempDevicePath = NextDevicePathNode (TempDevicePath);
840 //
841 // Parse one instance
842 //
843 while (!IsDevicePathEndType (TempDevicePath)) {
844 TempDevicePath = NextDevicePathNode (TempDevicePath);
845 }
846
847 Count++;
848 //
849 // If the CachedDevicePath variable contain too much instance, only remain 12 instances.
850 //
851 if (Count == 12) {
852 SetDevicePathEndNode (TempDevicePath);
853 break;
854 }
855 }
856}
857
858/**
859 Expand a device path that starts with a hard drive media device path node to be a
860 full device path that includes the full hardware path to the device. We need
861 to do this so it can be booted. As an optimization the front match (the part point
862 to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable
863 so a connect all is not required on every boot. All successful history device path
864 which point to partition node (the front part) will be saved.
865
866 @param FilePath The device path pointing to a load option.
867 It could be a short-form device path.
868
869 @return The full device path pointing to the load option.
870**/
871EFI_DEVICE_PATH_PROTOCOL *
872BmExpandPartitionDevicePath (
873 IN EFI_DEVICE_PATH_PROTOCOL *FilePath
874 )
875{
876 EFI_STATUS Status;
877 UINTN BlockIoHandleCount;
878 EFI_HANDLE *BlockIoBuffer;
879 EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath;
880 UINTN Index;
881 EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath;
882 EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath;
883 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
884 EFI_DEVICE_PATH_PROTOCOL *FullPath;
885 UINTN CachedDevicePathSize;
886 BOOLEAN NeedAdjust;
887 EFI_DEVICE_PATH_PROTOCOL *Instance;
888 UINTN Size;
889 BOOLEAN MatchFound;
890 BOOLEAN ConnectAllAttempted;
891
892 //
893 // Check if there is prestore 'HDDP' variable.
894 // If exist, search the front path which point to partition node in the variable instants.
895 // If fail to find or 'HDDP' not exist, reconnect all and search in all system
896 //
897 GetVariable2 (L"HDDP", &mBmHardDriveBootVariableGuid, (VOID **)&CachedDevicePath, &CachedDevicePathSize);
898
899 //
900 // Delete the invalid 'HDDP' variable.
901 //
902 if ((CachedDevicePath != NULL) && !IsDevicePathValid (CachedDevicePath, CachedDevicePathSize)) {
903 FreePool (CachedDevicePath);
904 CachedDevicePath = NULL;
905 Status = gRT->SetVariable (
906 L"HDDP",
907 &mBmHardDriveBootVariableGuid,
908 0,
909 0,
910 NULL
911 );
912 ASSERT_EFI_ERROR (Status);
913 }
914
915 FullPath = NULL;
916 if (CachedDevicePath != NULL) {
917 TempNewDevicePath = CachedDevicePath;
918 NeedAdjust = FALSE;
919 do {
920 //
921 // Check every instance of the variable
922 // First, check whether the instance contain the partition node, which is needed for distinguishing multi
923 // partial partition boot option. Second, check whether the instance could be connected.
924 //
925 Instance = GetNextDevicePathInstance (&TempNewDevicePath, &Size);
926 if (BmMatchPartitionDevicePathNode (Instance, (HARDDRIVE_DEVICE_PATH *)FilePath)) {
927 //
928 // Connect the device path instance, the device path point to hard drive media device path node
929 // e.g. ACPI() /PCI()/ATA()/Partition()
930 //
931 Status = EfiBootManagerConnectDevicePath (Instance, NULL);
932 if (!EFI_ERROR (Status)) {
933 TempDevicePath = AppendDevicePath (Instance, NextDevicePathNode (FilePath));
934 //
935 // TempDevicePath = ACPI()/PCI()/ATA()/Partition()
936 // or = ACPI()/PCI()/ATA()/Partition()/.../A.EFI
937 //
938 // When TempDevicePath = ACPI()/PCI()/ATA()/Partition(),
939 // it may expand to two potienal full paths (nested partition, rarely happen):
940 // 1. ACPI()/PCI()/ATA()/Partition()/Partition(A1)/EFI/BootX64.EFI
941 // 2. ACPI()/PCI()/ATA()/Partition()/Partition(A2)/EFI/BootX64.EFI
942 // For simplicity, only #1 is returned.
943 //
944 FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL);
945 FreePool (TempDevicePath);
946
947 if (FullPath != NULL) {
948 //
949 // Adjust the 'HDDP' instances sequence if the matched one is not first one.
950 //
951 if (NeedAdjust) {
952 BmCachePartitionDevicePath (&CachedDevicePath, Instance);
953 //
954 // Save the matching Device Path so we don't need to do a connect all next time
955 // Failing to save only impacts performance next time expanding the short-form device path
956 //
957 Status = gRT->SetVariable (
958 L"HDDP",
959 &mBmHardDriveBootVariableGuid,
960 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
961 GetDevicePathSize (CachedDevicePath),
962 CachedDevicePath
963 );
964 }
965
966 FreePool (Instance);
967 FreePool (CachedDevicePath);
968 return FullPath;
969 }
970 }
971 }
972
973 //
974 // Come here means the first instance is not matched
975 //
976 NeedAdjust = TRUE;
977 FreePool (Instance);
978 } while (TempNewDevicePath != NULL);
979 }
980
981 //
982 // If we get here we fail to find or 'HDDP' not exist, and now we need
983 // to search all devices in the system for a matched partition
984 //
985 BlockIoBuffer = NULL;
986 MatchFound = FALSE;
987 ConnectAllAttempted = FALSE;
988 do {
989 if (BlockIoBuffer != NULL) {
990 FreePool (BlockIoBuffer);
991 }
992
993 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer);
994 if (EFI_ERROR (Status)) {
995 BlockIoHandleCount = 0;
996 BlockIoBuffer = NULL;
997 }
998
999 //
1000 // Loop through all the device handles that support the BLOCK_IO Protocol
1001 //
1002 for (Index = 0; Index < BlockIoHandleCount; Index++) {
1003 BlockIoDevicePath = DevicePathFromHandle (BlockIoBuffer[Index]);
1004 if (BlockIoDevicePath == NULL) {
1005 continue;
1006 }
1007
1008 if (BmMatchPartitionDevicePathNode (BlockIoDevicePath, (HARDDRIVE_DEVICE_PATH *)FilePath)) {
1009 //
1010 // Find the matched partition device path
1011 //
1012 TempDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (FilePath));
1013 FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL);
1014 FreePool (TempDevicePath);
1015
1016 if (FullPath != NULL) {
1017 BmCachePartitionDevicePath (&CachedDevicePath, BlockIoDevicePath);
1018
1019 //
1020 // Save the matching Device Path so we don't need to do a connect all next time
1021 // Failing to save only impacts performance next time expanding the short-form device path
1022 //
1023 Status = gRT->SetVariable (
1024 L"HDDP",
1025 &mBmHardDriveBootVariableGuid,
1026 EFI_VARIABLE_BOOTSERVICE_ACCESS |
1027 EFI_VARIABLE_NON_VOLATILE,
1028 GetDevicePathSize (CachedDevicePath),
1029 CachedDevicePath
1030 );
1031 MatchFound = TRUE;
1032 break;
1033 }
1034 }
1035 }
1036
1037 //
1038 // If we found a matching BLOCK_IO handle or we've already
1039 // tried a ConnectAll, we are done searching.
1040 //
1041 if (MatchFound || ConnectAllAttempted) {
1042 break;
1043 }
1044
1045 EfiBootManagerConnectAll ();
1046 ConnectAllAttempted = TRUE;
1047 } while (1);
1048
1049 if (CachedDevicePath != NULL) {
1050 FreePool (CachedDevicePath);
1051 }
1052
1053 if (BlockIoBuffer != NULL) {
1054 FreePool (BlockIoBuffer);
1055 }
1056
1057 return FullPath;
1058}
1059
1060#ifdef VBOX
1061/**
1062 * Checks which filename to try loading by inspecting what is existing on the provided
1063 * simple filesystem protocol provider.
1064 *
1065 * This is required to support booting macOS as it stores the efi OS loader in a non standard location
1066 * and we have to support both styles without rewriting half of the boot manager library.
1067 */
1068EFI_STATUS VBoxBmQueryMediaFileNameForSFs(EFI_HANDLE hSFs, CHAR16 **ppwszFileName)
1069{
1070 EFI_STATUS Status = EFI_SUCCESS;
1071 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *pSFs = NULL;
1072 EFI_FILE_PROTOCOL *pRoot = NULL;
1073
1074 *ppwszFileName = EFI_REMOVABLE_MEDIA_FILE_NAME;
1075
1076 Status = gBS->HandleProtocol(hSFs, &gEfiSimpleFileSystemProtocolGuid, (void **)&pSFs);
1077 if (!EFI_ERROR(Status))
1078 {
1079 Status = pSFs->OpenVolume(pSFs, &pRoot);
1080 if (!EFI_ERROR(Status))
1081 {
1082#if 0
1083# define VBOX_EFI_APPLE_MEDIA_FILE_NAME L"\\System\\Library\\CoreServices\\boot.efi"
1084 EFI_FILE_PROTOCOL *pFile = NULL;
1085
1086 Status = pRoot->Open(pRoot, &pFile, VBOX_EFI_APPLE_MEDIA_FILE_NAME, EFI_FILE_MODE_READ,
1087 EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
1088 if (!EFI_ERROR(Status))
1089 {
1090 *ppwszFileName = VBOX_EFI_APPLE_MEDIA_FILE_NAME;
1091 pFile->Close(pFile);
1092 }
1093#else /* Doesn't quite work yet. */
1094 VBOX_FS_BLESSED_FILE *Buffer = NULL;
1095 UINTN BufferSize = 0;
1096
1097 Status = pRoot->GetInfo(pRoot, &gVBoxFsBlessedFileInfoGuid, &BufferSize, Buffer);
1098 if (Status == EFI_BUFFER_TOO_SMALL) {
1099 Buffer = AllocatePool (BufferSize);
1100 ASSERT (Buffer != NULL);
1101
1102 /** @todo We might leak this allocation but it doesn't really matter as it
1103 * is of type BootServicesData and will be reclaimed by the OS when it boots.
1104 */
1105 Status = pRoot->GetInfo(pRoot, &gVBoxFsBlessedFileInfoGuid, &BufferSize, Buffer);
1106 if (!EFI_ERROR(Status))
1107 {
1108 DEBUG ((EFI_D_INFO, "[Bds] VBoxBmQueryMediaFileNameForSFs: Got blessed file info %s\n", &Buffer->BlessedFile[0]));
1109 *ppwszFileName = &Buffer->BlessedFile[0];
1110 }
1111 }
1112#endif
1113
1114 pRoot->Close(pRoot);
1115 }
1116 }
1117
1118 return EFI_SUCCESS;
1119}
1120#endif
1121
1122/**
1123 Expand the media device path which points to a BlockIo or SimpleFileSystem instance
1124 by appending EFI_REMOVABLE_MEDIA_FILE_NAME.
1125
1126 @param DevicePath The media device path pointing to a BlockIo or SimpleFileSystem instance.
1127 @param FullPath The full path returned by the routine in last call.
1128 Set to NULL in first call.
1129
1130 @return The next possible full path pointing to the load option.
1131 Caller is responsible to free the memory.
1132**/
1133EFI_DEVICE_PATH_PROTOCOL *
1134BmExpandMediaDevicePath (
1135 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
1136 IN EFI_DEVICE_PATH_PROTOCOL *FullPath
1137 )
1138{
1139 EFI_STATUS Status;
1140 EFI_HANDLE Handle;
1141 EFI_BLOCK_IO_PROTOCOL *BlockIo;
1142 VOID *Buffer;
1143 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
1144 EFI_DEVICE_PATH_PROTOCOL *NextFullPath;
1145 UINTN Size;
1146 UINTN TempSize;
1147 EFI_HANDLE *SimpleFileSystemHandles;
1148 UINTN NumberSimpleFileSystemHandles;
1149 UINTN Index;
1150 BOOLEAN GetNext;
1151#ifdef VBOX
1152 CHAR16 *pwszFilename = NULL;
1153#endif
1154
1155 GetNext = (BOOLEAN)(FullPath == NULL);
1156
1157 //
1158 // Check whether the device is connected
1159 //
1160 TempDevicePath = DevicePath;
1161 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle);
1162 if (!EFI_ERROR (Status)) {
1163 ASSERT (IsDevicePathEnd (TempDevicePath));
1164
1165#ifndef VBOX
1166 NextFullPath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
1167#else
1168 Status = VBoxBmQueryMediaFileNameForSFs(Handle, &pwszFilename);
1169 if (!EFI_ERROR(Status))
1170 NextFullPath = FileDevicePath (Handle, pwszFilename);
1171 else
1172 return NULL;
1173#endif
1174 //
1175 // For device path pointing to simple file system, it only expands to one full path.
1176 //
1177 if (GetNext) {
1178 return NextFullPath;
1179 } else {
1180 FreePool (NextFullPath);
1181 return NULL;
1182 }
1183 }
1184
1185 Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);
1186 ASSERT_EFI_ERROR (Status);
1187
1188 //
1189 // For device boot option only pointing to the removable device handle,
1190 // should make sure all its children handles (its child partion or media handles)
1191 // are created and connected.
1192 //
1193 gBS->ConnectController (Handle, NULL, NULL, TRUE);
1194
1195 //
1196 // Issue a dummy read to the device to check for media change.
1197 // When the removable media is changed, any Block IO read/write will
1198 // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is
1199 // returned. After the Block IO protocol is reinstalled, subsequent
1200 // Block IO read/write will success.
1201 //
1202 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);
1203 ASSERT_EFI_ERROR (Status);
1204 if (EFI_ERROR (Status)) {
1205 return NULL;
1206 }
1207
1208 Buffer = AllocatePool (BlockIo->Media->BlockSize);
1209 if (Buffer != NULL) {
1210 BlockIo->ReadBlocks (
1211 BlockIo,
1212 BlockIo->Media->MediaId,
1213 0,
1214 BlockIo->Media->BlockSize,
1215 Buffer
1216 );
1217 FreePool (Buffer);
1218 }
1219
1220 //
1221 // Detect the the default boot file from removable Media
1222 //
1223 NextFullPath = NULL;
1224 Size = GetDevicePathSize (DevicePath) - END_DEVICE_PATH_LENGTH;
1225 gBS->LocateHandleBuffer (
1226 ByProtocol,
1227 &gEfiSimpleFileSystemProtocolGuid,
1228 NULL,
1229 &NumberSimpleFileSystemHandles,
1230 &SimpleFileSystemHandles
1231 );
1232 for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {
1233 //
1234 // Get the device path size of SimpleFileSystem handle
1235 //
1236 TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);
1237 TempSize = GetDevicePathSize (TempDevicePath) - END_DEVICE_PATH_LENGTH;
1238 //
1239 // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path
1240 //
1241 if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) {
1242#ifndef VBOX
1243 NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);
1244#else
1245 Status = VBoxBmQueryMediaFileNameForSFs(SimpleFileSystemHandles[Index], &pwszFilename);
1246 if (!EFI_ERROR(Status))
1247 NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], pwszFilename);
1248 else
1249 return NULL;
1250#endif
1251 if (GetNext) {
1252 break;
1253 } else {
1254 GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
1255 FreePool (NextFullPath);
1256 NextFullPath = NULL;
1257 }
1258 }
1259 }
1260
1261 if (SimpleFileSystemHandles != NULL) {
1262 FreePool (SimpleFileSystemHandles);
1263 }
1264
1265 return NextFullPath;
1266}
1267
1268/**
1269 Check whether Left and Right are the same without matching the specific
1270 device path data in IP device path and URI device path node.
1271
1272 @retval TRUE Left and Right are the same.
1273 @retval FALSE Left and Right are the different.
1274**/
1275BOOLEAN
1276BmMatchHttpBootDevicePath (
1277 IN EFI_DEVICE_PATH_PROTOCOL *Left,
1278 IN EFI_DEVICE_PATH_PROTOCOL *Right
1279 )
1280{
1281 for ( ; !IsDevicePathEnd (Left) && !IsDevicePathEnd (Right)
1282 ; Left = NextDevicePathNode (Left), Right = NextDevicePathNode (Right)
1283 )
1284 {
1285 if (CompareMem (Left, Right, DevicePathNodeLength (Left)) != 0) {
1286 if ((DevicePathType (Left) != MESSAGING_DEVICE_PATH) || (DevicePathType (Right) != MESSAGING_DEVICE_PATH)) {
1287 return FALSE;
1288 }
1289
1290 if (DevicePathSubType (Left) == MSG_DNS_DP) {
1291 Left = NextDevicePathNode (Left);
1292 }
1293
1294 if (DevicePathSubType (Right) == MSG_DNS_DP) {
1295 Right = NextDevicePathNode (Right);
1296 }
1297
1298 if (((DevicePathSubType (Left) != MSG_IPv4_DP) || (DevicePathSubType (Right) != MSG_IPv4_DP)) &&
1299 ((DevicePathSubType (Left) != MSG_IPv6_DP) || (DevicePathSubType (Right) != MSG_IPv6_DP)) &&
1300 ((DevicePathSubType (Left) != MSG_URI_DP) || (DevicePathSubType (Right) != MSG_URI_DP))
1301 )
1302 {
1303 return FALSE;
1304 }
1305 }
1306 }
1307
1308 return (BOOLEAN)(IsDevicePathEnd (Left) && IsDevicePathEnd (Right));
1309}
1310
1311/**
1312 Get the file buffer from the file system produced by Load File instance.
1313
1314 @param LoadFileHandle The handle of LoadFile instance.
1315 @param RamDiskHandle Return the RAM Disk handle.
1316
1317 @return The next possible full path pointing to the load option.
1318 Caller is responsible to free the memory.
1319**/
1320EFI_DEVICE_PATH_PROTOCOL *
1321BmExpandNetworkFileSystem (
1322 IN EFI_HANDLE LoadFileHandle,
1323 OUT EFI_HANDLE *RamDiskHandle
1324 )
1325{
1326 EFI_STATUS Status;
1327 EFI_HANDLE Handle;
1328 EFI_HANDLE *Handles;
1329 UINTN HandleCount;
1330 UINTN Index;
1331 EFI_DEVICE_PATH_PROTOCOL *Node;
1332
1333 Status = gBS->LocateHandleBuffer (
1334 ByProtocol,
1335 &gEfiBlockIoProtocolGuid,
1336 NULL,
1337 &HandleCount,
1338 &Handles
1339 );
1340 if (EFI_ERROR (Status)) {
1341 Handles = NULL;
1342 HandleCount = 0;
1343 }
1344
1345 Handle = NULL;
1346 for (Index = 0; Index < HandleCount; Index++) {
1347 Node = DevicePathFromHandle (Handles[Index]);
1348 Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);
1349 if (!EFI_ERROR (Status) &&
1350 (Handle == LoadFileHandle) &&
1351 (DevicePathType (Node) == MEDIA_DEVICE_PATH) && (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP))
1352 {
1353 //
1354 // Find the BlockIo instance populated from the LoadFile.
1355 //
1356 Handle = Handles[Index];
1357 break;
1358 }
1359 }
1360
1361 if (Handles != NULL) {
1362 FreePool (Handles);
1363 }
1364
1365 if (Index == HandleCount) {
1366 Handle = NULL;
1367 }
1368
1369 *RamDiskHandle = Handle;
1370
1371 if (Handle != NULL) {
1372 //
1373 // Re-use BmExpandMediaDevicePath() to get the full device path of load option.
1374 // But assume only one SimpleFileSystem can be found under the BlockIo.
1375 //
1376 return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), NULL);
1377 } else {
1378 return NULL;
1379 }
1380}
1381
1382/**
1383 Return the RAM Disk device path created by LoadFile.
1384
1385 @param FilePath The source file path.
1386
1387 @return Callee-to-free RAM Disk device path
1388**/
1389EFI_DEVICE_PATH_PROTOCOL *
1390BmGetRamDiskDevicePath (
1391 IN EFI_DEVICE_PATH_PROTOCOL *FilePath
1392 )
1393{
1394 EFI_STATUS Status;
1395 EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;
1396 EFI_DEVICE_PATH_PROTOCOL *Node;
1397 EFI_HANDLE Handle;
1398
1399 Node = FilePath;
1400 Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);
1401 if (!EFI_ERROR (Status) &&
1402 (DevicePathType (Node) == MEDIA_DEVICE_PATH) &&
1403 (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP)
1404 )
1405 {
1406 //
1407 // Construct the device path pointing to RAM Disk
1408 //
1409 Node = NextDevicePathNode (Node);
1410 RamDiskDevicePath = DuplicateDevicePath (FilePath);
1411 ASSERT (RamDiskDevicePath != NULL);
1412 SetDevicePathEndNode ((VOID *)((UINTN)RamDiskDevicePath + ((UINTN)Node - (UINTN)FilePath)));
1413 return RamDiskDevicePath;
1414 }
1415
1416 return NULL;
1417}
1418
1419/**
1420 Return the buffer and buffer size occupied by the RAM Disk.
1421
1422 @param RamDiskDevicePath RAM Disk device path.
1423 @param RamDiskSizeInPages Return RAM Disk size in pages.
1424
1425 @retval RAM Disk buffer.
1426**/
1427VOID *
1428BmGetRamDiskMemoryInfo (
1429 IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath,
1430 OUT UINTN *RamDiskSizeInPages
1431 )
1432{
1433 EFI_STATUS Status;
1434 EFI_HANDLE Handle;
1435 UINT64 StartingAddr;
1436 UINT64 EndingAddr;
1437
1438 ASSERT (RamDiskDevicePath != NULL);
1439
1440 *RamDiskSizeInPages = 0;
1441
1442 //
1443 // Get the buffer occupied by RAM Disk.
1444 //
1445 Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &RamDiskDevicePath, &Handle);
1446 ASSERT_EFI_ERROR (Status);
1447 ASSERT (
1448 (DevicePathType (RamDiskDevicePath) == MEDIA_DEVICE_PATH) &&
1449 (DevicePathSubType (RamDiskDevicePath) == MEDIA_RAM_DISK_DP)
1450 );
1451 StartingAddr = ReadUnaligned64 ((UINT64 *)((MEDIA_RAM_DISK_DEVICE_PATH *)RamDiskDevicePath)->StartingAddr);
1452 EndingAddr = ReadUnaligned64 ((UINT64 *)((MEDIA_RAM_DISK_DEVICE_PATH *)RamDiskDevicePath)->EndingAddr);
1453 *RamDiskSizeInPages = EFI_SIZE_TO_PAGES ((UINTN)(EndingAddr - StartingAddr + 1));
1454 return (VOID *)(UINTN)StartingAddr;
1455}
1456
1457/**
1458 Destroy the RAM Disk.
1459
1460 The destroy operation includes to call RamDisk.Unregister to
1461 unregister the RAM DISK from RAM DISK driver, free the memory
1462 allocated for the RAM Disk.
1463
1464 @param RamDiskDevicePath RAM Disk device path.
1465**/
1466VOID
1467BmDestroyRamDisk (
1468 IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath
1469 )
1470{
1471 EFI_STATUS Status;
1472 VOID *RamDiskBuffer;
1473 UINTN RamDiskSizeInPages;
1474
1475 ASSERT (RamDiskDevicePath != NULL);
1476
1477 RamDiskBuffer = BmGetRamDiskMemoryInfo (RamDiskDevicePath, &RamDiskSizeInPages);
1478
1479 //
1480 // Destroy RAM Disk.
1481 //
1482 if (mRamDisk == NULL) {
1483 Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID *)&mRamDisk);
1484 ASSERT_EFI_ERROR (Status);
1485 }
1486
1487 Status = mRamDisk->Unregister (RamDiskDevicePath);
1488 ASSERT_EFI_ERROR (Status);
1489 FreePages (RamDiskBuffer, RamDiskSizeInPages);
1490}
1491
1492/**
1493 Get the file buffer from the specified Load File instance.
1494
1495 @param LoadFileHandle The specified Load File instance.
1496 @param FilePath The file path which will pass to LoadFile().
1497
1498 @return The full device path pointing to the load option buffer.
1499**/
1500EFI_DEVICE_PATH_PROTOCOL *
1501BmExpandLoadFile (
1502 IN EFI_HANDLE LoadFileHandle,
1503 IN EFI_DEVICE_PATH_PROTOCOL *FilePath
1504 )
1505{
1506 EFI_STATUS Status;
1507 EFI_LOAD_FILE_PROTOCOL *LoadFile;
1508 VOID *FileBuffer;
1509 EFI_HANDLE RamDiskHandle;
1510 UINTN BufferSize;
1511 EFI_DEVICE_PATH_PROTOCOL *FullPath;
1512
1513 Status = gBS->OpenProtocol (
1514 LoadFileHandle,
1515 &gEfiLoadFileProtocolGuid,
1516 (VOID **)&LoadFile,
1517 gImageHandle,
1518 NULL,
1519 EFI_OPEN_PROTOCOL_GET_PROTOCOL
1520 );
1521 ASSERT_EFI_ERROR (Status);
1522
1523 FileBuffer = NULL;
1524 BufferSize = 0;
1525 Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);
1526 if ((Status != EFI_WARN_FILE_SYSTEM) && (Status != EFI_BUFFER_TOO_SMALL)) {
1527 return NULL;
1528 }
1529
1530 if (Status == EFI_BUFFER_TOO_SMALL) {
1531 //
1532 // The load option buffer is directly returned by LoadFile.
1533 //
1534 return DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle));
1535 }
1536
1537 //
1538 // The load option resides in a RAM disk.
1539 //
1540 FileBuffer = AllocateReservedPages (EFI_SIZE_TO_PAGES (BufferSize));
1541 if (FileBuffer == NULL) {
1542 DEBUG_CODE_BEGIN ();
1543 EFI_DEVICE_PATH *LoadFilePath;
1544 CHAR16 *LoadFileText;
1545 CHAR16 *FileText;
1546
1547 LoadFilePath = DevicePathFromHandle (LoadFileHandle);
1548 if (LoadFilePath == NULL) {
1549 LoadFileText = NULL;
1550 } else {
1551 LoadFileText = ConvertDevicePathToText (LoadFilePath, FALSE, FALSE);
1552 }
1553
1554 FileText = ConvertDevicePathToText (FilePath, FALSE, FALSE);
1555
1556 DEBUG ((
1557 DEBUG_ERROR,
1558 "%a:%a: failed to allocate reserved pages: "
1559 "BufferSize=%Lu LoadFile=\"%s\" FilePath=\"%s\"\n",
1560 gEfiCallerBaseName,
1561 __func__,
1562 (UINT64)BufferSize,
1563 LoadFileText,
1564 FileText
1565 ));
1566
1567 if (FileText != NULL) {
1568 FreePool (FileText);
1569 }
1570
1571 if (LoadFileText != NULL) {
1572 FreePool (LoadFileText);
1573 }
1574
1575 DEBUG_CODE_END ();
1576 return NULL;
1577 }
1578
1579 Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);
1580 if (EFI_ERROR (Status)) {
1581 FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize));
1582 return NULL;
1583 }
1584
1585 FullPath = BmExpandNetworkFileSystem (LoadFileHandle, &RamDiskHandle);
1586 if (FullPath == NULL) {
1587 //
1588 // Free the memory occupied by the RAM disk if there is no BlockIo or SimpleFileSystem instance.
1589 //
1590 BmDestroyRamDisk (DevicePathFromHandle (RamDiskHandle));
1591 }
1592
1593 return FullPath;
1594}
1595
1596/**
1597 Return the full device path pointing to the load option.
1598
1599 FilePath may:
1600 1. Exactly matches to a LoadFile instance.
1601 2. Cannot match to any LoadFile instance. Wide match is required.
1602 In either case, the routine may return:
1603 1. A copy of FilePath when FilePath matches to a LoadFile instance and
1604 the LoadFile returns a load option buffer.
1605 2. A new device path with IP and URI information updated when wide match
1606 happens.
1607 3. A new device path pointing to a load option in RAM disk.
1608 In either case, only one full device path is returned for a specified
1609 FilePath.
1610
1611 @param FilePath The media device path pointing to a LoadFile instance.
1612
1613 @return The load option buffer.
1614**/
1615EFI_DEVICE_PATH_PROTOCOL *
1616BmExpandLoadFiles (
1617 IN EFI_DEVICE_PATH_PROTOCOL *FilePath
1618 )
1619{
1620 EFI_STATUS Status;
1621 EFI_HANDLE Handle;
1622 EFI_HANDLE *Handles;
1623 UINTN HandleCount;
1624 UINTN Index;
1625 EFI_DEVICE_PATH_PROTOCOL *Node;
1626
1627 //
1628 // Get file buffer from load file instance.
1629 //
1630 Node = FilePath;
1631 Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);
1632 if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) {
1633 //
1634 // When wide match happens, pass full device path to LoadFile (),
1635 // otherwise, pass remaining device path to LoadFile ().
1636 //
1637 FilePath = Node;
1638 } else {
1639 Handle = NULL;
1640 //
1641 // Use wide match algorithm to find one when
1642 // cannot find a LoadFile instance to exactly match the FilePath
1643 //
1644 Status = gBS->LocateHandleBuffer (
1645 ByProtocol,
1646 &gEfiLoadFileProtocolGuid,
1647 NULL,
1648 &HandleCount,
1649 &Handles
1650 );
1651 if (EFI_ERROR (Status)) {
1652 Handles = NULL;
1653 HandleCount = 0;
1654 }
1655
1656 for (Index = 0; Index < HandleCount; Index++) {
1657 if (BmMatchHttpBootDevicePath (DevicePathFromHandle (Handles[Index]), FilePath)) {
1658 Handle = Handles[Index];
1659 break;
1660 }
1661 }
1662
1663 if (Handles != NULL) {
1664 FreePool (Handles);
1665 }
1666 }
1667
1668 if (Handle == NULL) {
1669 return NULL;
1670 }
1671
1672 return BmExpandLoadFile (Handle, FilePath);
1673}
1674
1675/**
1676 Get the load option by its device path.
1677
1678 @param FilePath The device path pointing to a load option.
1679 It could be a short-form device path.
1680 @param FullPath Return the full device path of the load option after
1681 short-form device path expanding.
1682 Caller is responsible to free it.
1683 @param FileSize Return the load option size.
1684
1685 @return The load option buffer. Caller is responsible to free the memory.
1686**/
1687VOID *
1688EFIAPI
1689EfiBootManagerGetLoadOptionBuffer (
1690 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
1691 OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
1692 OUT UINTN *FileSize
1693 )
1694{
1695 *FullPath = NULL;
1696
1697 EfiBootManagerConnectDevicePath (FilePath, NULL);
1698 return BmGetNextLoadOptionBuffer (LoadOptionTypeMax, FilePath, FullPath, FileSize);
1699}
1700
1701/**
1702 Get the next possible full path pointing to the load option.
1703 The routine doesn't guarantee the returned full path points to an existing
1704 file, and it also doesn't guarantee the existing file is a valid load option.
1705 BmGetNextLoadOptionBuffer() guarantees.
1706
1707 @param FilePath The device path pointing to a load option.
1708 It could be a short-form device path.
1709 @param FullPath The full path returned by the routine in last call.
1710 Set to NULL in first call.
1711
1712 @return The next possible full path pointing to the load option.
1713 Caller is responsible to free the memory.
1714**/
1715EFI_DEVICE_PATH_PROTOCOL *
1716BmGetNextLoadOptionDevicePath (
1717 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
1718 IN EFI_DEVICE_PATH_PROTOCOL *FullPath
1719 )
1720{
1721 EFI_HANDLE Handle;
1722 EFI_DEVICE_PATH_PROTOCOL *Node;
1723 EFI_STATUS Status;
1724
1725 ASSERT (FilePath != NULL);
1726
1727 //
1728 // Boot from media device by adding a default file name \EFI\BOOT\BOOT{machine type short-name}.EFI
1729 //
1730 Node = FilePath;
1731 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle);
1732 if (EFI_ERROR (Status)) {
1733 Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &Node, &Handle);
1734 }
1735
1736 if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) {
1737 return BmExpandMediaDevicePath (FilePath, FullPath);
1738 }
1739
1740 //
1741 // Expand the short-form device path to full device path
1742 //
1743 if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) &&
1744 (DevicePathSubType (FilePath) == MEDIA_HARDDRIVE_DP))
1745 {
1746 //
1747 // Expand the Harddrive device path
1748 //
1749 if (FullPath == NULL) {
1750 return BmExpandPartitionDevicePath (FilePath);
1751 } else {
1752 return NULL;
1753 }
1754 } else if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) &&
1755 (DevicePathSubType (FilePath) == MEDIA_FILEPATH_DP))
1756 {
1757 //
1758 // Expand the File-path device path
1759 //
1760 return BmExpandFileDevicePath (FilePath, FullPath);
1761 } else if ((DevicePathType (FilePath) == MESSAGING_DEVICE_PATH) &&
1762 (DevicePathSubType (FilePath) == MSG_URI_DP))
1763 {
1764 //
1765 // Expand the URI device path
1766 //
1767 return BmExpandUriDevicePath (FilePath, FullPath);
1768 } else {
1769 Node = FilePath;
1770 Status = gBS->LocateDevicePath (&gEfiUsbIoProtocolGuid, &Node, &Handle);
1771 if (EFI_ERROR (Status)) {
1772 //
1773 // Only expand the USB WWID/Class device path
1774 // when FilePath doesn't point to a physical UsbIo controller.
1775 // Otherwise, infinite recursion will happen.
1776 //
1777 for (Node = FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) {
1778 if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&
1779 ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) || (DevicePathSubType (Node) == MSG_USB_WWID_DP)))
1780 {
1781 break;
1782 }
1783 }
1784
1785 //
1786 // Expand the USB WWID/Class device path
1787 //
1788 if (!IsDevicePathEnd (Node)) {
1789 if (FilePath == Node) {
1790 //
1791 // Boot Option device path starts with USB Class or USB WWID device path.
1792 // For Boot Option device path which doesn't begin with the USB Class or
1793 // USB WWID device path, it's not needed to connect again here.
1794 //
1795 BmConnectUsbShortFormDevicePath (FilePath);
1796 }
1797
1798 return BmExpandUsbDevicePath (FilePath, FullPath, Node);
1799 }
1800 }
1801 }
1802
1803 //
1804 // For the below cases, FilePath only expands to one Full path.
1805 // So just handle the case when FullPath == NULL.
1806 //
1807 if (FullPath != NULL) {
1808 return NULL;
1809 }
1810
1811 //
1812 // Load option resides in FV.
1813 //
1814 if (BmIsFvFilePath (FilePath)) {
1815 return BmAdjustFvFilePath (FilePath);
1816 }
1817
1818 //
1819 // Load option resides in Simple File System.
1820 //
1821 Node = FilePath;
1822 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle);
1823 if (!EFI_ERROR (Status)) {
1824 return DuplicateDevicePath (FilePath);
1825 }
1826
1827 //
1828 // Last chance to try: Load option may be loaded through LoadFile.
1829 //
1830 return BmExpandLoadFiles (FilePath);
1831}
1832
1833/**
1834 Check if it's a Device Path pointing to BootManagerMenu.
1835
1836 @param DevicePath Input device path.
1837
1838 @retval TRUE The device path is BootManagerMenu File Device Path.
1839 @retval FALSE The device path is NOT BootManagerMenu File Device Path.
1840**/
1841BOOLEAN
1842BmIsBootManagerMenuFilePath (
1843 EFI_DEVICE_PATH_PROTOCOL *DevicePath
1844 )
1845{
1846 EFI_HANDLE FvHandle;
1847 VOID *NameGuid;
1848 EFI_STATUS Status;
1849
1850 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &DevicePath, &FvHandle);
1851 if (!EFI_ERROR (Status)) {
1852 NameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)DevicePath);
1853 if (NameGuid != NULL) {
1854 return CompareGuid (NameGuid, PcdGetPtr (PcdBootManagerMenuFile));
1855 }
1856 }
1857
1858 return FALSE;
1859}
1860
1861/**
1862 Report status code with EFI_RETURN_STATUS_EXTENDED_DATA about LoadImage() or
1863 StartImage() failure.
1864
1865 @param[in] ErrorCode An Error Code in the Software Class, DXE Boot
1866 Service Driver Subclass. ErrorCode will be used to
1867 compose the Value parameter for status code
1868 reporting. Must be one of
1869 EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR and
1870 EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED.
1871
1872 @param[in] FailureStatus The failure status returned by the boot service
1873 that should be reported.
1874**/
1875VOID
1876BmReportLoadFailure (
1877 IN UINT32 ErrorCode,
1878 IN EFI_STATUS FailureStatus
1879 )
1880{
1881 EFI_RETURN_STATUS_EXTENDED_DATA ExtendedData;
1882
1883 if (!ReportErrorCodeEnabled ()) {
1884 return;
1885 }
1886
1887 ASSERT (
1888 (ErrorCode == EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR) ||
1889 (ErrorCode == EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED)
1890 );
1891
1892 ZeroMem (&ExtendedData, sizeof (ExtendedData));
1893 ExtendedData.ReturnStatus = FailureStatus;
1894
1895 REPORT_STATUS_CODE_EX (
1896 (EFI_ERROR_CODE | EFI_ERROR_MINOR),
1897 (EFI_SOFTWARE_DXE_BS_DRIVER | ErrorCode),
1898 0,
1899 NULL,
1900 NULL,
1901 &ExtendedData.DataHeader + 1,
1902 sizeof (ExtendedData) - sizeof (ExtendedData.DataHeader)
1903 );
1904}
1905
1906/**
1907 Attempt to boot the EFI boot option. This routine sets L"BootCurent" and
1908 also signals the EFI ready to boot event. If the device path for the option
1909 starts with a BBS device path a legacy boot is attempted via the registered
1910 gLegacyBoot function. Short form device paths are also supported via this
1911 rountine. A device path starting with MEDIA_HARDDRIVE_DP, MSG_USB_WWID_DP,
1912 MSG_USB_CLASS_DP gets expaned out to find the first device that matches.
1913 If the BootOption Device Path fails the removable media boot algorithm
1914 is attempted (\EFI\BOOTIA32.EFI, \EFI\BOOTX64.EFI,... only one file type
1915 is tried per processor type)
1916
1917 @param BootOption Boot Option to try and boot.
1918 On return, BootOption->Status contains the boot status.
1919 EFI_SUCCESS BootOption was booted
1920 EFI_UNSUPPORTED A BBS device path was found with no valid callback
1921 registered via EfiBootManagerInitialize().
1922 EFI_NOT_FOUND The BootOption was not found on the system
1923 !EFI_SUCCESS BootOption failed with this error status
1924
1925**/
1926VOID
1927EFIAPI
1928EfiBootManagerBoot (
1929 IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
1930 )
1931{
1932 EFI_STATUS Status;
1933 EFI_HANDLE ImageHandle;
1934 EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
1935 UINT16 Uint16;
1936 UINTN OptionNumber;
1937 UINTN OriginalOptionNumber;
1938 EFI_DEVICE_PATH_PROTOCOL *FilePath;
1939 EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;
1940 VOID *FileBuffer;
1941 UINTN FileSize;
1942 EFI_BOOT_LOGO_PROTOCOL *BootLogo;
1943 EFI_EVENT LegacyBootEvent;
1944
1945 if (BootOption == NULL) {
1946 return;
1947 }
1948
1949 if ((BootOption->FilePath == NULL) || (BootOption->OptionType != LoadOptionTypeBoot)) {
1950 BootOption->Status = EFI_INVALID_PARAMETER;
1951 return;
1952 }
1953
1954 //
1955 // 1. Create Boot#### for a temporary boot if there is no match Boot#### (i.e. a boot by selected a EFI Shell using "Boot From File")
1956 //
1957 OptionNumber = BmFindBootOptionInVariable (BootOption);
1958 if (OptionNumber == LoadOptionNumberUnassigned) {
1959 Status = BmGetFreeOptionNumber (LoadOptionTypeBoot, &Uint16);
1960 if (!EFI_ERROR (Status)) {
1961 //
1962 // Save the BootOption->OptionNumber to restore later
1963 //
1964 OptionNumber = Uint16;
1965 OriginalOptionNumber = BootOption->OptionNumber;
1966 BootOption->OptionNumber = OptionNumber;
1967 Status = EfiBootManagerLoadOptionToVariable (BootOption);
1968 BootOption->OptionNumber = OriginalOptionNumber;
1969 }
1970
1971 if (EFI_ERROR (Status)) {
1972 DEBUG ((DEBUG_ERROR, "[Bds] Failed to create Boot#### for a temporary boot - %r!\n", Status));
1973 BootOption->Status = Status;
1974 return;
1975 }
1976 }
1977
1978 //
1979 // 2. Set BootCurrent
1980 //
1981 Uint16 = (UINT16)OptionNumber;
1982 BmSetVariableAndReportStatusCodeOnError (
1983 L"BootCurrent",
1984 &gEfiGlobalVariableGuid,
1985 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
1986 sizeof (UINT16),
1987 &Uint16
1988 );
1989
1990 //
1991 // 3. Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute
1992 // the boot option.
1993 //
1994 if (BmIsBootManagerMenuFilePath (BootOption->FilePath)) {
1995 DEBUG ((DEBUG_INFO, "[Bds] Booting Boot Manager Menu.\n"));
1996 BmStopHotkeyService (NULL, NULL);
1997 } else {
1998 EfiSignalEventReadyToBoot ();
1999 //
2000 // Report Status Code to indicate ReadyToBoot was signalled
2001 //
2002 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));
2003 //
2004 // 4. Repair system through DriverHealth protocol
2005 //
2006 BmRepairAllControllers (0);
2007 }
2008
2009 PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32)OptionNumber);
2010
2011 //
2012 // 5. Adjust the different type memory page number just before booting
2013 // and save the updated info into the variable for next boot to use
2014 //
2015 BmSetMemoryTypeInformationVariable (
2016 (BOOLEAN)((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT)
2017 );
2018
2019 //
2020 // 6. Load EFI boot option to ImageHandle
2021 //
2022 DEBUG_CODE_BEGIN ();
2023 if (BootOption->Description == NULL) {
2024 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting from unknown device path\n"));
2025 } else {
2026 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting %s\n", BootOption->Description));
2027 }
2028
2029 DEBUG_CODE_END ();
2030
2031 ImageHandle = NULL;
2032 RamDiskDevicePath = NULL;
2033 if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) {
2034 Status = EFI_NOT_FOUND;
2035 FilePath = NULL;
2036 EfiBootManagerConnectDevicePath (BootOption->FilePath, NULL);
2037 FileBuffer = BmGetNextLoadOptionBuffer (LoadOptionTypeBoot, BootOption->FilePath, &FilePath, &FileSize);
2038 if (FileBuffer != NULL) {
2039 RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath);
2040
2041 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));
2042 Status = gBS->LoadImage (
2043 TRUE,
2044 gImageHandle,
2045 FilePath,
2046 FileBuffer,
2047 FileSize,
2048 &ImageHandle
2049 );
2050 }
2051
2052 if (FileBuffer != NULL) {
2053 FreePool (FileBuffer);
2054 }
2055
2056 if (FilePath != NULL) {
2057 FreePool (FilePath);
2058 }
2059
2060 if (EFI_ERROR (Status)) {
2061 //
2062 // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created
2063 // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.
2064 // If the caller doesn't have the option to defer the execution of an image, we should
2065 // unload image for the EFI_SECURITY_VIOLATION to avoid resource leak.
2066 //
2067 if (Status == EFI_SECURITY_VIOLATION) {
2068 gBS->UnloadImage (ImageHandle);
2069 }
2070
2071 //
2072 // Destroy the RAM disk
2073 //
2074 if (RamDiskDevicePath != NULL) {
2075 BmDestroyRamDisk (RamDiskDevicePath);
2076 FreePool (RamDiskDevicePath);
2077 }
2078
2079 //
2080 // Report Status Code with the failure status to indicate that the failure to load boot option
2081 //
2082 BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR, Status);
2083 BootOption->Status = Status;
2084 return;
2085 }
2086 }
2087
2088 //
2089 // Check to see if we should legacy BOOT. If yes then do the legacy boot
2090 // Write boot to OS performance data for Legacy boot
2091 //
2092 if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) {
2093 if (mBmLegacyBoot != NULL) {
2094 //
2095 // Write boot to OS performance data for legacy boot.
2096 //
2097 PERF_CODE (
2098 //
2099 // Create an event to be signalled when Legacy Boot occurs to write performance data.
2100 //
2101 Status = EfiCreateEventLegacyBootEx (
2102 TPL_NOTIFY,
2103 BmEndOfBdsPerfCode,
2104 NULL,
2105 &LegacyBootEvent
2106 );
2107 ASSERT_EFI_ERROR (Status);
2108 );
2109
2110 mBmLegacyBoot (BootOption);
2111 } else {
2112 BootOption->Status = EFI_UNSUPPORTED;
2113 }
2114
2115 PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32)OptionNumber);
2116 return;
2117 }
2118
2119 //
2120 // Provide the image with its load options
2121 //
2122 Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&ImageInfo);
2123 ASSERT_EFI_ERROR (Status);
2124
2125 if (!BmIsAutoCreateBootOption (BootOption)) {
2126 ImageInfo->LoadOptionsSize = BootOption->OptionalDataSize;
2127 ImageInfo->LoadOptions = BootOption->OptionalData;
2128 }
2129
2130 //
2131 // Clean to NULL because the image is loaded directly from the firmwares boot manager.
2132 //
2133 ImageInfo->ParentHandle = NULL;
2134
2135 //
2136 // Before calling the image, enable the Watchdog Timer for 5 minutes period
2137 //
2138 gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
2139
2140 //
2141 // Write boot to OS performance data for UEFI boot
2142 //
2143 PERF_CODE (
2144 BmEndOfBdsPerfCode (NULL, NULL);
2145 );
2146
2147 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart));
2148
2149 Status = gBS->StartImage (ImageHandle, &BootOption->ExitDataSize, &BootOption->ExitData);
2150 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status));
2151 BootOption->Status = Status;
2152
2153 //
2154 // Destroy the RAM disk
2155 //
2156 if (RamDiskDevicePath != NULL) {
2157 BmDestroyRamDisk (RamDiskDevicePath);
2158 FreePool (RamDiskDevicePath);
2159 }
2160
2161 if (EFI_ERROR (Status)) {
2162 //
2163 // Report Status Code with the failure status to indicate that boot failure
2164 //
2165 BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED, Status);
2166 }
2167
2168 PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32)OptionNumber);
2169
2170 //
2171 // Clear the Watchdog Timer after the image returns
2172 //
2173 gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
2174
2175 //
2176 // Set Logo status invalid after trying one boot option
2177 //
2178 BootLogo = NULL;
2179 Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **)&BootLogo);
2180 if (!EFI_ERROR (Status) && (BootLogo != NULL)) {
2181 Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0);
2182 ASSERT_EFI_ERROR (Status);
2183 }
2184
2185 //
2186 // Clear Boot Current
2187 //
2188 Status = gRT->SetVariable (
2189 L"BootCurrent",
2190 &gEfiGlobalVariableGuid,
2191 0,
2192 0,
2193 NULL
2194 );
2195 //
2196 // Deleting variable with current variable implementation shouldn't fail.
2197 // When BootXXXX (e.g.: BootManagerMenu) boots BootYYYY, exiting BootYYYY causes BootCurrent deleted,
2198 // exiting BootXXXX causes deleting BootCurrent returns EFI_NOT_FOUND.
2199 //
2200 ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);
2201}
2202
2203/**
2204 Check whether there is a instance in BlockIoDevicePath, which contain multi device path
2205 instances, has the same partition node with HardDriveDevicePath device path
2206
2207 @param BlockIoDevicePath Multi device path instances which need to check
2208 @param HardDriveDevicePath A device path which starts with a hard drive media
2209 device path.
2210
2211 @retval TRUE There is a matched device path instance.
2212 @retval FALSE There is no matched device path instance.
2213
2214**/
2215BOOLEAN
2216BmMatchPartitionDevicePathNode (
2217 IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath,
2218 IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath
2219 )
2220{
2221 HARDDRIVE_DEVICE_PATH *Node;
2222
2223 if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) {
2224 return FALSE;
2225 }
2226
2227 //
2228 // Match all the partition device path nodes including the nested partition nodes
2229 //
2230 while (!IsDevicePathEnd (BlockIoDevicePath)) {
2231 if ((DevicePathType (BlockIoDevicePath) == MEDIA_DEVICE_PATH) &&
2232 (DevicePathSubType (BlockIoDevicePath) == MEDIA_HARDDRIVE_DP)
2233 )
2234 {
2235 //
2236 // See if the harddrive device path in blockio matches the orig Hard Drive Node
2237 //
2238 Node = (HARDDRIVE_DEVICE_PATH *)BlockIoDevicePath;
2239
2240 //
2241 // Match Signature and PartitionNumber.
2242 // Unused bytes in Signature are initiaized with zeros.
2243 //
2244 if ((Node->PartitionNumber == HardDriveDevicePath->PartitionNumber) &&
2245 (Node->MBRType == HardDriveDevicePath->MBRType) &&
2246 (Node->SignatureType == HardDriveDevicePath->SignatureType) &&
2247 (CompareMem (Node->Signature, HardDriveDevicePath->Signature, sizeof (Node->Signature)) == 0))
2248 {
2249 return TRUE;
2250 }
2251 }
2252
2253 BlockIoDevicePath = NextDevicePathNode (BlockIoDevicePath);
2254 }
2255
2256 return FALSE;
2257}
2258
2259/**
2260 Emuerate all possible bootable medias in the following order:
2261 1. Removable BlockIo - The boot option only points to the removable media
2262 device, like USB key, DVD, Floppy etc.
2263 2. Fixed BlockIo - The boot option only points to a Fixed blockIo device,
2264 like HardDisk.
2265 3. Non-BlockIo SimpleFileSystem - The boot option points to a device supporting
2266 SimpleFileSystem Protocol, but not supporting BlockIo
2267 protocol.
2268 4. LoadFile - The boot option points to the media supporting
2269 LoadFile protocol.
2270 Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot Behavior
2271
2272 @param BootOptionCount Return the boot option count which has been found.
2273
2274 @retval Pointer to the boot option array.
2275**/
2276EFI_BOOT_MANAGER_LOAD_OPTION *
2277BmEnumerateBootOptions (
2278 UINTN *BootOptionCount
2279 )
2280{
2281 EFI_STATUS Status;
2282 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
2283 UINTN HandleCount;
2284 EFI_HANDLE *Handles;
2285 EFI_BLOCK_IO_PROTOCOL *BlkIo;
2286 UINTN Removable;
2287 UINTN Index;
2288 CHAR16 *Description;
2289
2290 ASSERT (BootOptionCount != NULL);
2291
2292 *BootOptionCount = 0;
2293 BootOptions = NULL;
2294
2295 //
2296 // Parse removable block io followed by fixed block io
2297 //
2298 gBS->LocateHandleBuffer (
2299 ByProtocol,
2300 &gEfiBlockIoProtocolGuid,
2301 NULL,
2302 &HandleCount,
2303 &Handles
2304 );
2305
2306 for (Removable = 0; Removable < 2; Removable++) {
2307 for (Index = 0; Index < HandleCount; Index++) {
2308 Status = gBS->HandleProtocol (
2309 Handles[Index],
2310 &gEfiBlockIoProtocolGuid,
2311 (VOID **)&BlkIo
2312 );
2313 if (EFI_ERROR (Status)) {
2314 continue;
2315 }
2316
2317 //
2318 // Skip the logical partitions
2319 //
2320 if (BlkIo->Media->LogicalPartition) {
2321 continue;
2322 }
2323
2324 //
2325 // Skip the fixed block io then the removable block io
2326 //
2327 if (BlkIo->Media->RemovableMedia == ((Removable == 0) ? FALSE : TRUE)) {
2328 continue;
2329 }
2330
2331 //
2332 // Skip removable media if not present
2333 //
2334 if ((BlkIo->Media->RemovableMedia == TRUE) &&
2335 (BlkIo->Media->MediaPresent == FALSE))
2336 {
2337 continue;
2338 }
2339
2340 Description = BmGetBootDescription (Handles[Index]);
2341 BootOptions = ReallocatePool (
2342 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),
2343 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),
2344 BootOptions
2345 );
2346 ASSERT (BootOptions != NULL);
2347
2348 Status = EfiBootManagerInitializeLoadOption (
2349 &BootOptions[(*BootOptionCount)++],
2350 LoadOptionNumberUnassigned,
2351 LoadOptionTypeBoot,
2352 LOAD_OPTION_ACTIVE,
2353 Description,
2354 DevicePathFromHandle (Handles[Index]),
2355 NULL,
2356 0
2357 );
2358 ASSERT_EFI_ERROR (Status);
2359
2360 FreePool (Description);
2361 }
2362 }
2363
2364 if (HandleCount != 0) {
2365 FreePool (Handles);
2366 }
2367
2368 //
2369 // Parse simple file system not based on block io
2370 //
2371 gBS->LocateHandleBuffer (
2372 ByProtocol,
2373 &gEfiSimpleFileSystemProtocolGuid,
2374 NULL,
2375 &HandleCount,
2376 &Handles
2377 );
2378 for (Index = 0; Index < HandleCount; Index++) {
2379 Status = gBS->HandleProtocol (
2380 Handles[Index],
2381 &gEfiBlockIoProtocolGuid,
2382 (VOID **)&BlkIo
2383 );
2384 if (!EFI_ERROR (Status)) {
2385 //
2386 // Skip if the file system handle supports a BlkIo protocol, which we've handled in above
2387 //
2388 continue;
2389 }
2390
2391 Description = BmGetBootDescription (Handles[Index]);
2392 BootOptions = ReallocatePool (
2393 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),
2394 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),
2395 BootOptions
2396 );
2397 ASSERT (BootOptions != NULL);
2398
2399 Status = EfiBootManagerInitializeLoadOption (
2400 &BootOptions[(*BootOptionCount)++],
2401 LoadOptionNumberUnassigned,
2402 LoadOptionTypeBoot,
2403 LOAD_OPTION_ACTIVE,
2404 Description,
2405 DevicePathFromHandle (Handles[Index]),
2406 NULL,
2407 0
2408 );
2409 ASSERT_EFI_ERROR (Status);
2410 FreePool (Description);
2411 }
2412
2413 if (HandleCount != 0) {
2414 FreePool (Handles);
2415 }
2416
2417 //
2418 // Parse load file protocol
2419 //
2420 gBS->LocateHandleBuffer (
2421 ByProtocol,
2422 &gEfiLoadFileProtocolGuid,
2423 NULL,
2424 &HandleCount,
2425 &Handles
2426 );
2427 for (Index = 0; Index < HandleCount; Index++) {
2428 //
2429 // Ignore BootManagerMenu. its boot option will be created by EfiBootManagerGetBootManagerMenu().
2430 //
2431 if (BmIsBootManagerMenuFilePath (DevicePathFromHandle (Handles[Index]))) {
2432 continue;
2433 }
2434
2435 Description = BmGetBootDescription (Handles[Index]);
2436 BootOptions = ReallocatePool (
2437 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),
2438 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),
2439 BootOptions
2440 );
2441 ASSERT (BootOptions != NULL);
2442
2443 Status = EfiBootManagerInitializeLoadOption (
2444 &BootOptions[(*BootOptionCount)++],
2445 LoadOptionNumberUnassigned,
2446 LoadOptionTypeBoot,
2447 LOAD_OPTION_ACTIVE,
2448 Description,
2449 DevicePathFromHandle (Handles[Index]),
2450 NULL,
2451 0
2452 );
2453 ASSERT_EFI_ERROR (Status);
2454 FreePool (Description);
2455 }
2456
2457 if (HandleCount != 0) {
2458 FreePool (Handles);
2459 }
2460
2461 BmMakeBootOptionDescriptionUnique (BootOptions, *BootOptionCount);
2462 return BootOptions;
2463}
2464
2465/**
2466 The function enumerates all boot options, creates them and registers them in the BootOrder variable.
2467**/
2468VOID
2469EFIAPI
2470EfiBootManagerRefreshAllBootOption (
2471 VOID
2472 )
2473{
2474 EFI_STATUS Status;
2475 EFI_BOOT_MANAGER_LOAD_OPTION *NvBootOptions;
2476 UINTN NvBootOptionCount;
2477 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
2478 UINTN BootOptionCount;
2479 EFI_BOOT_MANAGER_LOAD_OPTION *UpdatedBootOptions;
2480 UINTN UpdatedBootOptionCount;
2481 UINTN Index;
2482 EDKII_PLATFORM_BOOT_MANAGER_PROTOCOL *PlatformBootManager;
2483
2484 //
2485 // Optionally refresh the legacy boot option
2486 //
2487 if (mBmRefreshLegacyBootOption != NULL) {
2488 mBmRefreshLegacyBootOption ();
2489 }
2490
2491 BootOptions = BmEnumerateBootOptions (&BootOptionCount);
2492
2493 //
2494 // Mark the boot option as added by BDS by setting OptionalData to a special GUID
2495 //
2496 for (Index = 0; Index < BootOptionCount; Index++) {
2497 BootOptions[Index].OptionalData = AllocateCopyPool (sizeof (EFI_GUID), &mBmAutoCreateBootOptionGuid);
2498 BootOptions[Index].OptionalDataSize = sizeof (EFI_GUID);
2499 }
2500
2501 //
2502 // Locate Platform Boot Options Protocol
2503 //
2504 Status = gBS->LocateProtocol (
2505 &gEdkiiPlatformBootManagerProtocolGuid,
2506 NULL,
2507 (VOID **)&PlatformBootManager
2508 );
2509 if (!EFI_ERROR (Status)) {
2510 //
2511 // If found, call platform specific refresh to all auto enumerated and NV
2512 // boot options.
2513 //
2514 Status = PlatformBootManager->RefreshAllBootOptions (
2515 (CONST EFI_BOOT_MANAGER_LOAD_OPTION *)BootOptions,
2516 (CONST UINTN)BootOptionCount,
2517 &UpdatedBootOptions,
2518 &UpdatedBootOptionCount
2519 );
2520 if (!EFI_ERROR (Status)) {
2521 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
2522 BootOptions = UpdatedBootOptions;
2523 BootOptionCount = UpdatedBootOptionCount;
2524 }
2525 }
2526
2527 NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot);
2528
2529 //
2530 // Remove invalid EFI boot options from NV
2531 //
2532 for (Index = 0; Index < NvBootOptionCount; Index++) {
2533 if (((DevicePathType (NvBootOptions[Index].FilePath) != BBS_DEVICE_PATH) ||
2534 (DevicePathSubType (NvBootOptions[Index].FilePath) != BBS_BBS_DP)
2535 ) && BmIsAutoCreateBootOption (&NvBootOptions[Index])
2536 )
2537 {
2538 //
2539 // Only check those added by BDS
2540 // so that the boot options added by end-user or OS installer won't be deleted
2541 //
2542 if (EfiBootManagerFindLoadOption (&NvBootOptions[Index], BootOptions, BootOptionCount) == -1) {
2543 Status = EfiBootManagerDeleteLoadOptionVariable (NvBootOptions[Index].OptionNumber, LoadOptionTypeBoot);
2544 //
2545 // Deleting variable with current variable implementation shouldn't fail.
2546 //
2547 ASSERT_EFI_ERROR (Status);
2548 }
2549 }
2550 }
2551
2552 //
2553 // Add new EFI boot options to NV
2554 //
2555 for (Index = 0; Index < BootOptionCount; Index++) {
2556 if (EfiBootManagerFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == -1) {
2557 EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN)-1);
2558 //
2559 // Try best to add the boot options so continue upon failure.
2560 //
2561 }
2562 }
2563
2564 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
2565 EfiBootManagerFreeLoadOptions (NvBootOptions, NvBootOptionCount);
2566}
2567
2568/**
2569 This function is called to get or create the boot option for the Boot Manager Menu.
2570
2571 The Boot Manager Menu is shown after successfully booting a boot option.
2572 This function will first try to search the BootManagerMenuFile is in the same FV as
2573 the module links to this library. If fails, it will search in all FVs.
2574
2575 @param BootOption Return the boot option of the Boot Manager Menu
2576
2577 @retval EFI_SUCCESS Successfully register the Boot Manager Menu.
2578 @retval EFI_NOT_FOUND The Boot Manager Menu cannot be found.
2579 @retval others Return status of gRT->SetVariable (). BootOption still points
2580 to the Boot Manager Menu even the Status is not EFI_SUCCESS
2581 and EFI_NOT_FOUND.
2582**/
2583EFI_STATUS
2584BmRegisterBootManagerMenu (
2585 OUT EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
2586 )
2587{
2588 EFI_STATUS Status;
2589 CHAR16 *Description;
2590 UINTN DescriptionLength;
2591 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
2592 UINTN HandleCount;
2593 EFI_HANDLE *Handles;
2594 UINTN Index;
2595
2596 DevicePath = NULL;
2597 Description = NULL;
2598 //
2599 // Try to find BootManagerMenu from LoadFile protocol
2600 //
2601 gBS->LocateHandleBuffer (
2602 ByProtocol,
2603 &gEfiLoadFileProtocolGuid,
2604 NULL,
2605 &HandleCount,
2606 &Handles
2607 );
2608 for (Index = 0; Index < HandleCount; Index++) {
2609 if (BmIsBootManagerMenuFilePath (DevicePathFromHandle (Handles[Index]))) {
2610 DevicePath = DuplicateDevicePath (DevicePathFromHandle (Handles[Index]));
2611 Description = BmGetBootDescription (Handles[Index]);
2612 break;
2613 }
2614 }
2615
2616 if (HandleCount != 0) {
2617 FreePool (Handles);
2618 }
2619
2620 if (DevicePath == NULL) {
2621 Status = GetFileDevicePathFromAnyFv (
2622 PcdGetPtr (PcdBootManagerMenuFile),
2623 EFI_SECTION_PE32,
2624 0,
2625 &DevicePath
2626 );
2627 if (EFI_ERROR (Status)) {
2628 DEBUG ((DEBUG_WARN, "[Bds]BootManagerMenu FFS section can not be found, skip its boot option registration\n"));
2629 return EFI_NOT_FOUND;
2630 }
2631
2632 ASSERT (DevicePath != NULL);
2633 //
2634 // Get BootManagerMenu application's description from EFI User Interface Section.
2635 //
2636 Status = GetSectionFromAnyFv (
2637 PcdGetPtr (PcdBootManagerMenuFile),
2638 EFI_SECTION_USER_INTERFACE,
2639 0,
2640 (VOID **)&Description,
2641 &DescriptionLength
2642 );
2643 if (EFI_ERROR (Status)) {
2644 Description = NULL;
2645 }
2646 }
2647
2648 Status = EfiBootManagerInitializeLoadOption (
2649 BootOption,
2650 LoadOptionNumberUnassigned,
2651 LoadOptionTypeBoot,
2652 LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN,
2653 (Description != NULL) ? Description : L"Boot Manager Menu",
2654 DevicePath,
2655 NULL,
2656 0
2657 );
2658 ASSERT_EFI_ERROR (Status);
2659 FreePool (DevicePath);
2660 if (Description != NULL) {
2661 FreePool (Description);
2662 }
2663
2664 DEBUG_CODE (
2665 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
2666 UINTN BootOptionCount;
2667
2668 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
2669 ASSERT (EfiBootManagerFindLoadOption (BootOption, BootOptions, BootOptionCount) == -1);
2670 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
2671 );
2672
2673 return EfiBootManagerAddLoadOptionVariable (BootOption, (UINTN)-1);
2674}
2675
2676/**
2677 Return the boot option corresponding to the Boot Manager Menu.
2678 It may automatically create one if the boot option hasn't been created yet.
2679
2680 @param BootOption Return the Boot Manager Menu.
2681
2682 @retval EFI_SUCCESS The Boot Manager Menu is successfully returned.
2683 @retval EFI_NOT_FOUND The Boot Manager Menu cannot be found.
2684 @retval others Return status of gRT->SetVariable (). BootOption still points
2685 to the Boot Manager Menu even the Status is not EFI_SUCCESS
2686 and EFI_NOT_FOUND.
2687**/
2688EFI_STATUS
2689EFIAPI
2690EfiBootManagerGetBootManagerMenu (
2691 EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
2692 )
2693{
2694 EFI_STATUS Status;
2695 UINTN BootOptionCount;
2696 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
2697 UINTN Index;
2698
2699 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
2700
2701 for (Index = 0; Index < BootOptionCount; Index++) {
2702 if (BmIsBootManagerMenuFilePath (BootOptions[Index].FilePath)) {
2703 Status = EfiBootManagerInitializeLoadOption (
2704 BootOption,
2705 BootOptions[Index].OptionNumber,
2706 BootOptions[Index].OptionType,
2707 BootOptions[Index].Attributes,
2708 BootOptions[Index].Description,
2709 BootOptions[Index].FilePath,
2710 BootOptions[Index].OptionalData,
2711 BootOptions[Index].OptionalDataSize
2712 );
2713 ASSERT_EFI_ERROR (Status);
2714 break;
2715 }
2716 }
2717
2718 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
2719
2720 //
2721 // Automatically create the Boot#### for Boot Manager Menu when not found.
2722 //
2723 if (Index == BootOptionCount) {
2724 return BmRegisterBootManagerMenu (BootOption);
2725 } else {
2726 return EFI_SUCCESS;
2727 }
2728}
2729
2730/**
2731 Get the next possible full path pointing to the load option.
2732 The routine doesn't guarantee the returned full path points to an existing
2733 file, and it also doesn't guarantee the existing file is a valid load option.
2734 BmGetNextLoadOptionBuffer() guarantees.
2735
2736 @param FilePath The device path pointing to a load option.
2737 It could be a short-form device path.
2738 @param FullPath The full path returned by the routine in last call.
2739 Set to NULL in first call.
2740
2741 @return The next possible full path pointing to the load option.
2742 Caller is responsible to free the memory.
2743**/
2744EFI_DEVICE_PATH_PROTOCOL *
2745EFIAPI
2746EfiBootManagerGetNextLoadOptionDevicePath (
2747 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
2748 IN EFI_DEVICE_PATH_PROTOCOL *FullPath
2749 )
2750{
2751 return BmGetNextLoadOptionDevicePath (FilePath, FullPath);
2752}
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