Support adding boot option from removable media, and removing invalid EFI boot option
[people/mcb30/edk2.git] / edk2 / EdkNt32Pkg / Library / EdkGenericBdsLib / BdsBoot.c
1 /*++\r
2 \r
3 Copyright (c) 2006, Intel Corporation                                                         \r
4 All rights reserved. This program and the accompanying materials                          \r
5 are licensed and made available under the terms and conditions of the BSD License         \r
6 which accompanies this distribution.  The full text of the license may be found at        \r
7 http://opensource.org/licenses/bsd-license.php                                            \r
8                                                                                           \r
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             \r
11 \r
12 Module Name:\r
13 \r
14   BdsBoot.c\r
15 \r
16 Abstract:\r
17 \r
18   BDS Lib functions which relate with create or process the boot\r
19   option.\r
20 \r
21 --*/\r
22 #include "Performance.h"\r
23 \r
24 BOOLEAN mEnumBootDevice = FALSE;\r
25 \r
26 EFI_STATUS\r
27 BdsLibDoLegacyBoot (\r
28   IN  BDS_COMMON_OPTION           *Option\r
29   )\r
30 /*++\r
31 \r
32 Routine Description:\r
33  \r
34   Boot the legacy system with the boot option\r
35 \r
36 Arguments:\r
37 \r
38   Option           - The legacy boot option which have BBS device path\r
39 \r
40 Returns:\r
41 \r
42   EFI_UNSUPPORTED  - There is no legacybios protocol, do not support\r
43                      legacy boot.\r
44                          \r
45   EFI_STATUS       - Return the status of LegacyBios->LegacyBoot ().\r
46 \r
47 --*/\r
48 {\r
49   EFI_STATUS                Status;\r
50   EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;\r
51 \r
52   Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, &LegacyBios);\r
53   if (EFI_ERROR (Status)) {\r
54     //\r
55     // If no LegacyBios protocol we do not support legacy boot\r
56     //\r
57     return EFI_UNSUPPORTED;\r
58   }\r
59   //\r
60   // Notes: if we seperate the int 19, then we don't need to refresh BBS\r
61   //\r
62   BdsRefreshBbsTableForBoot (Option);\r
63 \r
64   //\r
65   // Write boot to OS performance data to a file\r
66   //\r
67   PERF_CODE (\r
68     WriteBootToOsPerformanceData ();\r
69   );\r
70 \r
71 \r
72   DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Legacy Boot: %S\n", Option->Description));\r
73   return LegacyBios->LegacyBoot (\r
74                       LegacyBios,\r
75                       (BBS_BBS_DEVICE_PATH *) Option->DevicePath,\r
76                       Option->LoadOptionsSize,\r
77                       Option->LoadOptions\r
78                       );\r
79 }\r
80 \r
81 EFI_STATUS\r
82 BdsLibBootViaBootOption (\r
83   IN  BDS_COMMON_OPTION             * Option,\r
84   IN  EFI_DEVICE_PATH_PROTOCOL      * DevicePath,\r
85   OUT UINTN                         *ExitDataSize,\r
86   OUT CHAR16                        **ExitData OPTIONAL\r
87   )\r
88 /*++\r
89 \r
90 Routine Description:\r
91 \r
92   Process the boot option follow the EFI 1.1 specification and \r
93   special treat the legacy boot option with BBS_DEVICE_PATH.\r
94 \r
95 Arguments:\r
96 \r
97   Option       - The boot option need to be processed\r
98   \r
99   DevicePath   - The device path which describe where to load \r
100                  the boot image or the legcy BBS device path \r
101                  to boot the legacy OS\r
102 \r
103   ExitDataSize - Returned directly from gBS->StartImage ()\r
104 \r
105   ExitData     - Returned directly from gBS->StartImage ()\r
106 \r
107 Returns:\r
108 \r
109   EFI_SUCCESS   - Status from gBS->StartImage (),\r
110                   or BdsBootByDiskSignatureAndPartition ()\r
111 \r
112   EFI_NOT_FOUND - If the Device Path is not found in the system\r
113 \r
114 --*/\r
115 {\r
116   EFI_STATUS                Status;\r
117   EFI_HANDLE                Handle;\r
118   EFI_HANDLE                ImageHandle;\r
119   EFI_DEVICE_PATH_PROTOCOL  *TempDevicePath;\r
120   EFI_DEVICE_PATH_PROTOCOL  *FilePath;\r
121   EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;\r
122   EFI_ACPI_S3_SAVE_PROTOCOL *AcpiS3Save;\r
123   EFI_BLOCK_IO_PROTOCOL     *BlkIo;\r
124   VOID                      *Buffer;\r
125 \r
126   *ExitDataSize = 0;\r
127   *ExitData     = NULL;\r
128 \r
129   //\r
130   // Notes: put EFI64 ROM Shadow Solution\r
131   //\r
132   EFI64_SHADOW_ALL_LEGACY_ROM ();\r
133 \r
134   //\r
135   // Notes: this code can be remove after the s3 script table\r
136   // hook on the event EFI_EVENT_SIGNAL_READY_TO_BOOT or\r
137   // EFI_EVENT_SIGNAL_LEGACY_BOOT\r
138   //\r
139   Status = gBS->LocateProtocol (&gEfiAcpiS3SaveProtocolGuid, NULL, &AcpiS3Save);\r
140   if (!EFI_ERROR (Status)) {\r
141     AcpiS3Save->S3Save (AcpiS3Save, NULL);\r
142   }\r
143   //\r
144   // If it's Device Path that starts with a hard drive path,\r
145   // this routine will do the booting.\r
146   //\r
147   Status = BdsBootByDiskSignatureAndPartition (\r
148             Option,\r
149             (HARDDRIVE_DEVICE_PATH *) DevicePath,\r
150             Option->LoadOptionsSize,\r
151             Option->LoadOptions,\r
152             ExitDataSize,\r
153             ExitData\r
154             );\r
155   if (!EFI_ERROR (Status)) {\r
156     //\r
157     // If we found a disk signature and partition device path return success\r
158     //\r
159     return EFI_SUCCESS;\r
160   }\r
161 \r
162   EfiSignalEventReadyToBoot ();\r
163 \r
164   //\r
165   // Set Boot Current\r
166   //\r
167   gRT->SetVariable (\r
168         L"BootCurrent",\r
169         &gEfiGlobalVariableGuid,\r
170         EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
171         sizeof (UINT16),\r
172         &Option->BootCurrent\r
173         );\r
174 \r
175   if ((DevicePathType (Option->DevicePath) == BBS_DEVICE_PATH) &&\r
176       (DevicePathSubType (Option->DevicePath) == BBS_BBS_DP)\r
177     ) {\r
178     //\r
179     // Check to see if we should legacy BOOT. If yes then do the legacy boot\r
180     //\r
181     return BdsLibDoLegacyBoot (Option);\r
182   }\r
183 \r
184   DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Booting EFI 1.1 way %S\n", Option->Description));\r
185 \r
186   Status = gBS->LoadImage (\r
187                   TRUE,\r
188                   mBdsImageHandle,\r
189                   DevicePath,\r
190                   NULL,\r
191                   0,\r
192                   &ImageHandle\r
193                   );\r
194 \r
195   //\r
196   // If we didn't find an image, we may need to load the default\r
197   // boot behavior for the device.\r
198   //\r
199   if (EFI_ERROR (Status)) {\r
200     //\r
201     // Find a Simple File System protocol on the device path. If the remaining\r
202     // device path is set to end then no Files are being specified, so try\r
203     // the removable media file name.\r
204     //\r
205     TempDevicePath = DevicePath;\r
206     Status = gBS->LocateDevicePath (\r
207                     &gEfiSimpleFileSystemProtocolGuid,\r
208                     &TempDevicePath,\r
209                     &Handle\r
210                     );\r
211     if (!EFI_ERROR (Status) && IsDevicePathEnd (TempDevicePath)) {\r
212       FilePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);\r
213       if (FilePath) {\r
214         //\r
215         // Issue a dummy read to the device to check for media change.\r
216         // When the removable media is changed, any Block IO read/write will\r
217         // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is\r
218         // returned. After the Block IO protocol is reinstalled, subsequent\r
219         // Block IO read/write will success.\r
220         //\r
221         Status = gBS->HandleProtocol (\r
222                         Handle,\r
223                         &gEfiBlockIoProtocolGuid,\r
224                         (VOID **) &BlkIo\r
225                         );\r
226         if (!EFI_ERROR (Status)) {\r
227           Buffer = AllocatePool (BlkIo->Media->BlockSize);\r
228           if (Buffer != NULL) {\r
229             BlkIo->ReadBlocks (\r
230                      BlkIo,\r
231                      BlkIo->Media->MediaId,\r
232                      0,\r
233                      BlkIo->Media->BlockSize,\r
234                      Buffer\r
235                      );\r
236             gBS->FreePool (Buffer);\r
237           }\r
238         }\r
239 \r
240         Status = gBS->LoadImage (\r
241                         TRUE,\r
242                         mBdsImageHandle,\r
243                         FilePath,\r
244                         NULL,\r
245                         0,\r
246                         &ImageHandle\r
247                         );\r
248         if (EFI_ERROR (Status)) {\r
249           //\r
250           // The DevicePath failed, and it's not a valid\r
251           // removable media device.\r
252           //\r
253           goto Done;\r
254         }\r
255       }\r
256     } else {\r
257       Status = EFI_NOT_FOUND;\r
258     }\r
259   }\r
260 \r
261   if (EFI_ERROR (Status)) {\r
262     //\r
263     // It there is any error from the Boot attempt exit now.\r
264     //\r
265     goto Done;\r
266   }\r
267   //\r
268   // Provide the image with it's load options\r
269   //\r
270   Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, &ImageInfo);\r
271   ASSERT_EFI_ERROR (Status);\r
272 \r
273   if (Option->LoadOptionsSize != 0) {\r
274     ImageInfo->LoadOptionsSize  = Option->LoadOptionsSize;\r
275     ImageInfo->LoadOptions      = Option->LoadOptions;\r
276   }\r
277   //\r
278   // Before calling the image, enable the Watchdog Timer for\r
279   // the 5 Minute period\r
280   //\r
281   gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);\r
282 \r
283   Status = gBS->StartImage (ImageHandle, ExitDataSize, ExitData);\r
284   DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Image Return Status = %r\n", Status));\r
285 \r
286   //\r
287   // Clear the Watchdog Timer after the image returns\r
288   //\r
289   gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);\r
290 \r
291 Done:\r
292   //\r
293   // Clear Boot Current\r
294   //\r
295   gRT->SetVariable (\r
296         L"BootCurrent",\r
297         &gEfiGlobalVariableGuid,\r
298         EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
299         0,\r
300         &Option->BootCurrent\r
301         );\r
302 \r
303   return Status;\r
304 }\r
305 \r
306 EFI_STATUS\r
307 BdsBootByDiskSignatureAndPartition (\r
308   IN  BDS_COMMON_OPTION          * Option,\r
309   IN  HARDDRIVE_DEVICE_PATH      * HardDriveDevicePath,\r
310   IN  UINT32                     LoadOptionsSize,\r
311   IN  VOID                       *LoadOptions,\r
312   OUT UINTN                      *ExitDataSize,\r
313   OUT CHAR16                     **ExitData OPTIONAL\r
314   )\r
315 /*++\r
316 \r
317 Routine Description:\r
318 \r
319   Check to see if a hard ware device path was passed in. If it was then search\r
320   all the block IO devices for the passed in hard drive device path. \r
321   \r
322 Arguments:\r
323 \r
324   Option - The current processing boot option.\r
325 \r
326   HardDriveDevicePath - EFI Device Path to boot, if it starts with a hard\r
327                         drive device path.\r
328 \r
329   LoadOptionsSize - Passed into gBS->StartImage ()\r
330                     via the loaded image protocol.\r
331 \r
332   LoadOptions     - Passed into gBS->StartImage ()\r
333                     via the loaded image protocol.\r
334 \r
335   ExitDataSize - returned directly from gBS->StartImage ()\r
336 \r
337   ExitData     - returned directly from gBS->StartImage ()\r
338 \r
339 Returns:\r
340 \r
341   EFI_SUCCESS   - Status from gBS->StartImage (),\r
342                   or BootByDiskSignatureAndPartition ()\r
343                   \r
344   EFI_NOT_FOUND - If the Device Path is not found in the system\r
345 \r
346 --*/\r
347 {\r
348   EFI_STATUS                Status;\r
349   UINTN                     BlockIoHandleCount;\r
350   EFI_HANDLE                *BlockIoBuffer;\r
351   EFI_DEVICE_PATH_PROTOCOL  *BlockIoDevicePath;\r
352   EFI_DEVICE_PATH_PROTOCOL  *BlockIoHdDevicePath;\r
353   HARDDRIVE_DEVICE_PATH     *TmpHdPath;\r
354   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
355   EFI_DEVICE_PATH_PROTOCOL  *NewDevicePath;\r
356   UINTN                     Index;\r
357   BOOLEAN                   DevicePathMatch;\r
358   HARDDRIVE_DEVICE_PATH     *TempPath;\r
359 \r
360   *ExitDataSize = 0;\r
361   *ExitData     = NULL;\r
362 \r
363   if ( !((DevicePathType (&HardDriveDevicePath->Header) == MEDIA_DEVICE_PATH) &&\r
364           (DevicePathSubType (&HardDriveDevicePath->Header) == MEDIA_HARDDRIVE_DP))\r
365         ) {\r
366     //\r
367     // If the HardDriveDevicePath does not start with a Hard Drive Device Path\r
368     // exit.\r
369     //\r
370     return EFI_NOT_FOUND;\r
371   }\r
372   //\r
373   // The boot device have already been connected\r
374   //\r
375   Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer);\r
376   if (EFI_ERROR (Status) || BlockIoHandleCount == 0) {\r
377     //\r
378     // If there was an error or there are no device handles that support\r
379     // the BLOCK_IO Protocol, then return.\r
380     //\r
381     return EFI_NOT_FOUND;\r
382   }\r
383   //\r
384   // Loop through all the device handles that support the BLOCK_IO Protocol\r
385   //\r
386   for (Index = 0; Index < BlockIoHandleCount; Index++) {\r
387 \r
388     Status = gBS->HandleProtocol (BlockIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID *) &BlockIoDevicePath);\r
389     if (EFI_ERROR (Status) || BlockIoDevicePath == NULL) {\r
390       continue;\r
391     }\r
392     //\r
393     // Make PreviousDevicePath == the device path node before the end node\r
394     //\r
395     DevicePath          = BlockIoDevicePath;\r
396     BlockIoHdDevicePath = NULL;\r
397 \r
398     //\r
399     // find HardDriver device path node\r
400     //\r
401     while (!IsDevicePathEnd (DevicePath)) {\r
402       if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) && \r
403           (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)\r
404           ) {\r
405         BlockIoHdDevicePath = DevicePath;\r
406         break;\r
407       }\r
408 \r
409       DevicePath = NextDevicePathNode (DevicePath);\r
410     }\r
411 \r
412     if (BlockIoHdDevicePath == NULL) {\r
413       continue;\r
414     }\r
415     //\r
416     // See if the harddrive device path in blockio matches the orig Hard Drive Node\r
417     //\r
418     DevicePathMatch = FALSE;\r
419 \r
420     TmpHdPath       = (HARDDRIVE_DEVICE_PATH *) BlockIoHdDevicePath;\r
421     TempPath        = (HARDDRIVE_DEVICE_PATH *) BdsLibUnpackDevicePath ((EFI_DEVICE_PATH_PROTOCOL *) HardDriveDevicePath);\r
422 \r
423     //\r
424     // Only several fields will be checked. NOT whole NODE\r
425     //\r
426     if ( TmpHdPath->PartitionNumber == TempPath->PartitionNumber &&\r
427         TmpHdPath->MBRType == TempPath->MBRType &&\r
428         TmpHdPath->SignatureType == TempPath->SignatureType &&\r
429         CompareGuid ((EFI_GUID *) TmpHdPath->Signature, (EFI_GUID *) TempPath->Signature)) {\r
430       //\r
431       // Get the matched device path\r
432       //\r
433       DevicePathMatch = TRUE;\r
434     }\r
435     //\r
436     // Only do the boot, when devicepath match\r
437     //\r
438     if (DevicePathMatch) {\r
439       //\r
440       // Combine the Block IO and Hard Drive Device path together and try\r
441       // to boot from it.\r
442       //\r
443       DevicePath    = NextDevicePathNode ((EFI_DEVICE_PATH_PROTOCOL *) HardDriveDevicePath);\r
444       NewDevicePath = AppendDevicePath (BlockIoDevicePath, DevicePath);\r
445 \r
446       //\r
447       // Recursive boot with new device path\r
448       //\r
449       Status = BdsLibBootViaBootOption (Option, NewDevicePath, ExitDataSize, ExitData);\r
450       if (!EFI_ERROR (Status)) {\r
451         break;\r
452       }\r
453     }\r
454   }\r
455 \r
456   gBS->FreePool (BlockIoBuffer);\r
457   return Status;\r
458 }\r
459 \r
460 EFI_STATUS\r
461 BdsLibDeleteOptionFromHandle (\r
462   IN  EFI_HANDLE                 Handle\r
463   )\r
464 /*++\r
465 \r
466 Routine Description:\r
467 \r
468   Delete the boot option associated with the handle passed in\r
469 \r
470 Arguments:\r
471 \r
472   Handle - The handle which present the device path to create boot option\r
473 \r
474 Returns:\r
475 \r
476   EFI_SUCCESS           - Delete the boot option success\r
477 \r
478   EFI_NOT_FOUND         - If the Device Path is not found in the system\r
479 \r
480   EFI_OUT_OF_RESOURCES  - Lack of memory resource\r
481 \r
482   Other                 - Error return value from SetVariable()\r
483 \r
484 --*/\r
485 {\r
486   UINT16                    *BootOrder;\r
487   UINT8                     *BootOptionVar;\r
488   UINTN                     BootOrderSize;\r
489   UINTN                     BootOptionSize;\r
490   EFI_STATUS                Status;\r
491   UINTN                     Index;\r
492   UINT16                    BootOption[BOOT_OPTION_MAX_CHAR];\r
493   UINTN                     DevicePathSize;\r
494   UINTN                     OptionDevicePathSize;\r
495   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
496   EFI_DEVICE_PATH_PROTOCOL  *OptionDevicePath;\r
497   UINT8                     *TempPtr;\r
498   CHAR16                    *Description;\r
499 \r
500   Status        = EFI_SUCCESS;\r
501   BootOrder     = NULL;\r
502   BootOrderSize = 0;\r
503 \r
504   BootOrder = BdsLibGetVariableAndSize (\r
505                 L"BootOrder",\r
506                 &gEfiGlobalVariableGuid,\r
507                 &BootOrderSize\r
508                 );\r
509   if (NULL == BootOrder) {\r
510     return EFI_NOT_FOUND;\r
511   }\r
512 \r
513   DevicePath = DevicePathFromHandle (Handle);\r
514   if (DevicePath == NULL) {\r
515     return EFI_NOT_FOUND;\r
516   }\r
517   DevicePathSize = GetDevicePathSize (DevicePath);\r
518 \r
519   Index = 0;\r
520   while (Index < BootOrderSize / sizeof (UINT16)) {\r
521     UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);\r
522     BootOptionVar = BdsLibGetVariableAndSize (\r
523                       BootOption,\r
524                       &gEfiGlobalVariableGuid,\r
525                       &BootOptionSize\r
526                       );\r
527     if (NULL == BootOptionVar) {\r
528       gBS->FreePool (BootOrder);\r
529       return EFI_OUT_OF_RESOURCES;\r
530     }\r
531 \r
532     TempPtr = BootOptionVar;\r
533     TempPtr += sizeof (UINT32) + sizeof (UINT16);\r
534     Description = (CHAR16 *) TempPtr;\r
535     TempPtr += StrSize ((CHAR16 *) TempPtr);\r
536     OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;\r
537     OptionDevicePathSize = GetDevicePathSize (OptionDevicePath);\r
538 \r
539     //\r
540     // Check whether the device path match\r
541     //\r
542     if ((OptionDevicePathSize == DevicePathSize) &&\r
543         (CompareMem (DevicePath, OptionDevicePath, DevicePathSize) == 0)) {\r
544       BdsDeleteBootOption (BootOrder[Index], BootOrder, &BootOrderSize);\r
545       gBS->FreePool (BootOptionVar);\r
546       break;\r
547     }\r
548 \r
549     gBS->FreePool (BootOptionVar);\r
550     Index++;\r
551   }\r
552 \r
553   Status = gRT->SetVariable (\r
554                   L"BootOrder",\r
555                   &gEfiGlobalVariableGuid,\r
556                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
557                   BootOrderSize,\r
558                   BootOrder\r
559                   );\r
560 \r
561   gBS->FreePool (BootOrder);\r
562 \r
563   return Status;\r
564 }\r
565 \r
566 EFI_STATUS\r
567 BdsDeleteAllInvalidEfiBootOption (\r
568   VOID\r
569   )\r
570 /*++\r
571 \r
572 Routine Description:\r
573 \r
574   Delete all invalid EFI boot options. The probable invalid boot option could\r
575   be Removable media or Network boot device.\r
576 \r
577 Arguments:\r
578 \r
579   VOID\r
580 \r
581 Returns:\r
582 \r
583   EFI_SUCCESS           - Delete all invalid boot option success\r
584 \r
585   EFI_NOT_FOUND         - Variable "BootOrder" is not found\r
586 \r
587   EFI_OUT_OF_RESOURCES  - Lack of memory resource\r
588 \r
589   Other                 - Error return value from SetVariable()\r
590 \r
591 --*/\r
592 {\r
593   UINT16                    *BootOrder;\r
594   UINT8                     *BootOptionVar;\r
595   UINTN                     BootOrderSize;\r
596   UINTN                     BootOptionSize;\r
597   EFI_STATUS                Status;\r
598   UINTN                     Index;\r
599   UINTN                     Index2;\r
600   UINT16                    BootOption[BOOT_OPTION_MAX_CHAR];\r
601   UINTN                     OptionDevicePathSize;\r
602   EFI_DEVICE_PATH_PROTOCOL  *TempDevicePath;\r
603   EFI_DEVICE_PATH_PROTOCOL  *LastDeviceNode;\r
604   EFI_DEVICE_PATH_PROTOCOL  *OptionDevicePath;\r
605   UINT8                     *TempPtr;\r
606   CHAR16                    *Description;\r
607   EFI_HANDLE                Handle;\r
608   BOOLEAN                   NeedDelete;\r
609 \r
610   Status        = EFI_SUCCESS;\r
611   BootOrder     = NULL;\r
612   BootOrderSize = 0;\r
613 \r
614   BootOrder = BdsLibGetVariableAndSize (\r
615                 L"BootOrder",\r
616                 &gEfiGlobalVariableGuid,\r
617                 &BootOrderSize\r
618                 );\r
619   if (NULL == BootOrder) {\r
620     return EFI_NOT_FOUND;\r
621   }\r
622 \r
623   Index = 0;\r
624   while (Index < BootOrderSize / sizeof (UINT16)) {\r
625     UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);\r
626     BootOptionVar = BdsLibGetVariableAndSize (\r
627                       BootOption,\r
628                       &gEfiGlobalVariableGuid,\r
629                       &BootOptionSize\r
630                       );\r
631     if (NULL == BootOptionVar) {\r
632       gBS->FreePool (BootOrder);\r
633       return EFI_OUT_OF_RESOURCES;\r
634     }\r
635 \r
636     TempPtr = BootOptionVar;\r
637     TempPtr += sizeof (UINT32) + sizeof (UINT16);\r
638     Description = (CHAR16 *) TempPtr;\r
639     TempPtr += StrSize ((CHAR16 *) TempPtr);\r
640     OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;\r
641     OptionDevicePathSize = GetDevicePathSize (OptionDevicePath);\r
642 \r
643     //\r
644     // Skip legacy boot option (BBS boot device)\r
645     //\r
646     if ((DevicePathType (OptionDevicePath) == BBS_DEVICE_PATH) &&\r
647         (DevicePathSubType (OptionDevicePath) == BBS_BBS_DP)) {\r
648       gBS->FreePool (BootOptionVar);\r
649       Index++;\r
650       continue;\r
651     }\r
652 \r
653     TempDevicePath = OptionDevicePath;\r
654     LastDeviceNode = OptionDevicePath;\r
655     while (!EfiIsDevicePathEnd (TempDevicePath)) {\r
656       LastDeviceNode = TempDevicePath;\r
657       TempDevicePath = EfiNextDevicePathNode (TempDevicePath);\r
658     }\r
659     //\r
660     // Skip the boot option that point to a file, since the device path in \r
661     // removable media boot option doesn't contains a file name.\r
662     //\r
663     if (((DevicePathType (LastDeviceNode) == MEDIA_DEVICE_PATH) &&\r
664          (DevicePathSubType (LastDeviceNode) == MEDIA_FILEPATH_DP)) ||\r
665         //\r
666         // Skip boot option for internal Shell, it's always valid\r
667         //\r
668         (EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode) != NULL)) {\r
669       gBS->FreePool (BootOptionVar);\r
670       Index++;\r
671       continue;\r
672     }\r
673 \r
674     NeedDelete = TRUE;\r
675     //\r
676     // Check if it's a valid boot option for removable media\r
677     //\r
678     TempDevicePath = OptionDevicePath;\r
679     Status = gBS->LocateDevicePath (\r
680                     &gEfiSimpleFileSystemProtocolGuid,\r
681                     &TempDevicePath,\r
682                     &Handle\r
683                     );\r
684     if (!EFI_ERROR (Status)) {\r
685       NeedDelete = FALSE;\r
686     }\r
687     //\r
688     // Check if it's a valid boot option for network boot device\r
689     //\r
690     TempDevicePath = OptionDevicePath;\r
691     Status = gBS->LocateDevicePath (\r
692                     &gEfiLoadFileProtocolGuid,\r
693                     &TempDevicePath,\r
694                     &Handle\r
695                     );\r
696     if (!EFI_ERROR (Status)) {\r
697       NeedDelete = FALSE;\r
698     }\r
699 \r
700     if (NeedDelete) {\r
701       //\r
702       // Delete this invalid boot option "Boot####"\r
703       //\r
704       Status = gRT->SetVariable (\r
705                       BootOption,\r
706                       &gEfiGlobalVariableGuid,\r
707                       EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
708                       0,\r
709                       NULL\r
710                       );\r
711       //\r
712       // Mark this boot option in boot order as deleted\r
713       //\r
714       BootOrder[Index] = 0xffff;\r
715     }\r
716 \r
717     gBS->FreePool (BootOptionVar);\r
718     Index++;\r
719   }\r
720 \r
721   //\r
722   // Adjust boot order array\r
723   //\r
724   Index2 = 0;\r
725   for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {\r
726     if (BootOrder[Index] != 0xffff) {\r
727       BootOrder[Index2] = BootOrder[Index];\r
728       Index2 ++;\r
729     }\r
730   }\r
731   Status = gRT->SetVariable (\r
732                   L"BootOrder",\r
733                   &gEfiGlobalVariableGuid,\r
734                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
735                   Index2 * sizeof (UINT16),\r
736                   BootOrder\r
737                   );\r
738 \r
739   gBS->FreePool (BootOrder);\r
740 \r
741   return Status;\r
742 }\r
743 \r
744 EFI_STATUS\r
745 BdsLibEnumerateAllBootOption (\r
746   IN OUT LIST_ENTRY      *BdsBootOptionList\r
747   )\r
748 /*++\r
749 \r
750 Routine Description:\r
751 \r
752   This function will enumerate all possible boot device in the system,\r
753   it will only excute once of every boot.\r
754 \r
755 Arguments:\r
756 \r
757   BdsBootOptionList - The header of the link list which indexed all\r
758                       current boot options\r
759 \r
760 Returns:\r
761 \r
762   EFI_SUCCESS - Finished all the boot device enumerate and create\r
763                 the boot option base on that boot device\r
764 \r
765 --*/\r
766 {\r
767   EFI_STATUS                    Status;\r
768   UINT16                        BootOptionNumber;\r
769   UINTN                         NumberFileSystemHandles;\r
770   EFI_HANDLE                    *FileSystemHandles;\r
771   EFI_BLOCK_IO_PROTOCOL         *BlkIo;\r
772   UINTN                         Index;\r
773   UINTN                         NumberLoadFileHandles;\r
774   EFI_HANDLE                    *LoadFileHandles;\r
775   VOID                          *ProtocolInstance;\r
776   EFI_FIRMWARE_VOLUME_PROTOCOL  *Fv;\r
777   UINTN                         FvHandleCount;\r
778   EFI_HANDLE                    *FvHandleBuffer;\r
779   EFI_FV_FILETYPE               Type;\r
780   UINTN                         Size;\r
781   EFI_FV_FILE_ATTRIBUTES        Attributes;\r
782   UINT32                        AuthenticationStatus;\r
783   EFI_DEVICE_PATH_PROTOCOL      *FilePath;\r
784   EFI_HANDLE                    ImageHandle;\r
785   EFI_LOADED_IMAGE_PROTOCOL     *ImageInfo;\r
786   BOOLEAN                       NeedDelete;\r
787 \r
788   BootOptionNumber = 0;\r
789 \r
790   //\r
791   // If the boot device enumerate happened, just get the boot\r
792   // device from the boot order variable\r
793   //\r
794   if (mEnumBootDevice) {\r
795     BdsLibBuildOptionFromVar (BdsBootOptionList, L"BootOrder");\r
796     return EFI_SUCCESS;\r
797   }\r
798   //\r
799   // Notes: this dirty code is to get the legacy boot option from the\r
800   // BBS table and create to variable as the EFI boot option, it should\r
801   // be removed after the CSM can provide legacy boot option directly\r
802   //\r
803   REFRESH_LEGACY_BOOT_OPTIONS;\r
804 \r
805   //\r
806   // Delete invalid boot option\r
807   //\r
808   BdsDeleteAllInvalidEfiBootOption ();\r
809   //\r
810   // Parse removable media\r
811   //\r
812   gBS->LocateHandleBuffer (\r
813         ByProtocol,\r
814         &gEfiSimpleFileSystemProtocolGuid,\r
815         NULL,\r
816         &NumberFileSystemHandles,\r
817         &FileSystemHandles\r
818         );\r
819   for (Index = 0; Index < NumberFileSystemHandles; Index++) {\r
820     Status = gBS->HandleProtocol (\r
821                     FileSystemHandles[Index],\r
822                     &gEfiBlockIoProtocolGuid,\r
823                     (VOID **) &BlkIo\r
824                     );\r
825     if (!EFI_ERROR (Status)) {\r
826       if (!BlkIo->Media->RemovableMedia) {\r
827         //\r
828         // If the file system handle supports a BlkIo protocol,\r
829         // skip the removable media devices\r
830         //\r
831         continue;\r
832       }\r
833     }\r
834 \r
835     //\r
836     // Do the removable Media thing. \EFI\BOOT\boot{machinename}.EFI\r
837     //  machinename is ia32, ia64, x64, ...\r
838     //\r
839     FilePath = FileDevicePath (FileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);\r
840     NeedDelete = TRUE;\r
841     Status = gBS->LoadImage (\r
842                     TRUE,\r
843                     mBdsImageHandle,\r
844                     FilePath,\r
845                     NULL,\r
846                     0,\r
847                     &ImageHandle\r
848                     );\r
849     if (!EFI_ERROR(Status)) {\r
850       //\r
851       // Verify the image is a EFI application (and not a driver)\r
852       //\r
853       Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);\r
854       ASSERT (!EFI_ERROR(Status));\r
855 \r
856       if (ImageInfo->ImageCodeType == EfiLoaderCode) {\r
857         NeedDelete = FALSE;\r
858       }\r
859     }\r
860 \r
861     if (NeedDelete) {\r
862       //\r
863       // No such file or the file is not a EFI application, delete this boot option\r
864       //\r
865       BdsLibDeleteOptionFromHandle (FileSystemHandles[Index]);\r
866     } else {\r
867       BdsLibBuildOptionFromHandle (FileSystemHandles[Index], BdsBootOptionList);\r
868       BootOptionNumber++;\r
869     }\r
870   }\r
871 \r
872   if (NumberFileSystemHandles) {\r
873     gBS->FreePool (FileSystemHandles);\r
874   }\r
875   //\r
876   // Parse Network Boot Device\r
877   //\r
878   gBS->LocateHandleBuffer (\r
879         ByProtocol,\r
880         &gEfiSimpleNetworkProtocolGuid,\r
881         NULL,\r
882         &NumberLoadFileHandles,\r
883         &LoadFileHandles\r
884         );\r
885   for (Index = 0; Index < NumberLoadFileHandles; Index++) {\r
886     Status = gBS->HandleProtocol (\r
887                     LoadFileHandles[Index],\r
888                     &gEfiLoadFileProtocolGuid,\r
889                     (VOID **) &ProtocolInstance\r
890                     );\r
891     if (EFI_ERROR (Status)) {\r
892       continue;\r
893     }\r
894 \r
895     BdsLibBuildOptionFromHandle (LoadFileHandles[Index], BdsBootOptionList);\r
896     BootOptionNumber++;\r
897   }\r
898 \r
899   if (NumberLoadFileHandles) {\r
900     gBS->FreePool (LoadFileHandles);\r
901   }\r
902   //\r
903   // Check if we have on flash shell\r
904   //\r
905   gBS->LocateHandleBuffer (\r
906         ByProtocol,\r
907         &gEfiFirmwareVolumeProtocolGuid,\r
908         NULL,\r
909         &FvHandleCount,\r
910         &FvHandleBuffer\r
911         );\r
912   for (Index = 0; Index < FvHandleCount; Index++) {\r
913     gBS->HandleProtocol (\r
914           FvHandleBuffer[Index],\r
915           &gEfiFirmwareVolumeProtocolGuid,\r
916           (VOID **) &Fv\r
917           );\r
918 \r
919     Status = Fv->ReadFile (\r
920                   Fv,\r
921                   &gEfiShellFileGuid,\r
922                   NULL,\r
923                   &Size,\r
924                   &Type,\r
925                   &Attributes,\r
926                   &AuthenticationStatus\r
927                   );\r
928     if (EFI_ERROR (Status)) {\r
929       //\r
930       // Skip if no shell file in the FV\r
931       //\r
932       continue;\r
933     }\r
934     //\r
935     // Build the shell boot option\r
936     //\r
937     BdsLibBuildOptionFromShell (FvHandleBuffer[Index], BdsBootOptionList);\r
938     BootOptionNumber++;\r
939   }\r
940 \r
941   if (FvHandleCount) {\r
942     gBS->FreePool (FvHandleBuffer);\r
943   }\r
944   //\r
945   // Make sure every boot only have one time\r
946   // boot device enumerate\r
947   //\r
948   BdsLibBuildOptionFromVar (BdsBootOptionList, L"BootOrder");\r
949   mEnumBootDevice = TRUE;\r
950 \r
951   return EFI_SUCCESS;\r
952 }\r
953 \r
954 VOID\r
955 BdsLibBuildOptionFromHandle (\r
956   IN  EFI_HANDLE             Handle,\r
957   IN  LIST_ENTRY             *BdsBootOptionList\r
958   )\r
959 /*++\r
960 \r
961 Routine Description:\r
962   \r
963   Build the boot option with the handle parsed in\r
964   \r
965 Arguments:\r
966 \r
967   Handle - The handle which present the device path to create boot option\r
968   \r
969   BdsBootOptionList - The header of the link list which indexed all current\r
970                       boot options\r
971 \r
972 Returns:\r
973 \r
974   VOID\r
975 \r
976 --*/\r
977 {\r
978   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
979   CHAR16                    *TempString;\r
980 \r
981   DevicePath  = DevicePathFromHandle (Handle);\r
982   TempString  = DevicePathToStr (DevicePath);\r
983 \r
984   //\r
985   // Create and register new boot option\r
986   //\r
987   BdsLibRegisterNewOption (BdsBootOptionList, DevicePath, TempString, L"BootOrder");\r
988 }\r
989 \r
990 VOID\r
991 BdsLibBuildOptionFromShell (\r
992   IN EFI_HANDLE              Handle,\r
993   IN OUT LIST_ENTRY          *BdsBootOptionList\r
994   )\r
995 /*++\r
996 \r
997 Routine Description:\r
998   \r
999   Build the on flash shell boot option with the handle parsed in\r
1000   \r
1001 Arguments:\r
1002 \r
1003   Handle - The handle which present the device path to create on flash shell\r
1004            boot option\r
1005   \r
1006   BdsBootOptionList - The header of the link list which indexed all current\r
1007                       boot options\r
1008 \r
1009 Returns:\r
1010 \r
1011   None\r
1012 \r
1013 --*/\r
1014 {\r
1015   EFI_DEVICE_PATH_PROTOCOL          *DevicePath;\r
1016   MEDIA_FW_VOL_FILEPATH_DEVICE_PATH ShellNode;\r
1017 \r
1018   DevicePath = DevicePathFromHandle (Handle);\r
1019 \r
1020   //\r
1021   // Build the shell device path\r
1022   //\r
1023   EfiInitializeFwVolDevicepathNode (&ShellNode, &gEfiShellFileGuid);\r
1024   DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &ShellNode);\r
1025 \r
1026   //\r
1027   // Create and register the shell boot option\r
1028   //\r
1029   BdsLibRegisterNewOption (BdsBootOptionList, DevicePath, L"Internal EFI Shell", L"BootOrder");\r
1030 \r
1031 }\r
1032 \r
1033 VOID\r
1034 BdsLibBootNext (\r
1035   VOID\r
1036   )\r
1037 /*++\r
1038 \r
1039 Routine Description:\r
1040   \r
1041   Boot from the EFI1.1 spec defined "BootNext" variable\r
1042   \r
1043 Arguments:\r
1044 \r
1045   None\r
1046   \r
1047 Returns:\r
1048 \r
1049   None\r
1050 \r
1051 --*/\r
1052 {\r
1053   UINT16            *BootNext;\r
1054   UINTN             BootNextSize;\r
1055   CHAR16            Buffer[20];\r
1056   BDS_COMMON_OPTION *BootOption;\r
1057   LIST_ENTRY        TempList;\r
1058   UINTN             ExitDataSize;\r
1059   CHAR16            *ExitData;\r
1060 \r
1061   //\r
1062   // Init the boot option name buffer and temp link list\r
1063   //\r
1064   InitializeListHead (&TempList);\r
1065   ZeroMem (Buffer, sizeof (Buffer));\r
1066 \r
1067   BootNext = BdsLibGetVariableAndSize (\r
1068               L"BootNext",\r
1069               &gEfiGlobalVariableGuid,\r
1070               &BootNextSize\r
1071               );\r
1072 \r
1073   //\r
1074   // Clear the boot next variable first\r
1075   //\r
1076   if (BootNext != NULL) {\r
1077     gRT->SetVariable (\r
1078           L"BootNext",\r
1079           &gEfiGlobalVariableGuid,\r
1080           EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
1081           0,\r
1082           BootNext\r
1083           );\r
1084 \r
1085     //\r
1086     // Start to build the boot option and try to boot\r
1087     //\r
1088     UnicodeSPrint (Buffer, sizeof (Buffer), L"Boot%04x", *BootNext);\r
1089     BootOption = BdsLibVariableToOption (&TempList, Buffer);\r
1090     BdsLibConnectDevicePath (BootOption->DevicePath);\r
1091     BdsLibBootViaBootOption (BootOption, BootOption->DevicePath, &ExitDataSize, &ExitData);\r
1092   }\r
1093 \r
1094 }\r