e6b2fe4d17cf6539a042ac9cc5f8fe4f8115a535
[efi/edk2/.git] / edk2 / IntelFrameworkModulePkg / Library / GenericBdsLib / BdsBoot.c
1 /** @file\r
2   BDS Lib functions which relate with create or process the boot option.\r
3 \r
4 Copyright (c) 2004 - 2010, Intel Corporation. <BR>\r
5 All rights reserved. This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution.  The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9 \r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12 \r
13 **/\r
14 \r
15 #include "InternalBdsLib.h"\r
16 \r
17 BOOLEAN mEnumBootDevice = FALSE;\r
18 \r
19 ///\r
20 /// This GUID is used for an EFI Variable that stores the front device pathes\r
21 /// for a partial device path that starts with the HD node.\r
22 ///\r
23 EFI_GUID  mHdBootVariablePrivateGuid = { 0xfab7e9e1, 0x39dd, 0x4f2b, { 0x84, 0x8, 0xe2, 0xe, 0x90, 0x6c, 0xb6, 0xde } };\r
24 \r
25 \r
26 \r
27 /**\r
28   Boot the legacy system with the boot option\r
29 \r
30   @param  Option                 The legacy boot option which have BBS device path\r
31 \r
32   @retval EFI_UNSUPPORTED        There is no legacybios protocol, do not support\r
33                                  legacy boot.\r
34   @retval EFI_STATUS             Return the status of LegacyBios->LegacyBoot ().\r
35 \r
36 **/\r
37 EFI_STATUS\r
38 BdsLibDoLegacyBoot (\r
39   IN  BDS_COMMON_OPTION           *Option\r
40   )\r
41 {\r
42   EFI_STATUS                Status;\r
43   EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;\r
44 \r
45   Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);\r
46   if (EFI_ERROR (Status)) {\r
47     //\r
48     // If no LegacyBios protocol we do not support legacy boot\r
49     //\r
50     return EFI_UNSUPPORTED;\r
51   }\r
52   //\r
53   // Notes: if we separate the int 19, then we don't need to refresh BBS\r
54   //\r
55   BdsRefreshBbsTableForBoot (Option);\r
56 \r
57   //\r
58   // Write boot to OS performance data for legacy boot.\r
59   //\r
60   PERF_CODE (\r
61     WriteBootToOsPerformanceData ();\r
62   );\r
63 \r
64   DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Legacy Boot: %S\n", Option->Description));\r
65   return LegacyBios->LegacyBoot (\r
66                       LegacyBios,\r
67                       (BBS_BBS_DEVICE_PATH *) Option->DevicePath,\r
68                       Option->LoadOptionsSize,\r
69                       Option->LoadOptions\r
70                       );\r
71 }\r
72 \r
73 /**\r
74   Internal function to check if the input boot option is a valid EFI NV Boot####.\r
75 \r
76   @param OptionToCheck  Boot option to be checked.\r
77 \r
78   @retval TRUE      This boot option matches a valid EFI NV Boot####.\r
79   @retval FALSE     If not.\r
80 \r
81 **/\r
82 BOOLEAN\r
83 IsBootOptionValidNVVarialbe (\r
84   IN  BDS_COMMON_OPTION             *OptionToCheck\r
85   )\r
86 {\r
87   LIST_ENTRY        TempList;\r
88   BDS_COMMON_OPTION *BootOption;\r
89   BOOLEAN           Valid;\r
90   CHAR16            OptionName[20];\r
91 \r
92   Valid = FALSE;\r
93 \r
94   InitializeListHead (&TempList);\r
95   UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", OptionToCheck->BootCurrent);\r
96 \r
97   BootOption = BdsLibVariableToOption (&TempList, OptionName);\r
98   if (BootOption == NULL) {\r
99     return FALSE;\r
100   }\r
101 \r
102   //\r
103   // If the Boot Option Number and Device Path matches, OptionToCheck matches a\r
104   // valid EFI NV Boot####.\r
105   //\r
106   if ((OptionToCheck->BootCurrent == BootOption->BootCurrent) &&\r
107       (CompareMem (OptionToCheck->DevicePath, BootOption->DevicePath, GetDevicePathSize (OptionToCheck->DevicePath)) == 0))\r
108       {\r
109     Valid = TRUE;\r
110   }\r
111 \r
112   FreePool (BootOption);\r
113 \r
114   return Valid;\r
115 }\r
116 /**\r
117   Process the boot option follow the UEFI specification and\r
118   special treat the legacy boot option with BBS_DEVICE_PATH.\r
119 \r
120   @param  Option                 The boot option need to be processed\r
121   @param  DevicePath             The device path which describe where to load the\r
122                                  boot image or the legacy BBS device path to boot\r
123                                  the legacy OS\r
124   @param  ExitDataSize           The size of exit data.\r
125   @param  ExitData               Data returned when Boot image failed.\r
126 \r
127   @retval EFI_SUCCESS            Boot from the input boot option successfully.\r
128   @retval EFI_NOT_FOUND          If the Device Path is not found in the system\r
129 \r
130 **/\r
131 EFI_STATUS\r
132 EFIAPI\r
133 BdsLibBootViaBootOption (\r
134   IN  BDS_COMMON_OPTION             *Option,\r
135   IN  EFI_DEVICE_PATH_PROTOCOL      *DevicePath,\r
136   OUT UINTN                         *ExitDataSize,\r
137   OUT CHAR16                        **ExitData OPTIONAL\r
138   )\r
139 {\r
140   EFI_STATUS                Status;\r
141   EFI_HANDLE                Handle;\r
142   EFI_HANDLE                ImageHandle;\r
143   EFI_DEVICE_PATH_PROTOCOL  *FilePath;\r
144   EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;\r
145   EFI_DEVICE_PATH_PROTOCOL  *WorkingDevicePath;\r
146   EFI_ACPI_S3_SAVE_PROTOCOL *AcpiS3Save;\r
147   LIST_ENTRY                TempBootLists;\r
148 \r
149   //\r
150   // Record the performance data for End of BDS\r
151   //\r
152   PERF_END(NULL, "BDS", NULL, 0);\r
153 \r
154   *ExitDataSize = 0;\r
155   *ExitData     = NULL;\r
156 \r
157   //\r
158   // Notes: put EFI64 ROM Shadow Solution\r
159   //\r
160   EFI64_SHADOW_ALL_LEGACY_ROM ();\r
161 \r
162   //\r
163   // Notes: this code can be remove after the s3 script table\r
164   // hook on the event EVT_SIGNAL_READY_TO_BOOT or\r
165   // EVT_SIGNAL_LEGACY_BOOT\r
166   //\r
167   Status = gBS->LocateProtocol (&gEfiAcpiS3SaveProtocolGuid, NULL, (VOID **) &AcpiS3Save);\r
168   if (!EFI_ERROR (Status)) {\r
169     AcpiS3Save->S3Save (AcpiS3Save, NULL);\r
170   }\r
171   //\r
172   // If it's Device Path that starts with a hard drive path, append it with the front part to compose a\r
173   // full device path\r
174   //\r
175   WorkingDevicePath = NULL;\r
176   if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&\r
177       (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)) {\r
178     WorkingDevicePath = BdsExpandPartitionPartialDevicePathToFull (\r
179                           (HARDDRIVE_DEVICE_PATH *)DevicePath\r
180                           );\r
181     if (WorkingDevicePath != NULL) {\r
182       DevicePath = WorkingDevicePath;\r
183     }\r
184   }\r
185   //\r
186   // Signal the EVT_SIGNAL_READY_TO_BOOT event\r
187   //\r
188   EfiSignalEventReadyToBoot();\r
189 \r
190 \r
191   //\r
192   // Set Boot Current\r
193   //\r
194   if (IsBootOptionValidNVVarialbe (Option)) {\r
195     //\r
196     // For a temporary boot (i.e. a boot by selected a EFI Shell using "Boot From File"), Boot Current is actually not valid.\r
197     // In this case, "BootCurrent" is not created.\r
198     // Only create the BootCurrent variable when it points to a valid Boot#### variable.\r
199     //\r
200     gRT->SetVariable (\r
201           L"BootCurrent",\r
202           &gEfiGlobalVariableGuid,\r
203           EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
204           sizeof (UINT16),\r
205           &Option->BootCurrent\r
206           );\r
207   }\r
208 \r
209   ASSERT (Option->DevicePath != NULL);\r
210   if ((DevicePathType (Option->DevicePath) == BBS_DEVICE_PATH) &&\r
211       (DevicePathSubType (Option->DevicePath) == BBS_BBS_DP)\r
212     ) {\r
213     //\r
214     // Check to see if we should legacy BOOT. If yes then do the legacy boot\r
215     //\r
216     return BdsLibDoLegacyBoot (Option);\r
217   }\r
218 \r
219   //\r
220   // If the boot option point to Internal FV shell, make sure it is valid\r
221   //\r
222   Status = BdsLibUpdateFvFileDevicePath (&DevicePath, PcdGetPtr(PcdShellFile));\r
223   if (!EFI_ERROR(Status)) {\r
224     if (Option->DevicePath != NULL) {\r
225       FreePool(Option->DevicePath);\r
226     }\r
227     Option->DevicePath  = AllocateZeroPool (GetDevicePathSize (DevicePath));\r
228     ASSERT(Option->DevicePath != NULL);\r
229     CopyMem (Option->DevicePath, DevicePath, GetDevicePathSize (DevicePath));\r
230     //\r
231     // Update the shell boot option\r
232     //\r
233     InitializeListHead (&TempBootLists);\r
234     BdsLibRegisterNewOption (&TempBootLists, DevicePath, L"EFI Internal Shell", L"BootOrder");\r
235 \r
236     //\r
237     // free the temporary device path created by BdsLibUpdateFvFileDevicePath()\r
238     //\r
239     FreePool (DevicePath);\r
240     DevicePath = Option->DevicePath;\r
241   }\r
242 \r
243   DEBUG_CODE_BEGIN();\r
244     UINTN                     DevicePathTypeValue;\r
245     CHAR16                    *HiiString;\r
246     CHAR16                    *BootStringNumber;\r
247     UINTN                     BufferSize;\r
248   \r
249     DevicePathTypeValue = BdsGetBootTypeFromDevicePath (Option->DevicePath);\r
250   \r
251     //\r
252     // store number string of boot option temporary.\r
253     //\r
254     HiiString = NULL;\r
255     switch (DevicePathTypeValue) {\r
256     case BDS_EFI_ACPI_FLOPPY_BOOT:\r
257       HiiString = L"EFI Floppy";\r
258       break;\r
259     case BDS_EFI_MEDIA_CDROM_BOOT:\r
260     case BDS_EFI_MESSAGE_SATA_BOOT:\r
261     case BDS_EFI_MESSAGE_ATAPI_BOOT:\r
262       HiiString = L"EFI DVD/CDROM";\r
263       break;\r
264     case BDS_EFI_MESSAGE_USB_DEVICE_BOOT:\r
265       HiiString = L"EFI USB Device";\r
266       break;\r
267     case BDS_EFI_MESSAGE_SCSI_BOOT:\r
268       HiiString = L"EFI SCSI Device";\r
269       break;\r
270     case BDS_EFI_MESSAGE_MISC_BOOT:\r
271       HiiString = L"EFI Misc Device";\r
272       break;\r
273     case BDS_EFI_MESSAGE_MAC_BOOT:\r
274       HiiString = L"EFI Network";\r
275       break;\r
276     case BBS_DEVICE_PATH:\r
277       //\r
278       // Do nothing for legacy boot option.\r
279       //\r
280       break;\r
281     default:\r
282       DEBUG((EFI_D_INFO, "Can not find HiiString for given device path type 0x%x\n", DevicePathTypeValue));\r
283     }\r
284 \r
285     //\r
286     // If found Hii description string then cat Hii string with original description.\r
287     //\r
288     if (HiiString != NULL) {\r
289       BootStringNumber = Option->Description;\r
290       BufferSize = StrSize(BootStringNumber);\r
291       BufferSize += StrSize(HiiString);\r
292       Option->Description = AllocateZeroPool(BufferSize);\r
293       ASSERT (Option->Description != NULL);\r
294       StrCpy (Option->Description, HiiString);\r
295       if (StrnCmp (BootStringNumber, L"0", 1) != 0) {\r
296         StrCat (Option->Description, L" ");\r
297         StrCat (Option->Description, BootStringNumber);\r
298       } \r
299       \r
300       FreePool (BootStringNumber);\r
301     }\r
302   \r
303     DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Booting %S\n", Option->Description));\r
304     \r
305   DEBUG_CODE_END();\r
306   \r
307   Status = gBS->LoadImage (\r
308                   TRUE,\r
309                   mBdsImageHandle,\r
310                   DevicePath,\r
311                   NULL,\r
312                   0,\r
313                   &ImageHandle\r
314                   );\r
315 \r
316   //\r
317   // If we didn't find an image directly, we need to try as if it is a removable device boot option\r
318   // and load the image according to the default boot behavior for removable device.\r
319   //\r
320   if (EFI_ERROR (Status)) {\r
321     //\r
322     // check if there is a bootable removable media could be found in this device path ,\r
323     // and get the bootable media handle\r
324     //\r
325     Handle = BdsLibGetBootableHandle(DevicePath);\r
326     if (Handle == NULL) {\r
327        goto Done;\r
328     }\r
329     //\r
330     // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media\r
331     //  machinename is ia32, ia64, x64, ...\r
332     //\r
333     FilePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);\r
334     if (FilePath != NULL) {\r
335       Status = gBS->LoadImage (\r
336                       TRUE,\r
337                       mBdsImageHandle,\r
338                       FilePath,\r
339                       NULL,\r
340                       0,\r
341                       &ImageHandle\r
342                       );\r
343       if (EFI_ERROR (Status)) {\r
344         //\r
345         // The DevicePath failed, and it's not a valid\r
346         // removable media device.\r
347         //\r
348         goto Done;\r
349       }\r
350     }\r
351   }\r
352 \r
353   if (EFI_ERROR (Status)) {\r
354     //\r
355     // It there is any error from the Boot attempt exit now.\r
356     //\r
357     goto Done;\r
358   }\r
359   //\r
360   // Provide the image with it's load options\r
361   //\r
362   Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);\r
363   ASSERT_EFI_ERROR (Status);\r
364 \r
365   if (Option->LoadOptionsSize != 0) {\r
366     ImageInfo->LoadOptionsSize  = Option->LoadOptionsSize;\r
367     ImageInfo->LoadOptions      = Option->LoadOptions;\r
368   }\r
369   //\r
370   // Before calling the image, enable the Watchdog Timer for\r
371   // the 5 Minute period\r
372   //\r
373   gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);\r
374 \r
375   //\r
376   // Write boot to OS performance data for UEFI boot\r
377   //\r
378   PERF_CODE (\r
379     WriteBootToOsPerformanceData ();\r
380   );\r
381 \r
382   Status = gBS->StartImage (ImageHandle, ExitDataSize, ExitData);\r
383   DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status));\r
384 \r
385   //\r
386   // Clear the Watchdog Timer after the image returns\r
387   //\r
388   gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);\r
389 \r
390 Done:\r
391   //\r
392   // Clear Boot Current\r
393   //\r
394   gRT->SetVariable (\r
395         L"BootCurrent",\r
396         &gEfiGlobalVariableGuid,\r
397         EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
398         0,\r
399         &Option->BootCurrent\r
400         );\r
401 \r
402   return Status;\r
403 }\r
404 \r
405 \r
406 /**\r
407   Expand a device path that starts with a hard drive media device path node to be a\r
408   full device path that includes the full hardware path to the device. We need\r
409   to do this so it can be booted. As an optimization the front match (the part point\r
410   to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable\r
411   so a connect all is not required on every boot. All successful history device path\r
412   which point to partition node (the front part) will be saved.\r
413 \r
414   @param  HardDriveDevicePath    EFI Device Path to boot, if it starts with a hard\r
415                                  drive media device path.\r
416   @return A Pointer to the full device path or NULL if a valid Hard Drive devic path\r
417           cannot be found.\r
418 \r
419 **/\r
420 EFI_DEVICE_PATH_PROTOCOL *\r
421 EFIAPI\r
422 BdsExpandPartitionPartialDevicePathToFull (\r
423   IN  HARDDRIVE_DEVICE_PATH      *HardDriveDevicePath\r
424   )\r
425 {\r
426   EFI_STATUS                Status;\r
427   UINTN                     BlockIoHandleCount;\r
428   EFI_HANDLE                *BlockIoBuffer;\r
429   EFI_DEVICE_PATH_PROTOCOL  *FullDevicePath;\r
430   EFI_DEVICE_PATH_PROTOCOL  *BlockIoDevicePath;\r
431   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
432   UINTN                     Index;\r
433   UINTN                     InstanceNum;\r
434   EFI_DEVICE_PATH_PROTOCOL  *CachedDevicePath;\r
435   EFI_DEVICE_PATH_PROTOCOL  *TempNewDevicePath;\r
436   UINTN                     CachedDevicePathSize;\r
437   BOOLEAN                   DeviceExist;\r
438   BOOLEAN                   NeedAdjust;\r
439   EFI_DEVICE_PATH_PROTOCOL  *Instance;\r
440   UINTN                     Size;\r
441 \r
442   FullDevicePath = NULL;\r
443   //\r
444   // Check if there is prestore 'HDDP' variable.\r
445   // If exist, search the front path which point to partition node in the variable instants.\r
446   // If fail to find or 'HDDP' not exist, reconnect all and search in all system\r
447   //\r
448   CachedDevicePath = BdsLibGetVariableAndSize (\r
449                       L"HDDP",\r
450                       &mHdBootVariablePrivateGuid,\r
451                       &CachedDevicePathSize\r
452                       );\r
453 \r
454   if (CachedDevicePath != NULL) {\r
455     TempNewDevicePath = CachedDevicePath;\r
456     DeviceExist = FALSE;\r
457     NeedAdjust = FALSE;\r
458     do {\r
459       //\r
460       // Check every instance of the variable\r
461       // First, check whether the instance contain the partition node, which is needed for distinguishing  multi\r
462       // partial partition boot option. Second, check whether the instance could be connected.\r
463       //\r
464       Instance  = GetNextDevicePathInstance (&TempNewDevicePath, &Size);\r
465       if (MatchPartitionDevicePathNode (Instance, HardDriveDevicePath)) {\r
466         //\r
467         // Connect the device path instance, the device path point to hard drive media device path node\r
468         // e.g. ACPI() /PCI()/ATA()/Partition()\r
469         //\r
470         Status = BdsLibConnectDevicePath (Instance);\r
471         if (!EFI_ERROR (Status)) {\r
472           DeviceExist = TRUE;\r
473           break;\r
474         }\r
475       }\r
476       //\r
477       // Come here means the first instance is not matched\r
478       //\r
479       NeedAdjust = TRUE;\r
480       FreePool(Instance);\r
481     } while (TempNewDevicePath != NULL);\r
482 \r
483     if (DeviceExist) {\r
484       //\r
485       // Find the matched device path.\r
486       // Append the file path information from the boot option and return the fully expanded device path.\r
487       //\r
488       DevicePath     = NextDevicePathNode ((EFI_DEVICE_PATH_PROTOCOL *) HardDriveDevicePath);\r
489       FullDevicePath = AppendDevicePath (Instance, DevicePath);\r
490 \r
491       //\r
492       // Adjust the 'HDDP' instances sequence if the matched one is not first one.\r
493       //\r
494       if (NeedAdjust) {\r
495         //\r
496         // First delete the matched instance.\r
497         //\r
498         TempNewDevicePath = CachedDevicePath;\r
499         CachedDevicePath  = BdsLibDelPartMatchInstance (CachedDevicePath, Instance );\r
500         FreePool (TempNewDevicePath);\r
501 \r
502         //\r
503         // Second, append the remaining path after the matched instance\r
504         //\r
505         TempNewDevicePath = CachedDevicePath;\r
506         CachedDevicePath = AppendDevicePathInstance (Instance, CachedDevicePath );\r
507         FreePool (TempNewDevicePath);\r
508         //\r
509         // Save the matching Device Path so we don't need to do a connect all next time\r
510         //\r
511         Status = gRT->SetVariable (\r
512                         L"HDDP",\r
513                         &mHdBootVariablePrivateGuid,\r
514                         EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
515                         GetDevicePathSize (CachedDevicePath),\r
516                         CachedDevicePath\r
517                         );\r
518       }\r
519 \r
520       FreePool (Instance);\r
521       FreePool (CachedDevicePath);\r
522       return FullDevicePath;\r
523     }\r
524   }\r
525 \r
526   //\r
527   // If we get here we fail to find or 'HDDP' not exist, and now we need\r
528   // to search all devices in the system for a matched partition\r
529   //\r
530   BdsLibConnectAllDriversToAllControllers ();\r
531   Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer);\r
532   if (EFI_ERROR (Status) || BlockIoHandleCount == 0 || BlockIoBuffer == NULL) {\r
533     //\r
534     // If there was an error or there are no device handles that support\r
535     // the BLOCK_IO Protocol, then return.\r
536     //\r
537     return NULL;\r
538   }\r
539   //\r
540   // Loop through all the device handles that support the BLOCK_IO Protocol\r
541   //\r
542   for (Index = 0; Index < BlockIoHandleCount; Index++) {\r
543 \r
544     Status = gBS->HandleProtocol (BlockIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID *) &BlockIoDevicePath);\r
545     if (EFI_ERROR (Status) || BlockIoDevicePath == NULL) {\r
546       continue;\r
547     }\r
548 \r
549     if (MatchPartitionDevicePathNode (BlockIoDevicePath, HardDriveDevicePath)) {\r
550       //\r
551       // Find the matched partition device path\r
552       //\r
553       DevicePath    = NextDevicePathNode ((EFI_DEVICE_PATH_PROTOCOL *) HardDriveDevicePath);\r
554       FullDevicePath = AppendDevicePath (BlockIoDevicePath, DevicePath);\r
555 \r
556       //\r
557       // Save the matched partition device path in 'HDDP' variable\r
558       //\r
559       if (CachedDevicePath != NULL) {\r
560         //\r
561         // Save the matched partition device path as first instance of 'HDDP' variable\r
562         //\r
563         if (BdsLibMatchDevicePaths (CachedDevicePath, BlockIoDevicePath)) {\r
564           TempNewDevicePath = CachedDevicePath;\r
565           CachedDevicePath = BdsLibDelPartMatchInstance (CachedDevicePath, BlockIoDevicePath);\r
566           FreePool(TempNewDevicePath);\r
567 \r
568           TempNewDevicePath = CachedDevicePath;\r
569           CachedDevicePath = AppendDevicePathInstance (BlockIoDevicePath, CachedDevicePath);\r
570           if (TempNewDevicePath != NULL) {\r
571             FreePool(TempNewDevicePath);\r
572           }\r
573         } else {\r
574           TempNewDevicePath = CachedDevicePath;\r
575           CachedDevicePath = AppendDevicePathInstance (BlockIoDevicePath, CachedDevicePath);\r
576           FreePool(TempNewDevicePath);\r
577         }\r
578         //\r
579         // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller\r
580         // If the user try to boot many OS in different HDs or partitions, in theory, the 'HDDP' variable maybe become larger and larger.\r
581         //\r
582         InstanceNum = 0;\r
583         ASSERT (CachedDevicePath != NULL);\r
584         TempNewDevicePath = CachedDevicePath;\r
585         while (!IsDevicePathEnd (TempNewDevicePath)) {\r
586           TempNewDevicePath = NextDevicePathNode (TempNewDevicePath);\r
587           //\r
588           // Parse one instance\r
589           //\r
590           while (!IsDevicePathEndType (TempNewDevicePath)) {\r
591             TempNewDevicePath = NextDevicePathNode (TempNewDevicePath);\r
592           }\r
593           InstanceNum++;\r
594           //\r
595           // If the CachedDevicePath variable contain too much instance, only remain 12 instances.\r
596           //\r
597           if (InstanceNum >= 12) {\r
598             SetDevicePathEndNode (TempNewDevicePath);\r
599             break;\r
600           }\r
601         }\r
602       } else {\r
603         CachedDevicePath = DuplicateDevicePath (BlockIoDevicePath);\r
604       }\r
605 \r
606       //\r
607       // Save the matching Device Path so we don't need to do a connect all next time\r
608       //\r
609       Status = gRT->SetVariable (\r
610                       L"HDDP",\r
611                       &mHdBootVariablePrivateGuid,\r
612                       EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
613                       GetDevicePathSize (CachedDevicePath),\r
614                       CachedDevicePath\r
615                       );\r
616 \r
617       break;\r
618     }\r
619   }\r
620 \r
621   if (CachedDevicePath != NULL) {\r
622     FreePool (CachedDevicePath);\r
623   }\r
624   if (BlockIoBuffer != NULL) {\r
625     FreePool (BlockIoBuffer);\r
626   }\r
627   return FullDevicePath;\r
628 }\r
629 \r
630 /**\r
631   Check whether there is a instance in BlockIoDevicePath, which contain multi device path\r
632   instances, has the same partition node with HardDriveDevicePath device path\r
633 \r
634   @param  BlockIoDevicePath      Multi device path instances which need to check\r
635   @param  HardDriveDevicePath    A device path which starts with a hard drive media\r
636                                  device path.\r
637 \r
638   @retval TRUE                   There is a matched device path instance.\r
639   @retval FALSE                  There is no matched device path instance.\r
640 \r
641 **/\r
642 BOOLEAN\r
643 EFIAPI\r
644 MatchPartitionDevicePathNode (\r
645   IN  EFI_DEVICE_PATH_PROTOCOL   *BlockIoDevicePath,\r
646   IN  HARDDRIVE_DEVICE_PATH      *HardDriveDevicePath\r
647   )\r
648 {\r
649   HARDDRIVE_DEVICE_PATH     *TmpHdPath;\r
650   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
651   BOOLEAN                   Match;\r
652   EFI_DEVICE_PATH_PROTOCOL  *BlockIoHdDevicePathNode;\r
653 \r
654   if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) {\r
655     return FALSE;\r
656   }\r
657 \r
658   //\r
659   // Make PreviousDevicePath == the device path node before the end node\r
660   //\r
661   DevicePath              = BlockIoDevicePath;\r
662   BlockIoHdDevicePathNode = NULL;\r
663 \r
664   //\r
665   // find the partition device path node\r
666   //\r
667   while (!IsDevicePathEnd (DevicePath)) {\r
668     if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&\r
669         (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)\r
670         ) {\r
671       BlockIoHdDevicePathNode = DevicePath;\r
672       break;\r
673     }\r
674 \r
675     DevicePath = NextDevicePathNode (DevicePath);\r
676   }\r
677 \r
678   if (BlockIoHdDevicePathNode == NULL) {\r
679     return FALSE;\r
680   }\r
681   //\r
682   // See if the harddrive device path in blockio matches the orig Hard Drive Node\r
683   //\r
684   TmpHdPath = (HARDDRIVE_DEVICE_PATH *) BlockIoHdDevicePathNode;\r
685   Match = FALSE;\r
686 \r
687   //\r
688   // Check for the match\r
689   //\r
690   if ((TmpHdPath->MBRType == HardDriveDevicePath->MBRType) &&\r
691       (TmpHdPath->SignatureType == HardDriveDevicePath->SignatureType)) {\r
692     switch (TmpHdPath->SignatureType) {\r
693     case SIGNATURE_TYPE_GUID:\r
694       Match = CompareGuid ((EFI_GUID *)TmpHdPath->Signature, (EFI_GUID *)HardDriveDevicePath->Signature);\r
695       break;\r
696     case SIGNATURE_TYPE_MBR:\r
697       Match = (BOOLEAN)(*((UINT32 *)(&(TmpHdPath->Signature[0]))) == ReadUnaligned32((UINT32 *)(&(HardDriveDevicePath->Signature[0]))));\r
698       break;\r
699     default:\r
700       Match = FALSE;\r
701       break;\r
702     }\r
703   }\r
704 \r
705   return Match;\r
706 }\r
707 \r
708 /**\r
709   Delete the boot option associated with the handle passed in.\r
710 \r
711   @param  Handle                 The handle which present the device path to create\r
712                                  boot option\r
713 \r
714   @retval EFI_SUCCESS            Delete the boot option success\r
715   @retval EFI_NOT_FOUND          If the Device Path is not found in the system\r
716   @retval EFI_OUT_OF_RESOURCES   Lack of memory resource\r
717   @retval Other                  Error return value from SetVariable()\r
718 \r
719 **/\r
720 EFI_STATUS\r
721 BdsLibDeleteOptionFromHandle (\r
722   IN  EFI_HANDLE                 Handle\r
723   )\r
724 {\r
725   UINT16                    *BootOrder;\r
726   UINT8                     *BootOptionVar;\r
727   UINTN                     BootOrderSize;\r
728   UINTN                     BootOptionSize;\r
729   EFI_STATUS                Status;\r
730   UINTN                     Index;\r
731   UINT16                    BootOption[BOOT_OPTION_MAX_CHAR];\r
732   UINTN                     DevicePathSize;\r
733   UINTN                     OptionDevicePathSize;\r
734   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
735   EFI_DEVICE_PATH_PROTOCOL  *OptionDevicePath;\r
736   UINT8                     *TempPtr;\r
737 \r
738   Status        = EFI_SUCCESS;\r
739   BootOrder     = NULL;\r
740   BootOrderSize = 0;\r
741 \r
742   //\r
743   // Check "BootOrder" variable, if no, means there is no any boot order.\r
744   //\r
745   BootOrder = BdsLibGetVariableAndSize (\r
746                 L"BootOrder",\r
747                 &gEfiGlobalVariableGuid,\r
748                 &BootOrderSize\r
749                 );\r
750   if (BootOrder == NULL) {\r
751     return EFI_NOT_FOUND;\r
752   }\r
753 \r
754   //\r
755   // Convert device handle to device path protocol instance\r
756   //\r
757   DevicePath = DevicePathFromHandle (Handle);\r
758   if (DevicePath == NULL) {\r
759     return EFI_NOT_FOUND;\r
760   }\r
761   DevicePathSize = GetDevicePathSize (DevicePath);\r
762 \r
763   //\r
764   // Loop all boot order variable and find the matching device path\r
765   //\r
766   Index = 0;\r
767   while (Index < BootOrderSize / sizeof (UINT16)) {\r
768     UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);\r
769     BootOptionVar = BdsLibGetVariableAndSize (\r
770                       BootOption,\r
771                       &gEfiGlobalVariableGuid,\r
772                       &BootOptionSize\r
773                       );\r
774 \r
775     if (BootOptionVar == NULL) {\r
776       FreePool (BootOrder);\r
777       return EFI_OUT_OF_RESOURCES;\r
778     }\r
779 \r
780     TempPtr = BootOptionVar;\r
781     TempPtr += sizeof (UINT32) + sizeof (UINT16);\r
782     TempPtr += StrSize ((CHAR16 *) TempPtr);\r
783     OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;\r
784     OptionDevicePathSize = GetDevicePathSize (OptionDevicePath);\r
785 \r
786     //\r
787     // Check whether the device path match\r
788     //\r
789     if ((OptionDevicePathSize == DevicePathSize) &&\r
790         (CompareMem (DevicePath, OptionDevicePath, DevicePathSize) == 0)) {\r
791       BdsDeleteBootOption (BootOrder[Index], BootOrder, &BootOrderSize);\r
792       FreePool (BootOptionVar);\r
793       break;\r
794     }\r
795 \r
796     FreePool (BootOptionVar);\r
797     Index++;\r
798   }\r
799 \r
800   //\r
801   // Adjust number of boot option for "BootOrder" variable.\r
802   //\r
803   Status = gRT->SetVariable (\r
804                   L"BootOrder",\r
805                   &gEfiGlobalVariableGuid,\r
806                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
807                   BootOrderSize,\r
808                   BootOrder\r
809                   );\r
810 \r
811   FreePool (BootOrder);\r
812 \r
813   return Status;\r
814 }\r
815 \r
816 \r
817 /**\r
818   Delete all invalid EFI boot options.\r
819 \r
820   @retval EFI_SUCCESS            Delete all invalid boot option success\r
821   @retval EFI_NOT_FOUND          Variable "BootOrder" is not found\r
822   @retval EFI_OUT_OF_RESOURCES   Lack of memory resource\r
823   @retval Other                  Error return value from SetVariable()\r
824 \r
825 **/\r
826 EFI_STATUS\r
827 BdsDeleteAllInvalidEfiBootOption (\r
828   VOID\r
829   )\r
830 {\r
831   UINT16                    *BootOrder;\r
832   UINT8                     *BootOptionVar;\r
833   UINTN                     BootOrderSize;\r
834   UINTN                     BootOptionSize;\r
835   EFI_STATUS                Status;\r
836   UINTN                     Index;\r
837   UINTN                     Index2;\r
838   UINT16                    BootOption[BOOT_OPTION_MAX_CHAR];\r
839   EFI_DEVICE_PATH_PROTOCOL  *OptionDevicePath;\r
840   UINT8                     *TempPtr;\r
841   CHAR16                    *Description;\r
842 \r
843   Status        = EFI_SUCCESS;\r
844   BootOrder     = NULL;\r
845   BootOrderSize = 0;\r
846 \r
847   //\r
848   // Check "BootOrder" variable firstly, this variable hold the number of boot options\r
849   //\r
850   BootOrder = BdsLibGetVariableAndSize (\r
851                 L"BootOrder",\r
852                 &gEfiGlobalVariableGuid,\r
853                 &BootOrderSize\r
854                 );\r
855   if (NULL == BootOrder) {\r
856     return EFI_NOT_FOUND;\r
857   }\r
858 \r
859   Index = 0;\r
860   while (Index < BootOrderSize / sizeof (UINT16)) {\r
861     UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);\r
862     BootOptionVar = BdsLibGetVariableAndSize (\r
863                       BootOption,\r
864                       &gEfiGlobalVariableGuid,\r
865                       &BootOptionSize\r
866                       );\r
867     if (NULL == BootOptionVar) {\r
868       FreePool (BootOrder);\r
869       return EFI_OUT_OF_RESOURCES;\r
870     }\r
871 \r
872     TempPtr = BootOptionVar;\r
873     TempPtr += sizeof (UINT32) + sizeof (UINT16);\r
874     Description = (CHAR16 *) TempPtr;\r
875     TempPtr += StrSize ((CHAR16 *) TempPtr);\r
876     OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;\r
877 \r
878     //\r
879     // Skip legacy boot option (BBS boot device)\r
880     //\r
881     if ((DevicePathType (OptionDevicePath) == BBS_DEVICE_PATH) &&\r
882         (DevicePathSubType (OptionDevicePath) == BBS_BBS_DP)) {\r
883       FreePool (BootOptionVar);\r
884       Index++;\r
885       continue;\r
886     }\r
887 \r
888     if (!BdsLibIsValidEFIBootOptDevicePathExt (OptionDevicePath, FALSE, Description)) {\r
889       //\r
890       // Delete this invalid boot option "Boot####"\r
891       //\r
892       Status = gRT->SetVariable (\r
893                       BootOption,\r
894                       &gEfiGlobalVariableGuid,\r
895                       EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
896                       0,\r
897                       NULL\r
898                       );\r
899       //\r
900       // Mark this boot option in boot order as deleted\r
901       //\r
902       BootOrder[Index] = 0xffff;\r
903     }\r
904 \r
905     FreePool (BootOptionVar);\r
906     Index++;\r
907   }\r
908 \r
909   //\r
910   // Adjust boot order array\r
911   //\r
912   Index2 = 0;\r
913   for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {\r
914     if (BootOrder[Index] != 0xffff) {\r
915       BootOrder[Index2] = BootOrder[Index];\r
916       Index2 ++;\r
917     }\r
918   }\r
919   Status = gRT->SetVariable (\r
920                   L"BootOrder",\r
921                   &gEfiGlobalVariableGuid,\r
922                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
923                   Index2 * sizeof (UINT16),\r
924                   BootOrder\r
925                   );\r
926 \r
927   FreePool (BootOrder);\r
928 \r
929   return Status;\r
930 }\r
931 \r
932 \r
933 /**\r
934   For EFI boot option, BDS separate them as six types:\r
935   1. Network - The boot option points to the SimpleNetworkProtocol device.\r
936                Bds will try to automatically create this type boot option when enumerate.\r
937   2. Shell   - The boot option points to internal flash shell.\r
938                Bds will try to automatically create this type boot option when enumerate.\r
939   3. Removable BlockIo      - The boot option only points to the removable media\r
940                               device, like USB flash disk, DVD, Floppy etc.\r
941                               These device should contain a *removable* blockIo\r
942                               protocol in their device handle.\r
943                               Bds will try to automatically create this type boot option\r
944                               when enumerate.\r
945   4. Fixed BlockIo          - The boot option only points to a Fixed blockIo device,\r
946                               like HardDisk.\r
947                               These device should contain a *fixed* blockIo\r
948                               protocol in their device handle.\r
949                               BDS will skip fixed blockIo devices, and NOT\r
950                               automatically create boot option for them. But BDS\r
951                               will help to delete those fixed blockIo boot option,\r
952                               whose description rule conflict with other auto-created\r
953                               boot options.\r
954   5. Non-BlockIo Simplefile - The boot option points to a device whose handle\r
955                               has SimpleFileSystem Protocol, but has no blockio\r
956                               protocol. These devices do not offer blockIo\r
957                               protocol, but BDS still can get the\r
958                               \EFI\BOOT\boot{machinename}.EFI by SimpleFileSystem\r
959                               Protocol.\r
960   6. File    - The boot option points to a file. These boot options are usually\r
961                created by user manually or OS loader. BDS will not delete or modify\r
962                these boot options.\r
963 \r
964   This function will enumerate all possible boot device in the system, and\r
965   automatically create boot options for Network, Shell, Removable BlockIo,\r
966   and Non-BlockIo Simplefile devices.\r
967   It will only execute once of every boot.\r
968 \r
969   @param  BdsBootOptionList      The header of the link list which indexed all\r
970                                  current boot options\r
971 \r
972   @retval EFI_SUCCESS            Finished all the boot device enumerate and create\r
973                                  the boot option base on that boot device\r
974 \r
975   @retval EFI_OUT_OF_RESOURCES   Failed to enumerate the boot device and create the boot option list\r
976 **/\r
977 EFI_STATUS\r
978 EFIAPI\r
979 BdsLibEnumerateAllBootOption (\r
980   IN OUT LIST_ENTRY          *BdsBootOptionList\r
981   )\r
982 {\r
983   EFI_STATUS                    Status;\r
984   UINT16                        FloppyNumber;\r
985   UINT16                        CdromNumber;\r
986   UINT16                        UsbNumber;\r
987   UINT16                        MiscNumber;\r
988   UINT16                        ScsiNumber;\r
989   UINT16                        NonBlockNumber;\r
990   UINTN                         NumberBlockIoHandles;\r
991   EFI_HANDLE                    *BlockIoHandles;\r
992   EFI_BLOCK_IO_PROTOCOL         *BlkIo;\r
993   UINTN                         Index;\r
994   UINTN                         NumberNetworkHandles;\r
995   EFI_HANDLE                    *NetworkHandles;\r
996   UINTN                         FvHandleCount;\r
997   EFI_HANDLE                    *FvHandleBuffer;\r
998   EFI_FV_FILETYPE               Type;\r
999   UINTN                         Size;\r
1000   EFI_FV_FILE_ATTRIBUTES        Attributes;\r
1001   UINT32                        AuthenticationStatus;\r
1002   EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;\r
1003   EFI_DEVICE_PATH_PROTOCOL      *DevicePath;\r
1004   UINTN                         DevicePathType;\r
1005   CHAR16                        Buffer[40];\r
1006   EFI_HANDLE                    *FileSystemHandles;\r
1007   UINTN                         NumberFileSystemHandles;\r
1008   BOOLEAN                       NeedDelete;\r
1009   EFI_IMAGE_DOS_HEADER          DosHeader;\r
1010   EFI_IMAGE_OPTIONAL_HEADER_UNION       HdrData;\r
1011   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION   Hdr;\r
1012 \r
1013   FloppyNumber  = 0;\r
1014   CdromNumber   = 0;\r
1015   UsbNumber     = 0;\r
1016   MiscNumber    = 0;\r
1017   ScsiNumber    = 0;\r
1018   ZeroMem (Buffer, sizeof (Buffer));\r
1019 \r
1020   //\r
1021   // If the boot device enumerate happened, just get the boot\r
1022   // device from the boot order variable\r
1023   //\r
1024   if (mEnumBootDevice) {\r
1025     Status = BdsLibBuildOptionFromVar (BdsBootOptionList, L"BootOrder");\r
1026     return Status;\r
1027   }\r
1028 \r
1029   //\r
1030   // Notes: this dirty code is to get the legacy boot option from the\r
1031   // BBS table and create to variable as the EFI boot option, it should\r
1032   // be removed after the CSM can provide legacy boot option directly\r
1033   //\r
1034   REFRESH_LEGACY_BOOT_OPTIONS;\r
1035 \r
1036   //\r
1037   // Delete invalid boot option\r
1038   //\r
1039   BdsDeleteAllInvalidEfiBootOption ();\r
1040 \r
1041   //\r
1042   // Parse removable media\r
1043   //\r
1044   gBS->LocateHandleBuffer (\r
1045         ByProtocol,\r
1046         &gEfiBlockIoProtocolGuid,\r
1047         NULL,\r
1048         &NumberBlockIoHandles,\r
1049         &BlockIoHandles\r
1050         );\r
1051 \r
1052   for (Index = 0; Index < NumberBlockIoHandles; Index++) {\r
1053     Status = gBS->HandleProtocol (\r
1054                     BlockIoHandles[Index],\r
1055                     &gEfiBlockIoProtocolGuid,\r
1056                     (VOID **) &BlkIo\r
1057                     );\r
1058     if (!EFI_ERROR (Status)) {\r
1059       if (!BlkIo->Media->RemovableMedia) {\r
1060         //\r
1061         // skip the non-removable block devices\r
1062         //\r
1063         continue;\r
1064       }\r
1065     }\r
1066     DevicePath  = DevicePathFromHandle (BlockIoHandles[Index]);\r
1067     DevicePathType = BdsGetBootTypeFromDevicePath (DevicePath);\r
1068 \r
1069     switch (DevicePathType) {\r
1070     case BDS_EFI_ACPI_FLOPPY_BOOT:\r
1071       UnicodeSPrint (Buffer, sizeof (Buffer), L"%d", FloppyNumber);\r
1072       BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);\r
1073       FloppyNumber++;\r
1074       break;\r
1075 \r
1076     //\r
1077     // Assume a removable SATA device should be the DVD/CD device\r
1078     //\r
1079     case BDS_EFI_MESSAGE_ATAPI_BOOT:\r
1080     case BDS_EFI_MESSAGE_SATA_BOOT:\r
1081       UnicodeSPrint (Buffer, sizeof (Buffer), L"%d", CdromNumber);\r
1082       BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);\r
1083       CdromNumber++;\r
1084       break;\r
1085 \r
1086     case BDS_EFI_MESSAGE_USB_DEVICE_BOOT:\r
1087       UnicodeSPrint (Buffer, sizeof (Buffer), L"%d", UsbNumber);\r
1088       BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);\r
1089       UsbNumber++;\r
1090       break;\r
1091 \r
1092     case BDS_EFI_MESSAGE_SCSI_BOOT:\r
1093       UnicodeSPrint (Buffer, sizeof (Buffer), L"%d", ScsiNumber);\r
1094       BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);\r
1095       ScsiNumber++;\r
1096       break;\r
1097 \r
1098     case BDS_EFI_MESSAGE_MISC_BOOT:\r
1099       UnicodeSPrint (Buffer, sizeof (Buffer), L"%d", MiscNumber);\r
1100       BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);\r
1101       MiscNumber++;\r
1102       break;\r
1103 \r
1104     default:\r
1105       break;\r
1106     }\r
1107   }\r
1108 \r
1109   if (NumberBlockIoHandles != 0) {\r
1110     FreePool (BlockIoHandles);\r
1111   }\r
1112 \r
1113   //\r
1114   // If there is simple file protocol which does not consume block Io protocol, create a boot option for it here.\r
1115   //\r
1116   NonBlockNumber = 0;\r
1117   gBS->LocateHandleBuffer (\r
1118         ByProtocol,\r
1119         &gEfiSimpleFileSystemProtocolGuid,\r
1120         NULL,\r
1121         &NumberFileSystemHandles,\r
1122         &FileSystemHandles\r
1123         );\r
1124   for (Index = 0; Index < NumberFileSystemHandles; Index++) {\r
1125     Status = gBS->HandleProtocol (\r
1126                     FileSystemHandles[Index],\r
1127                     &gEfiBlockIoProtocolGuid,\r
1128                     (VOID **) &BlkIo\r
1129                     );\r
1130      if (!EFI_ERROR (Status)) {\r
1131       //\r
1132       //  Skip if the file system handle supports a BlkIo protocol,\r
1133       //\r
1134       continue;\r
1135     }\r
1136 \r
1137     //\r
1138     // Do the removable Media thing. \EFI\BOOT\boot{machinename}.EFI\r
1139     //  machinename is ia32, ia64, x64, ...\r
1140     //\r
1141     Hdr.Union = &HdrData;\r
1142     NeedDelete = TRUE;\r
1143     Status     = BdsLibGetImageHeader (\r
1144                    FileSystemHandles[Index],\r
1145                    EFI_REMOVABLE_MEDIA_FILE_NAME,\r
1146                    &DosHeader,\r
1147                    Hdr\r
1148                    );\r
1149     if (!EFI_ERROR (Status) &&\r
1150         EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Hdr.Pe32->FileHeader.Machine) &&\r
1151         Hdr.Pe32->OptionalHeader.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) {\r
1152       NeedDelete = FALSE;\r
1153     }\r
1154 \r
1155     if (NeedDelete) {\r
1156       //\r
1157       // No such file or the file is not a EFI application, delete this boot option\r
1158       //\r
1159       BdsLibDeleteOptionFromHandle (FileSystemHandles[Index]);\r
1160     } else {\r
1161       UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Non-Block Boot Device %d", NonBlockNumber);\r
1162       BdsLibBuildOptionFromHandle (FileSystemHandles[Index], BdsBootOptionList, Buffer);\r
1163       NonBlockNumber++;\r
1164     }\r
1165   }\r
1166 \r
1167   if (NumberFileSystemHandles != 0) {\r
1168     FreePool (FileSystemHandles);\r
1169   }\r
1170 \r
1171   //\r
1172   // Parse Network Boot Device\r
1173   //\r
1174   NumberNetworkHandles = 0;\r
1175   //\r
1176   // Search MNP Service Binding protocol for UEFI network stack\r
1177   //\r
1178   gBS->LocateHandleBuffer (\r
1179         ByProtocol,\r
1180         &gEfiManagedNetworkServiceBindingProtocolGuid,\r
1181         NULL,\r
1182         &NumberNetworkHandles,\r
1183         &NetworkHandles\r
1184         );\r
1185   if (NumberNetworkHandles == 0) {\r
1186     //\r
1187     // MNP Service Binding protocol not found, search SNP for EFI network stack\r
1188     //\r
1189     gBS->LocateHandleBuffer (\r
1190           ByProtocol,\r
1191           &gEfiSimpleNetworkProtocolGuid,\r
1192           NULL,\r
1193           &NumberNetworkHandles,\r
1194           &NetworkHandles\r
1195           );\r
1196   }\r
1197 \r
1198   for (Index = 0; Index < NumberNetworkHandles; Index++) {\r
1199     UnicodeSPrint (Buffer, sizeof (Buffer), L"%d", Index);\r
1200     BdsLibBuildOptionFromHandle (NetworkHandles[Index], BdsBootOptionList, Buffer);\r
1201   }\r
1202 \r
1203   if (NumberNetworkHandles != 0) {\r
1204     FreePool (NetworkHandles);\r
1205   }\r
1206 \r
1207   //\r
1208   // Check if we have on flash shell\r
1209   //\r
1210   gBS->LocateHandleBuffer (\r
1211         ByProtocol,\r
1212         &gEfiFirmwareVolume2ProtocolGuid,\r
1213         NULL,\r
1214         &FvHandleCount,\r
1215         &FvHandleBuffer\r
1216         );\r
1217   for (Index = 0; Index < FvHandleCount; Index++) {\r
1218     gBS->HandleProtocol (\r
1219           FvHandleBuffer[Index],\r
1220           &gEfiFirmwareVolume2ProtocolGuid,\r
1221           (VOID **) &Fv\r
1222           );\r
1223 \r
1224     Status = Fv->ReadFile (\r
1225                   Fv,\r
1226                   PcdGetPtr(PcdShellFile),\r
1227                   NULL,\r
1228                   &Size,\r
1229                   &Type,\r
1230                   &Attributes,\r
1231                   &AuthenticationStatus\r
1232                   );\r
1233     if (EFI_ERROR (Status)) {\r
1234       //\r
1235       // Skip if no shell file in the FV\r
1236       //\r
1237       continue;\r
1238     }\r
1239     //\r
1240     // Build the shell boot option\r
1241     //\r
1242     BdsLibBuildOptionFromShell (FvHandleBuffer[Index], BdsBootOptionList);\r
1243   }\r
1244 \r
1245   if (FvHandleCount != 0) {\r
1246     FreePool (FvHandleBuffer);\r
1247   }\r
1248   //\r
1249   // Make sure every boot only have one time\r
1250   // boot device enumerate\r
1251   //\r
1252   Status = BdsLibBuildOptionFromVar (BdsBootOptionList, L"BootOrder");\r
1253   mEnumBootDevice = TRUE;\r
1254 \r
1255   return Status;\r
1256 }\r
1257 \r
1258 /**\r
1259   Build the boot option with the handle parsed in\r
1260 \r
1261   @param  Handle                 The handle which present the device path to create\r
1262                                  boot option\r
1263   @param  BdsBootOptionList      The header of the link list which indexed all\r
1264                                  current boot options\r
1265   @param  String                 The description of the boot option.\r
1266 \r
1267 **/\r
1268 VOID\r
1269 EFIAPI\r
1270 BdsLibBuildOptionFromHandle (\r
1271   IN  EFI_HANDLE                 Handle,\r
1272   IN  LIST_ENTRY                 *BdsBootOptionList,\r
1273   IN  CHAR16                     *String\r
1274   )\r
1275 {\r
1276   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
1277 \r
1278   DevicePath = DevicePathFromHandle (Handle);\r
1279 \r
1280   //\r
1281   // Create and register new boot option\r
1282   //\r
1283   BdsLibRegisterNewOption (BdsBootOptionList, DevicePath, String, L"BootOrder");\r
1284 }\r
1285 \r
1286 \r
1287 /**\r
1288   Build the on flash shell boot option with the handle parsed in.\r
1289 \r
1290   @param  Handle                 The handle which present the device path to create\r
1291                                  on flash shell boot option\r
1292   @param  BdsBootOptionList      The header of the link list which indexed all\r
1293                                  current boot options\r
1294 \r
1295 **/\r
1296 VOID\r
1297 EFIAPI\r
1298 BdsLibBuildOptionFromShell (\r
1299   IN EFI_HANDLE                  Handle,\r
1300   IN OUT LIST_ENTRY              *BdsBootOptionList\r
1301   )\r
1302 {\r
1303   EFI_DEVICE_PATH_PROTOCOL          *DevicePath;\r
1304   MEDIA_FW_VOL_FILEPATH_DEVICE_PATH ShellNode;\r
1305 \r
1306   DevicePath = DevicePathFromHandle (Handle);\r
1307 \r
1308   //\r
1309   // Build the shell device path\r
1310   //\r
1311   EfiInitializeFwVolDevicepathNode (&ShellNode, PcdGetPtr(PcdShellFile));\r
1312 \r
1313   DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &ShellNode);\r
1314 \r
1315   //\r
1316   // Create and register the shell boot option\r
1317   //\r
1318   BdsLibRegisterNewOption (BdsBootOptionList, DevicePath, L"EFI Internal Shell", L"BootOrder");\r
1319 \r
1320 }\r
1321 \r
1322 /**\r
1323   Boot from the UEFI spec defined "BootNext" variable.\r
1324 \r
1325 **/\r
1326 VOID\r
1327 EFIAPI\r
1328 BdsLibBootNext (\r
1329   VOID\r
1330   )\r
1331 {\r
1332   UINT16            *BootNext;\r
1333   UINTN             BootNextSize;\r
1334   CHAR16            Buffer[20];\r
1335   BDS_COMMON_OPTION *BootOption;\r
1336   LIST_ENTRY        TempList;\r
1337   UINTN             ExitDataSize;\r
1338   CHAR16            *ExitData;\r
1339 \r
1340   //\r
1341   // Init the boot option name buffer and temp link list\r
1342   //\r
1343   InitializeListHead (&TempList);\r
1344   ZeroMem (Buffer, sizeof (Buffer));\r
1345 \r
1346   BootNext = BdsLibGetVariableAndSize (\r
1347               L"BootNext",\r
1348               &gEfiGlobalVariableGuid,\r
1349               &BootNextSize\r
1350               );\r
1351 \r
1352   //\r
1353   // Clear the boot next variable first\r
1354   //\r
1355   if (BootNext != NULL) {\r
1356     gRT->SetVariable (\r
1357           L"BootNext",\r
1358           &gEfiGlobalVariableGuid,\r
1359           EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
1360           0,\r
1361           BootNext\r
1362           );\r
1363 \r
1364     //\r
1365     // Start to build the boot option and try to boot\r
1366     //\r
1367     UnicodeSPrint (Buffer, sizeof (Buffer), L"Boot%04x", *BootNext);\r
1368     BootOption = BdsLibVariableToOption (&TempList, Buffer);\r
1369     ASSERT (BootOption != NULL);\r
1370     BdsLibConnectDevicePath (BootOption->DevicePath);\r
1371     BdsLibBootViaBootOption (BootOption, BootOption->DevicePath, &ExitDataSize, &ExitData);\r
1372   }\r
1373 \r
1374 }\r
1375 \r
1376 /**\r
1377   Return the bootable media handle.\r
1378   First, check the device is connected\r
1379   Second, check whether the device path point to a device which support SimpleFileSystemProtocol,\r
1380   Third, detect the the default boot file in the Media, and return the removable Media handle.\r
1381 \r
1382   @param  DevicePath  Device Path to a  bootable device\r
1383 \r
1384   @return  The bootable media handle. If the media on the DevicePath is not bootable, NULL will return.\r
1385 \r
1386 **/\r
1387 EFI_HANDLE\r
1388 EFIAPI\r
1389 BdsLibGetBootableHandle (\r
1390   IN  EFI_DEVICE_PATH_PROTOCOL      *DevicePath\r
1391   )\r
1392 {\r
1393   EFI_STATUS                      Status;\r
1394   EFI_DEVICE_PATH_PROTOCOL        *UpdatedDevicePath;\r
1395   EFI_DEVICE_PATH_PROTOCOL        *DupDevicePath;\r
1396   EFI_HANDLE                      Handle;\r
1397   EFI_BLOCK_IO_PROTOCOL           *BlockIo;\r
1398   VOID                            *Buffer;\r
1399   EFI_DEVICE_PATH_PROTOCOL        *TempDevicePath;\r
1400   UINTN                           Size;\r
1401   UINTN                           TempSize;\r
1402   EFI_HANDLE                      ReturnHandle;\r
1403   EFI_HANDLE                      *SimpleFileSystemHandles;\r
1404 \r
1405   UINTN                           NumberSimpleFileSystemHandles;\r
1406   UINTN                           Index;\r
1407   EFI_IMAGE_DOS_HEADER            DosHeader;\r
1408   EFI_IMAGE_OPTIONAL_HEADER_UNION       HdrData;\r
1409   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION   Hdr;\r
1410 \r
1411   UpdatedDevicePath = DevicePath;\r
1412 \r
1413   //\r
1414   // Check whether the device is connected\r
1415   //\r
1416   Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &UpdatedDevicePath, &Handle);\r
1417   if (EFI_ERROR (Status)) {\r
1418     //\r
1419     // Skip the case that the boot option point to a simple file protocol which does not consume block Io protocol,\r
1420     //\r
1421     Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &UpdatedDevicePath, &Handle);\r
1422     if (EFI_ERROR (Status)) {\r
1423       //\r
1424       // Fail to find the proper BlockIo and simple file protocol, maybe because device not present,  we need to connect it firstly\r
1425       //\r
1426       UpdatedDevicePath = DevicePath;\r
1427       Status            = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &UpdatedDevicePath, &Handle);\r
1428       gBS->ConnectController (Handle, NULL, NULL, TRUE);\r
1429     }\r
1430   } else {\r
1431     //\r
1432     // Get BlockIo protocol and check removable attribute\r
1433     //\r
1434     Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);\r
1435     //\r
1436     // Issue a dummy read to the device to check for media change.\r
1437     // When the removable media is changed, any Block IO read/write will\r
1438     // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is\r
1439     // returned. After the Block IO protocol is reinstalled, subsequent\r
1440     // Block IO read/write will success.\r
1441     //\r
1442     Buffer = AllocatePool (BlockIo->Media->BlockSize);\r
1443     if (Buffer != NULL) {\r
1444       BlockIo->ReadBlocks (\r
1445                BlockIo,\r
1446                BlockIo->Media->MediaId,\r
1447                0,\r
1448                BlockIo->Media->BlockSize,\r
1449                Buffer\r
1450                );\r
1451       FreePool(Buffer);\r
1452     }\r
1453   }\r
1454 \r
1455   //\r
1456   // Detect the the default boot file from removable Media\r
1457   //\r
1458 \r
1459   //\r
1460   // If fail to get bootable handle specified by a USB boot option, the BDS should try to find other bootable device in the same USB bus\r
1461   // Try to locate the USB node device path first, if fail then use its previous PCI node to search\r
1462   //\r
1463   DupDevicePath = DuplicateDevicePath (DevicePath);\r
1464   ASSERT (DupDevicePath != NULL);\r
1465 \r
1466   UpdatedDevicePath = DupDevicePath;\r
1467   Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &UpdatedDevicePath, &Handle);\r
1468   //\r
1469   // if the resulting device path point to a usb node, and the usb node is a dummy node, should only let device path only point to the previous Pci node\r
1470   // Acpi()/Pci()/Usb() --> Acpi()/Pci()\r
1471   //\r
1472   if ((DevicePathType (UpdatedDevicePath) == MESSAGING_DEVICE_PATH) &&\r
1473       (DevicePathSubType (UpdatedDevicePath) == MSG_USB_DP)) {\r
1474     //\r
1475     // Remove the usb node, let the device path only point to PCI node\r
1476     //\r
1477     SetDevicePathEndNode (UpdatedDevicePath);\r
1478     UpdatedDevicePath = DupDevicePath;\r
1479   } else {\r
1480     UpdatedDevicePath = DevicePath;\r
1481   }\r
1482 \r
1483   //\r
1484   // Get the device path size of boot option\r
1485   //\r
1486   Size = GetDevicePathSize(UpdatedDevicePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL); // minus the end node\r
1487   ReturnHandle = NULL;\r
1488   gBS->LocateHandleBuffer (\r
1489       ByProtocol,\r
1490       &gEfiSimpleFileSystemProtocolGuid,\r
1491       NULL,\r
1492       &NumberSimpleFileSystemHandles,\r
1493       &SimpleFileSystemHandles\r
1494       );\r
1495   for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {\r
1496     //\r
1497     // Get the device path size of SimpleFileSystem handle\r
1498     //\r
1499     TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);\r
1500     TempSize = GetDevicePathSize (TempDevicePath)- sizeof (EFI_DEVICE_PATH_PROTOCOL); // minus the end node\r
1501     //\r
1502     // Check whether the device path of boot option is part of the  SimpleFileSystem handle's device path\r
1503     //\r
1504     if (Size <= TempSize && CompareMem (TempDevicePath, UpdatedDevicePath, Size)==0) {\r
1505       //\r
1506       // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media\r
1507       //  machinename is ia32, ia64, x64, ...\r
1508       //\r
1509       Hdr.Union = &HdrData;\r
1510       Status = BdsLibGetImageHeader (\r
1511                  SimpleFileSystemHandles[Index],\r
1512                  EFI_REMOVABLE_MEDIA_FILE_NAME,\r
1513                  &DosHeader,\r
1514                  Hdr\r
1515                  );\r
1516       if (!EFI_ERROR (Status) &&\r
1517         EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Hdr.Pe32->FileHeader.Machine) &&\r
1518         Hdr.Pe32->OptionalHeader.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) {\r
1519         ReturnHandle = SimpleFileSystemHandles[Index];\r
1520         break;\r
1521       }\r
1522     }\r
1523   }\r
1524 \r
1525   FreePool(DupDevicePath);\r
1526 \r
1527   if (SimpleFileSystemHandles != NULL) {\r
1528     FreePool(SimpleFileSystemHandles);\r
1529   }\r
1530 \r
1531   return ReturnHandle;\r
1532 }\r
1533 \r
1534 /**\r
1535   Check to see if the network cable is plugged in. If the DevicePath is not\r
1536   connected it will be connected.\r
1537 \r
1538   @param  DevicePath             Device Path to check\r
1539 \r
1540   @retval TRUE                   DevicePath points to an Network that is connected\r
1541   @retval FALSE                  DevicePath does not point to a bootable network\r
1542 \r
1543 **/\r
1544 BOOLEAN\r
1545 BdsLibNetworkBootWithMediaPresent (\r
1546   IN  EFI_DEVICE_PATH_PROTOCOL      *DevicePath\r
1547   )\r
1548 {\r
1549   EFI_STATUS                      Status;\r
1550   EFI_DEVICE_PATH_PROTOCOL        *UpdatedDevicePath;\r
1551   EFI_HANDLE                      Handle;\r
1552   EFI_SIMPLE_NETWORK_PROTOCOL     *Snp;\r
1553   BOOLEAN                         MediaPresent;\r
1554 \r
1555   MediaPresent = FALSE;\r
1556 \r
1557   UpdatedDevicePath = DevicePath;\r
1558   //\r
1559   // Locate MNP Service Binding protocol for UEFI network stack first\r
1560   //\r
1561   Status = gBS->LocateDevicePath (&gEfiManagedNetworkServiceBindingProtocolGuid, &UpdatedDevicePath, &Handle);\r
1562   if (EFI_ERROR (Status)) {\r
1563     //\r
1564     // MNP Service Binding protocol not found, search SNP for EFI network stack\r
1565     //\r
1566     UpdatedDevicePath = DevicePath;\r
1567     Status = gBS->LocateDevicePath (&gEfiSimpleNetworkProtocolGuid, &UpdatedDevicePath, &Handle);\r
1568   }\r
1569   if (EFI_ERROR (Status)) {\r
1570     //\r
1571     // Device not present so see if we need to connect it\r
1572     //\r
1573     Status = BdsLibConnectDevicePath (DevicePath);\r
1574     if (!EFI_ERROR (Status)) {\r
1575       //\r
1576       // This one should work after we did the connect\r
1577       //\r
1578       Status = gBS->LocateDevicePath (&gEfiManagedNetworkServiceBindingProtocolGuid, &UpdatedDevicePath, &Handle);\r
1579       if (EFI_ERROR (Status)) {\r
1580         UpdatedDevicePath = DevicePath;\r
1581         Status = gBS->LocateDevicePath (&gEfiSimpleNetworkProtocolGuid, &UpdatedDevicePath, &Handle);\r
1582       }\r
1583     }\r
1584   }\r
1585 \r
1586   if (!EFI_ERROR (Status)) {\r
1587     Status = gBS->HandleProtocol (Handle, &gEfiSimpleNetworkProtocolGuid, (VOID **)&Snp);\r
1588     if (EFI_ERROR (Status)) {\r
1589       //\r
1590       // Failed to open SNP from this handle, try to get SNP from parent handle\r
1591       //\r
1592       UpdatedDevicePath = DevicePathFromHandle (Handle);\r
1593       if (UpdatedDevicePath != NULL) {\r
1594         Status = gBS->LocateDevicePath (&gEfiSimpleNetworkProtocolGuid, &UpdatedDevicePath, &Handle);\r
1595         if (!EFI_ERROR (Status)) {\r
1596           //\r
1597           // SNP handle found, get SNP from it\r
1598           //\r
1599           Status = gBS->HandleProtocol (Handle, &gEfiSimpleNetworkProtocolGuid, (VOID **) &Snp);\r
1600         }\r
1601       }\r
1602     }\r
1603 \r
1604     if (!EFI_ERROR (Status)) {\r
1605       if (Snp->Mode->MediaPresentSupported) {\r
1606         if (Snp->Mode->State == EfiSimpleNetworkInitialized) {\r
1607           //\r
1608           // In case some one else is using the SNP check to see if it's connected\r
1609           //\r
1610           MediaPresent = Snp->Mode->MediaPresent;\r
1611         } else {\r
1612           //\r
1613           // No one is using SNP so we need to Start and Initialize so\r
1614           // MediaPresent will be valid.\r
1615           //\r
1616           Status = Snp->Start (Snp);\r
1617           if (!EFI_ERROR (Status)) {\r
1618             Status = Snp->Initialize (Snp, 0, 0);\r
1619             if (!EFI_ERROR (Status)) {\r
1620               MediaPresent = Snp->Mode->MediaPresent;\r
1621               Snp->Shutdown (Snp);\r
1622             }\r
1623             Snp->Stop (Snp);\r
1624           }\r
1625         }\r
1626       } else {\r
1627         MediaPresent = TRUE;\r
1628       }\r
1629     }\r
1630   }\r
1631 \r
1632   return MediaPresent;\r
1633 }\r
1634 \r
1635 /**\r
1636   For a bootable Device path, return its boot type.\r
1637 \r
1638   @param  DevicePath                      The bootable device Path to check\r
1639 \r
1640   @retval BDS_EFI_MEDIA_HD_BOOT           If given device path contains MEDIA_DEVICE_PATH type device path node\r
1641                                           which subtype is MEDIA_HARDDRIVE_DP\r
1642   @retval BDS_EFI_MEDIA_CDROM_BOOT        If given device path contains MEDIA_DEVICE_PATH type device path node\r
1643                                           which subtype is MEDIA_CDROM_DP\r
1644   @retval BDS_EFI_ACPI_FLOPPY_BOOT        If given device path contains ACPI_DEVICE_PATH type device path node\r
1645                                           which HID is floppy device.\r
1646   @retval BDS_EFI_MESSAGE_ATAPI_BOOT      If given device path contains MESSAGING_DEVICE_PATH type device path node\r
1647                                           and its last device path node's subtype is MSG_ATAPI_DP.\r
1648   @retval BDS_EFI_MESSAGE_SCSI_BOOT       If given device path contains MESSAGING_DEVICE_PATH type device path node\r
1649                                           and its last device path node's subtype is MSG_SCSI_DP.\r
1650   @retval BDS_EFI_MESSAGE_USB_DEVICE_BOOT If given device path contains MESSAGING_DEVICE_PATH type device path node\r
1651                                           and its last device path node's subtype is MSG_USB_DP.\r
1652   @retval BDS_EFI_MESSAGE_MISC_BOOT       If the device path not contains any media device path node,  and\r
1653                                           its last device path node point to a message device path node.\r
1654   @retval BDS_LEGACY_BBS_BOOT             If given device path contains BBS_DEVICE_PATH type device path node.\r
1655   @retval BDS_EFI_UNSUPPORT               An EFI Removable BlockIO device path not point to a media and message device,\r
1656 \r
1657 **/\r
1658 UINT32\r
1659 EFIAPI\r
1660 BdsGetBootTypeFromDevicePath (\r
1661   IN  EFI_DEVICE_PATH_PROTOCOL     *DevicePath\r
1662   )\r
1663 {\r
1664   ACPI_HID_DEVICE_PATH          *Acpi;\r
1665   EFI_DEVICE_PATH_PROTOCOL      *TempDevicePath;\r
1666   EFI_DEVICE_PATH_PROTOCOL      *LastDeviceNode;\r
1667   UINT32                        BootType;\r
1668 \r
1669   if (NULL == DevicePath) {\r
1670     return BDS_EFI_UNSUPPORT;\r
1671   }\r
1672 \r
1673   TempDevicePath = DevicePath;\r
1674 \r
1675   while (!IsDevicePathEndType (TempDevicePath)) {\r
1676     switch (DevicePathType (TempDevicePath)) {\r
1677       case BBS_DEVICE_PATH:\r
1678          return BDS_LEGACY_BBS_BOOT;\r
1679       case MEDIA_DEVICE_PATH:\r
1680         if (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP) {\r
1681           return BDS_EFI_MEDIA_HD_BOOT;\r
1682         } else if (DevicePathSubType (TempDevicePath) == MEDIA_CDROM_DP) {\r
1683           return BDS_EFI_MEDIA_CDROM_BOOT;\r
1684         }\r
1685         break;\r
1686       case ACPI_DEVICE_PATH:\r
1687         Acpi = (ACPI_HID_DEVICE_PATH *) TempDevicePath;\r
1688         if (EISA_ID_TO_NUM (Acpi->HID) == 0x0604) {\r
1689           return BDS_EFI_ACPI_FLOPPY_BOOT;\r
1690         }\r
1691         break;\r
1692       case MESSAGING_DEVICE_PATH:\r
1693         //\r
1694         // Get the last device path node\r
1695         //\r
1696         LastDeviceNode = NextDevicePathNode (TempDevicePath);\r
1697         if (DevicePathSubType(LastDeviceNode) == MSG_DEVICE_LOGICAL_UNIT_DP) {\r
1698           //\r
1699           // if the next node type is Device Logical Unit, which specify the Logical Unit Number (LUN),\r
1700           // skip it\r
1701           //\r
1702           LastDeviceNode = NextDevicePathNode (LastDeviceNode);\r
1703         }\r
1704         //\r
1705         // if the device path not only point to driver device, it is not a messaging device path,\r
1706         //\r
1707         if (!IsDevicePathEndType (LastDeviceNode)) {\r
1708           break;\r
1709         }\r
1710 \r
1711         switch (DevicePathSubType (TempDevicePath)) {\r
1712         case MSG_ATAPI_DP:\r
1713           BootType = BDS_EFI_MESSAGE_ATAPI_BOOT;\r
1714           break;\r
1715 \r
1716         case MSG_USB_DP:\r
1717           BootType = BDS_EFI_MESSAGE_USB_DEVICE_BOOT;\r
1718           break;\r
1719 \r
1720         case MSG_SCSI_DP:\r
1721           BootType = BDS_EFI_MESSAGE_SCSI_BOOT;\r
1722           break;\r
1723 \r
1724         case MSG_SATA_DP:\r
1725           BootType = BDS_EFI_MESSAGE_SATA_BOOT;\r
1726           break;\r
1727 \r
1728         case MSG_MAC_ADDR_DP:\r
1729         case MSG_VLAN_DP:\r
1730           BootType = BDS_EFI_MESSAGE_MAC_BOOT;\r
1731           break;\r
1732 \r
1733         default:\r
1734           BootType = BDS_EFI_MESSAGE_MISC_BOOT;\r
1735           break;\r
1736         }\r
1737         return BootType;\r
1738 \r
1739       default:\r
1740         break;\r
1741     }\r
1742     TempDevicePath = NextDevicePathNode (TempDevicePath);\r
1743   }\r
1744 \r
1745   return BDS_EFI_UNSUPPORT;\r
1746 }\r
1747 \r
1748 /**\r
1749   Check whether the Device path in a boot option point to a valid bootable device,\r
1750   And if CheckMedia is true, check the device is ready to boot now.\r
1751 \r
1752   @param  DevPath     the Device path in a boot option\r
1753   @param  CheckMedia  if true, check the device is ready to boot now.\r
1754 \r
1755   @retval TRUE        the Device path  is valid\r
1756   @retval FALSE       the Device path  is invalid .\r
1757 \r
1758 **/\r
1759 BOOLEAN\r
1760 EFIAPI\r
1761 BdsLibIsValidEFIBootOptDevicePath (\r
1762   IN EFI_DEVICE_PATH_PROTOCOL     *DevPath,\r
1763   IN BOOLEAN                      CheckMedia\r
1764   )\r
1765 {\r
1766   return BdsLibIsValidEFIBootOptDevicePathExt (DevPath, CheckMedia, NULL);\r
1767 }\r
1768 \r
1769 /**\r
1770   Check whether the Device path in a boot option point to a valid bootable device,\r
1771   And if CheckMedia is true, check the device is ready to boot now.\r
1772   If Description is not NULL and the device path point to a fixed BlockIo\r
1773   device, check the description whether conflict with other auto-created\r
1774   boot options.\r
1775 \r
1776   @param  DevPath     the Device path in a boot option\r
1777   @param  CheckMedia  if true, check the device is ready to boot now.\r
1778   @param  Description the description in a boot option\r
1779 \r
1780   @retval TRUE        the Device path  is valid\r
1781   @retval FALSE       the Device path  is invalid .\r
1782 \r
1783 **/\r
1784 BOOLEAN\r
1785 EFIAPI\r
1786 BdsLibIsValidEFIBootOptDevicePathExt (\r
1787   IN EFI_DEVICE_PATH_PROTOCOL     *DevPath,\r
1788   IN BOOLEAN                      CheckMedia,\r
1789   IN CHAR16                       *Description\r
1790   )\r
1791 {\r
1792   EFI_STATUS                Status;\r
1793   EFI_HANDLE                Handle;\r
1794   EFI_DEVICE_PATH_PROTOCOL  *TempDevicePath;\r
1795   EFI_DEVICE_PATH_PROTOCOL  *LastDeviceNode;\r
1796   EFI_BLOCK_IO_PROTOCOL     *BlockIo;\r
1797   EFI_LOAD_FILE_PROTOCOL    *LoadFile;\r
1798 \r
1799   TempDevicePath = DevPath;\r
1800   LastDeviceNode = DevPath;\r
1801 \r
1802   //\r
1803   // Check if it's a valid boot option for network boot device\r
1804   // Check if there is MNP Service Binding Protocol or SimpleNetworkProtocol\r
1805   // installed. If yes, that means there is the network card there.\r
1806   //\r
1807   Status = gBS->LocateDevicePath (\r
1808                   &gEfiManagedNetworkServiceBindingProtocolGuid,\r
1809                   &TempDevicePath,\r
1810                   &Handle\r
1811                   );\r
1812   if (EFI_ERROR (Status)) {\r
1813     TempDevicePath = DevPath;\r
1814     Status = gBS->LocateDevicePath (\r
1815                     &gEfiSimpleNetworkProtocolGuid,\r
1816                     &TempDevicePath,\r
1817                     &Handle\r
1818                     );\r
1819   }\r
1820   if (EFI_ERROR (Status)) {\r
1821     //\r
1822     // Device not present so see if we need to connect it\r
1823     //\r
1824     TempDevicePath = DevPath;\r
1825     BdsLibConnectDevicePath (TempDevicePath);\r
1826     Status = gBS->LocateDevicePath (\r
1827                     &gEfiManagedNetworkServiceBindingProtocolGuid,\r
1828                     &TempDevicePath,\r
1829                     &Handle\r
1830                     );\r
1831     if (EFI_ERROR (Status)) {\r
1832       TempDevicePath = DevPath;\r
1833       Status = gBS->LocateDevicePath (\r
1834                       &gEfiSimpleNetworkProtocolGuid,\r
1835                       &TempDevicePath,\r
1836                       &Handle\r
1837                       );\r
1838     }\r
1839   }\r
1840 \r
1841   if (!EFI_ERROR (Status)) {\r
1842     //\r
1843     // Check whether LoadFile protocol is installed\r
1844     //\r
1845     Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFile);\r
1846     if (!EFI_ERROR (Status)) {\r
1847       if (!IsDevicePathEnd (TempDevicePath)) {\r
1848         //\r
1849         // LoadFile protocol is not installed on handle with exactly the same DevPath\r
1850         //\r
1851         return FALSE;\r
1852       }\r
1853 \r
1854       if (CheckMedia) {\r
1855         //\r
1856         // Test if it is ready to boot now\r
1857         //\r
1858         if (BdsLibNetworkBootWithMediaPresent(DevPath)) {\r
1859           return TRUE;\r
1860         }\r
1861       } else {\r
1862         return TRUE;\r
1863       }\r
1864     }\r
1865   }\r
1866 \r
1867   //\r
1868   // If the boot option point to a file, it is a valid EFI boot option,\r
1869   // and assume it is ready to boot now\r
1870   //\r
1871   while (!IsDevicePathEnd (TempDevicePath)) {\r
1872      LastDeviceNode = TempDevicePath;\r
1873      TempDevicePath = NextDevicePathNode (TempDevicePath);\r
1874   }\r
1875   if ((DevicePathType (LastDeviceNode) == MEDIA_DEVICE_PATH) &&\r
1876     (DevicePathSubType (LastDeviceNode) == MEDIA_FILEPATH_DP)) {\r
1877     return TRUE;\r
1878   }\r
1879 \r
1880   //\r
1881   // Check if it's a valid boot option for internal Shell\r
1882   //\r
1883   if (EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode) != NULL) {\r
1884     //\r
1885     // If the boot option point to Internal FV shell, make sure it is valid\r
1886     //\r
1887     TempDevicePath = DevPath;\r
1888     Status = BdsLibUpdateFvFileDevicePath (&TempDevicePath, PcdGetPtr(PcdShellFile));\r
1889     if (Status == EFI_ALREADY_STARTED) {\r
1890       return TRUE;\r
1891     } else {\r
1892       if (Status == EFI_SUCCESS) {\r
1893         FreePool (TempDevicePath);\r
1894       }\r
1895       return FALSE;\r
1896     }\r
1897   }\r
1898 \r
1899   //\r
1900   // If the boot option point to a blockIO device:\r
1901   //    if it is a removable blockIo device, it is valid.\r
1902   //    if it is a fixed blockIo device, check its description confliction.\r
1903   //\r
1904   TempDevicePath = DevPath;\r
1905   Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);\r
1906   if (EFI_ERROR (Status)) {\r
1907     //\r
1908     // Device not present so see if we need to connect it\r
1909     //\r
1910     Status = BdsLibConnectDevicePath (DevPath);\r
1911     if (!EFI_ERROR (Status)) {\r
1912       //\r
1913       // Try again to get the Block Io protocol after we did the connect\r
1914       //\r
1915       TempDevicePath = DevPath;\r
1916       Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);\r
1917     }\r
1918   }\r
1919 \r
1920   if (!EFI_ERROR (Status)) {\r
1921     Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);\r
1922     if (!EFI_ERROR (Status)) {\r
1923       if (CheckMedia) {\r
1924         //\r
1925         // Test if it is ready to boot now\r
1926         //\r
1927         if (BdsLibGetBootableHandle (DevPath) != NULL) {\r
1928           return TRUE;\r
1929         }\r
1930       } else {\r
1931         return TRUE;\r
1932       }\r
1933     }\r
1934   } else {\r
1935     //\r
1936     // if the boot option point to a simple file protocol which does not consume block Io protocol, it is also a valid EFI boot option,\r
1937     //\r
1938     Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle);\r
1939     if (!EFI_ERROR (Status)) {\r
1940       if (CheckMedia) {\r
1941         //\r
1942         // Test if it is ready to boot now\r
1943         //\r
1944         if (BdsLibGetBootableHandle (DevPath) != NULL) {\r
1945           return TRUE;\r
1946         }\r
1947       } else {\r
1948         return TRUE;\r
1949       }\r
1950     }\r
1951   }\r
1952 \r
1953   return FALSE;\r
1954 }\r
1955 \r
1956 \r
1957 /**\r
1958   According to a file guild, check a Fv file device path is valid. If it is invalid,\r
1959   try to return the valid device path.\r
1960   FV address maybe changes for memory layout adjust from time to time, use this function\r
1961   could promise the Fv file device path is right.\r
1962 \r
1963   @param  DevicePath             on input, the Fv file device path need to check on\r
1964                                  output, the updated valid Fv file device path\r
1965   @param  FileGuid               the Fv file guild\r
1966 \r
1967   @retval EFI_INVALID_PARAMETER  the input DevicePath or FileGuid is invalid\r
1968                                  parameter\r
1969   @retval EFI_UNSUPPORTED        the input DevicePath does not contain Fv file\r
1970                                  guild at all\r
1971   @retval EFI_ALREADY_STARTED    the input DevicePath has pointed to Fv file, it is\r
1972                                  valid\r
1973   @retval EFI_SUCCESS            has successfully updated the invalid DevicePath,\r
1974                                  and return the updated device path in DevicePath\r
1975 \r
1976 **/\r
1977 EFI_STATUS\r
1978 EFIAPI\r
1979 BdsLibUpdateFvFileDevicePath (\r
1980   IN  OUT EFI_DEVICE_PATH_PROTOCOL      ** DevicePath,\r
1981   IN  EFI_GUID                          *FileGuid\r
1982   )\r
1983 {\r
1984   EFI_DEVICE_PATH_PROTOCOL      *TempDevicePath;\r
1985   EFI_DEVICE_PATH_PROTOCOL      *LastDeviceNode;\r
1986   EFI_STATUS                    Status;\r
1987   EFI_GUID                      *GuidPoint;\r
1988   UINTN                         Index;\r
1989   UINTN                         FvHandleCount;\r
1990   EFI_HANDLE                    *FvHandleBuffer;\r
1991   EFI_FV_FILETYPE               Type;\r
1992   UINTN                         Size;\r
1993   EFI_FV_FILE_ATTRIBUTES        Attributes;\r
1994   UINT32                        AuthenticationStatus;\r
1995   BOOLEAN                       FindFvFile;\r
1996   EFI_LOADED_IMAGE_PROTOCOL     *LoadedImage;\r
1997   EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;\r
1998   MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FvFileNode;\r
1999   EFI_HANDLE                    FoundFvHandle;\r
2000   EFI_DEVICE_PATH_PROTOCOL      *NewDevicePath;\r
2001 \r
2002   if ((DevicePath == NULL) || (*DevicePath == NULL)) {\r
2003     return EFI_INVALID_PARAMETER;\r
2004   }\r
2005   if (FileGuid == NULL) {\r
2006     return EFI_INVALID_PARAMETER;\r
2007   }\r
2008 \r
2009   //\r
2010   // Check whether the device path point to the default the input Fv file\r
2011   //\r
2012   TempDevicePath = *DevicePath;\r
2013   LastDeviceNode = TempDevicePath;\r
2014   while (!IsDevicePathEnd (TempDevicePath)) {\r
2015      LastDeviceNode = TempDevicePath;\r
2016      TempDevicePath = NextDevicePathNode (TempDevicePath);\r
2017   }\r
2018   GuidPoint = EfiGetNameGuidFromFwVolDevicePathNode (\r
2019                 (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode\r
2020                 );\r
2021   if (GuidPoint == NULL) {\r
2022     //\r
2023     // if this option does not points to a Fv file, just return EFI_UNSUPPORTED\r
2024     //\r
2025     return EFI_UNSUPPORTED;\r
2026   }\r
2027   if (!CompareGuid (GuidPoint, FileGuid)) {\r
2028     //\r
2029     // If the Fv file is not the input file guid, just return EFI_UNSUPPORTED\r
2030     //\r
2031     return EFI_UNSUPPORTED;\r
2032   }\r
2033 \r
2034   //\r
2035   // Check whether the input Fv file device path is valid\r
2036   //\r
2037   TempDevicePath = *DevicePath;\r
2038   FoundFvHandle = NULL;\r
2039   Status = gBS->LocateDevicePath (\r
2040                   &gEfiFirmwareVolume2ProtocolGuid,\r
2041                   &TempDevicePath,\r
2042                   &FoundFvHandle\r
2043                   );\r
2044   if (!EFI_ERROR (Status)) {\r
2045     Status = gBS->HandleProtocol (\r
2046                     FoundFvHandle,\r
2047                     &gEfiFirmwareVolume2ProtocolGuid,\r
2048                     (VOID **) &Fv\r
2049                     );\r
2050     if (!EFI_ERROR (Status)) {\r
2051       //\r
2052       // Set FV ReadFile Buffer as NULL, only need to check whether input Fv file exist there\r
2053       //\r
2054       Status = Fv->ReadFile (\r
2055                     Fv,\r
2056                     FileGuid,\r
2057                     NULL,\r
2058                     &Size,\r
2059                     &Type,\r
2060                     &Attributes,\r
2061                     &AuthenticationStatus\r
2062                     );\r
2063       if (!EFI_ERROR (Status)) {\r
2064         return EFI_ALREADY_STARTED;\r
2065       }\r
2066     }\r
2067   }\r
2068 \r
2069   //\r
2070   // Look for the input wanted FV file in current FV\r
2071   // First, try to look for in Bds own FV. Bds and input wanted FV file usually are in the same FV\r
2072   //\r
2073   FindFvFile = FALSE;\r
2074   FoundFvHandle = NULL;\r
2075   Status = gBS->HandleProtocol (\r
2076              mBdsImageHandle,\r
2077              &gEfiLoadedImageProtocolGuid,\r
2078              (VOID **) &LoadedImage\r
2079              );\r
2080   if (!EFI_ERROR (Status)) {\r
2081     Status = gBS->HandleProtocol (\r
2082                     LoadedImage->DeviceHandle,\r
2083                     &gEfiFirmwareVolume2ProtocolGuid,\r
2084                     (VOID **) &Fv\r
2085                     );\r
2086     if (!EFI_ERROR (Status)) {\r
2087       Status = Fv->ReadFile (\r
2088                     Fv,\r
2089                     FileGuid,\r
2090                     NULL,\r
2091                     &Size,\r
2092                     &Type,\r
2093                     &Attributes,\r
2094                     &AuthenticationStatus\r
2095                     );\r
2096       if (!EFI_ERROR (Status)) {\r
2097         FindFvFile = TRUE;\r
2098         FoundFvHandle = LoadedImage->DeviceHandle;\r
2099       }\r
2100     }\r
2101   }\r
2102   //\r
2103   // Second, if fail to find, try to enumerate all FV\r
2104   //\r
2105   if (!FindFvFile) {\r
2106     FvHandleBuffer = NULL;\r
2107     gBS->LocateHandleBuffer (\r
2108           ByProtocol,\r
2109           &gEfiFirmwareVolume2ProtocolGuid,\r
2110           NULL,\r
2111           &FvHandleCount,\r
2112           &FvHandleBuffer\r
2113           );\r
2114     for (Index = 0; Index < FvHandleCount; Index++) {\r
2115       gBS->HandleProtocol (\r
2116             FvHandleBuffer[Index],\r
2117             &gEfiFirmwareVolume2ProtocolGuid,\r
2118             (VOID **) &Fv\r
2119             );\r
2120 \r
2121       Status = Fv->ReadFile (\r
2122                     Fv,\r
2123                     FileGuid,\r
2124                     NULL,\r
2125                     &Size,\r
2126                     &Type,\r
2127                     &Attributes,\r
2128                     &AuthenticationStatus\r
2129                     );\r
2130       if (EFI_ERROR (Status)) {\r
2131         //\r
2132         // Skip if input Fv file not in the FV\r
2133         //\r
2134         continue;\r
2135       }\r
2136       FindFvFile = TRUE;\r
2137       FoundFvHandle = FvHandleBuffer[Index];\r
2138       break;\r
2139     }\r
2140 \r
2141     if (FvHandleBuffer != NULL) {\r
2142       FreePool (FvHandleBuffer);\r
2143     }\r
2144   }\r
2145 \r
2146   if (FindFvFile) {\r
2147     //\r
2148     // Build the shell device path\r
2149     //\r
2150     NewDevicePath = DevicePathFromHandle (FoundFvHandle);\r
2151     EfiInitializeFwVolDevicepathNode (&FvFileNode, FileGuid);\r
2152     NewDevicePath = AppendDevicePathNode (NewDevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &FvFileNode);\r
2153     *DevicePath = NewDevicePath;\r
2154     return EFI_SUCCESS;\r
2155   }\r
2156   return EFI_NOT_FOUND;\r
2157 }\r