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