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