VirtualBox

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

Last change on this file since 85718 was 85718, checked in by vboxsync, 5 years ago

Devices/EFI: Merge edk-stable202005 and make it build, bugref:4643

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