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