VirtualBox

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

Last change on this file since 108793 was 105670, checked in by vboxsync, 8 months ago

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

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