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