• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   BDS Lib functions which relate with create or process the boot option.
3 
4 Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "InternalBdsLib.h"
16 #include "String.h"
17 #include <Library/NetLib.h>
18 #include "Library/DebugLib.h"
19 
20 BOOLEAN mEnumBootDevice = FALSE;
21 EFI_HII_HANDLE gBdsLibStringPackHandle = NULL;
22 
23 /**
24   The constructor function register UNI strings into imageHandle.
25 
26   It will ASSERT() if that operation fails and it will always return EFI_SUCCESS.
27 
28   @param  ImageHandle   The firmware allocated handle for the EFI image.
29   @param  SystemTable   A pointer to the EFI System Table.
30 
31   @retval EFI_SUCCESS   The constructor successfully added string package.
32   @retval Other value   The constructor can't add string package.
33 
34 **/
35 EFI_STATUS
36 EFIAPI
GenericBdsLibConstructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)37 GenericBdsLibConstructor (
38   IN EFI_HANDLE        ImageHandle,
39   IN EFI_SYSTEM_TABLE  *SystemTable
40   )
41 {
42 
43   gBdsLibStringPackHandle = HiiAddPackages (
44                               &gBdsLibStringPackageGuid,
45                               ImageHandle,
46                               GenericBdsLibStrings,
47                               NULL
48                               );
49 
50   ASSERT (gBdsLibStringPackHandle != NULL);
51 
52   return EFI_SUCCESS;
53 }
54 
55 /**
56   Deletete the Boot Option from EFI Variable. The Boot Order Arrray
57   is also updated.
58 
59   @param OptionNumber    The number of Boot option want to be deleted.
60   @param BootOrder       The Boot Order array.
61   @param BootOrderSize   The size of the Boot Order Array.
62 
63   @retval  EFI_SUCCESS           The Boot Option Variable was found and removed
64   @retval  EFI_UNSUPPORTED       The Boot Option Variable store was inaccessible
65   @retval  EFI_NOT_FOUND         The Boot Option Variable was not found
66 **/
67 EFI_STATUS
68 EFIAPI
BdsDeleteBootOption(IN UINTN OptionNumber,IN OUT UINT16 * BootOrder,IN OUT UINTN * BootOrderSize)69 BdsDeleteBootOption (
70   IN UINTN                       OptionNumber,
71   IN OUT UINT16                  *BootOrder,
72   IN OUT UINTN                   *BootOrderSize
73   )
74 {
75   CHAR16      BootOption[9];
76   UINTN       Index;
77   EFI_STATUS  Status;
78 
79   UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", OptionNumber);
80   Status = gRT->SetVariable (
81                   BootOption,
82                   &gEfiGlobalVariableGuid,
83                   0,
84                   0,
85                   NULL
86                   );
87   //
88   // Deleting variable with existing variable implementation shouldn't fail.
89   //
90   ASSERT_EFI_ERROR (Status);
91 
92   //
93   // adjust boot order array
94   //
95   for (Index = 0; Index < *BootOrderSize / sizeof (UINT16); Index++) {
96     if (BootOrder[Index] == OptionNumber) {
97       CopyMem (&BootOrder[Index], &BootOrder[Index+1], *BootOrderSize - (Index+1) * sizeof (UINT16));
98       *BootOrderSize -= sizeof (UINT16);
99       break;
100     }
101   }
102 
103   return Status;
104 }
105 /**
106 
107   Translate the first n characters of an Ascii string to
108   Unicode characters. The count n is indicated by parameter
109   Size. If Size is greater than the length of string, then
110   the entire string is translated.
111 
112 
113   @param AStr               Pointer to input Ascii string.
114   @param Size               The number of characters to translate.
115   @param UStr               Pointer to output Unicode string buffer.
116 
117 **/
118 VOID
AsciiToUnicodeSize(IN UINT8 * AStr,IN UINTN Size,OUT UINT16 * UStr)119 AsciiToUnicodeSize (
120   IN UINT8              *AStr,
121   IN UINTN              Size,
122   OUT UINT16            *UStr
123   )
124 {
125   UINTN Idx;
126 
127   Idx = 0;
128   while (AStr[Idx] != 0) {
129     UStr[Idx] = (CHAR16) AStr[Idx];
130     if (Idx == Size) {
131       break;
132     }
133 
134     Idx++;
135   }
136   UStr[Idx] = 0;
137 }
138 
139 /**
140   Build Legacy Device Name String according.
141 
142   @param CurBBSEntry     BBS Table.
143   @param Index           Index.
144   @param BufSize         The buffer size.
145   @param BootString      The output string.
146 
147 **/
148 VOID
BdsBuildLegacyDevNameString(IN BBS_TABLE * CurBBSEntry,IN UINTN Index,IN UINTN BufSize,OUT CHAR16 * BootString)149 BdsBuildLegacyDevNameString (
150   IN  BBS_TABLE                 *CurBBSEntry,
151   IN  UINTN                     Index,
152   IN  UINTN                     BufSize,
153   OUT CHAR16                    *BootString
154   )
155 {
156   CHAR16  *Fmt;
157   CHAR16  *Type;
158   UINT8   *StringDesc;
159   CHAR16  Temp[80];
160 
161   switch (Index) {
162   //
163   // Primary Master
164   //
165   case 1:
166     Fmt = L"Primary Master %s";
167     break;
168 
169  //
170  // Primary Slave
171  //
172   case 2:
173     Fmt = L"Primary Slave %s";
174     break;
175 
176   //
177   // Secondary Master
178   //
179   case 3:
180     Fmt = L"Secondary Master %s";
181     break;
182 
183   //
184   // Secondary Slave
185   //
186   case 4:
187     Fmt = L"Secondary Slave %s";
188     break;
189 
190   default:
191     Fmt = L"%s";
192     break;
193   }
194 
195   switch (CurBBSEntry->DeviceType) {
196   case BBS_FLOPPY:
197     Type = L"Floppy";
198     break;
199 
200   case BBS_HARDDISK:
201     Type = L"Harddisk";
202     break;
203 
204   case BBS_CDROM:
205     Type = L"CDROM";
206     break;
207 
208   case BBS_PCMCIA:
209     Type = L"PCMCIAe";
210     break;
211 
212   case BBS_USB:
213     Type = L"USB";
214     break;
215 
216   case BBS_EMBED_NETWORK:
217     Type = L"Network";
218     break;
219 
220   case BBS_BEV_DEVICE:
221     Type = L"BEVe";
222     break;
223 
224   case BBS_UNKNOWN:
225   default:
226     Type = L"Unknown";
227     break;
228   }
229   //
230   // If current BBS entry has its description then use it.
231   //
232   StringDesc = (UINT8 *) (UINTN) ((CurBBSEntry->DescStringSegment << 4) + CurBBSEntry->DescStringOffset);
233   if (NULL != StringDesc) {
234     //
235     // Only get fisrt 32 characters, this is suggested by BBS spec
236     //
237     AsciiToUnicodeSize (StringDesc, 32, Temp);
238     Fmt   = L"%s";
239     Type  = Temp;
240   }
241 
242   //
243   // BbsTable 16 entries are for onboard IDE.
244   // Set description string for SATA harddisks, Harddisk 0 ~ Harddisk 11
245   //
246   if (Index >= 5 && Index <= 16 && (CurBBSEntry->DeviceType == BBS_HARDDISK || CurBBSEntry->DeviceType == BBS_CDROM)) {
247     Fmt = L"%s %d";
248     UnicodeSPrint (BootString, BufSize, Fmt, Type, Index - 5);
249   } else {
250     UnicodeSPrint (BootString, BufSize, Fmt, Type);
251   }
252 }
253 
254 /**
255 
256   Create a legacy boot option for the specified entry of
257   BBS table, save it as variable, and append it to the boot
258   order list.
259 
260 
261   @param CurrentBbsEntry    Pointer to current BBS table.
262   @param CurrentBbsDevPath  Pointer to the Device Path Protocol instance of BBS
263   @param Index              Index of the specified entry in BBS table.
264   @param BootOrderList      On input, the original boot order list.
265                             On output, the new boot order list attached with the
266                             created node.
267   @param BootOrderListSize  On input, the original size of boot order list.
268                             On output, the size of new boot order list.
269 
270   @retval  EFI_SUCCESS             Boot Option successfully created.
271   @retval  EFI_OUT_OF_RESOURCES    Fail to allocate necessary memory.
272   @retval  Other                   Error occurs while setting variable.
273 
274 **/
275 EFI_STATUS
BdsCreateLegacyBootOption(IN BBS_TABLE * CurrentBbsEntry,IN EFI_DEVICE_PATH_PROTOCOL * CurrentBbsDevPath,IN UINTN Index,IN OUT UINT16 ** BootOrderList,IN OUT UINTN * BootOrderListSize)276 BdsCreateLegacyBootOption (
277   IN BBS_TABLE                        *CurrentBbsEntry,
278   IN EFI_DEVICE_PATH_PROTOCOL         *CurrentBbsDevPath,
279   IN UINTN                            Index,
280   IN OUT UINT16                       **BootOrderList,
281   IN OUT UINTN                        *BootOrderListSize
282   )
283 {
284   EFI_STATUS           Status;
285   UINT16               CurrentBootOptionNo;
286   UINT16               BootString[10];
287   CHAR16               BootDesc[100];
288   CHAR8                HelpString[100];
289   UINT16               *NewBootOrderList;
290   UINTN                BufferSize;
291   UINTN                StringLen;
292   VOID                 *Buffer;
293   UINT8                *Ptr;
294   UINT16               CurrentBbsDevPathSize;
295   UINTN                BootOrderIndex;
296   UINTN                BootOrderLastIndex;
297   UINTN                ArrayIndex;
298   BOOLEAN              IndexNotFound;
299   BBS_BBS_DEVICE_PATH  *NewBbsDevPathNode;
300 
301   if ((*BootOrderList) == NULL) {
302     CurrentBootOptionNo = 0;
303   } else {
304     for (ArrayIndex = 0; ArrayIndex < (UINTN) (*BootOrderListSize / sizeof (UINT16)); ArrayIndex++) {
305       IndexNotFound = TRUE;
306       for (BootOrderIndex = 0; BootOrderIndex < (UINTN) (*BootOrderListSize / sizeof (UINT16)); BootOrderIndex++) {
307         if ((*BootOrderList)[BootOrderIndex] == ArrayIndex) {
308           IndexNotFound = FALSE;
309           break;
310         }
311       }
312 
313       if (!IndexNotFound) {
314         continue;
315       } else {
316         break;
317       }
318     }
319 
320     CurrentBootOptionNo = (UINT16) ArrayIndex;
321   }
322 
323   UnicodeSPrint (
324     BootString,
325     sizeof (BootString),
326     L"Boot%04x",
327     CurrentBootOptionNo
328     );
329 
330   BdsBuildLegacyDevNameString (CurrentBbsEntry, Index, sizeof (BootDesc), BootDesc);
331 
332   //
333   // Create new BBS device path node with description string
334   //
335   UnicodeStrToAsciiStr (BootDesc, HelpString);
336 
337   StringLen = AsciiStrLen (HelpString);
338   NewBbsDevPathNode = AllocateZeroPool (sizeof (BBS_BBS_DEVICE_PATH) + StringLen);
339   if (NewBbsDevPathNode == NULL) {
340     return EFI_OUT_OF_RESOURCES;
341   }
342   CopyMem (NewBbsDevPathNode, CurrentBbsDevPath, sizeof (BBS_BBS_DEVICE_PATH));
343   CopyMem (NewBbsDevPathNode->String, HelpString, StringLen + 1);
344   SetDevicePathNodeLength (&(NewBbsDevPathNode->Header), sizeof (BBS_BBS_DEVICE_PATH) + StringLen);
345 
346   //
347   // Create entire new CurrentBbsDevPath with end node
348   //
349   CurrentBbsDevPath = AppendDevicePathNode (
350                         NULL,
351                         (EFI_DEVICE_PATH_PROTOCOL *) NewBbsDevPathNode
352                         );
353    if (CurrentBbsDevPath == NULL) {
354     FreePool (NewBbsDevPathNode);
355     return EFI_OUT_OF_RESOURCES;
356   }
357 
358   CurrentBbsDevPathSize = (UINT16) (GetDevicePathSize (CurrentBbsDevPath));
359 
360   BufferSize = sizeof (UINT32) +
361     sizeof (UINT16) +
362     StrSize (BootDesc) +
363     CurrentBbsDevPathSize +
364     sizeof (BBS_TABLE) +
365     sizeof (UINT16);
366 
367   Buffer = AllocateZeroPool (BufferSize);
368   if (Buffer == NULL) {
369     FreePool (NewBbsDevPathNode);
370     FreePool (CurrentBbsDevPath);
371     return EFI_OUT_OF_RESOURCES;
372   }
373 
374   Ptr               = (UINT8 *) Buffer;
375 
376   *((UINT32 *) Ptr) = LOAD_OPTION_ACTIVE;
377   Ptr += sizeof (UINT32);
378 
379   *((UINT16 *) Ptr) = CurrentBbsDevPathSize;
380   Ptr += sizeof (UINT16);
381 
382   CopyMem (
383     Ptr,
384     BootDesc,
385     StrSize (BootDesc)
386     );
387   Ptr += StrSize (BootDesc);
388 
389   CopyMem (
390     Ptr,
391     CurrentBbsDevPath,
392     CurrentBbsDevPathSize
393     );
394   Ptr += CurrentBbsDevPathSize;
395 
396   CopyMem (
397     Ptr,
398     CurrentBbsEntry,
399     sizeof (BBS_TABLE)
400     );
401 
402   Ptr += sizeof (BBS_TABLE);
403   *((UINT16 *) Ptr) = (UINT16) Index;
404 
405   Status = gRT->SetVariable (
406                   BootString,
407                   &gEfiGlobalVariableGuid,
408                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
409                   BufferSize,
410                   Buffer
411                   );
412 
413   FreePool (Buffer);
414 
415   Buffer = NULL;
416 
417   NewBootOrderList = AllocateZeroPool (*BootOrderListSize + sizeof (UINT16));
418   if (NULL == NewBootOrderList) {
419     FreePool (NewBbsDevPathNode);
420     FreePool (CurrentBbsDevPath);
421     return EFI_OUT_OF_RESOURCES;
422   }
423 
424   if (*BootOrderList != NULL) {
425     CopyMem (NewBootOrderList, *BootOrderList, *BootOrderListSize);
426     FreePool (*BootOrderList);
427   }
428 
429   BootOrderLastIndex                    = (UINTN) (*BootOrderListSize / sizeof (UINT16));
430   NewBootOrderList[BootOrderLastIndex]  = CurrentBootOptionNo;
431   *BootOrderListSize += sizeof (UINT16);
432   *BootOrderList = NewBootOrderList;
433 
434   FreePool (NewBbsDevPathNode);
435   FreePool (CurrentBbsDevPath);
436   return Status;
437 }
438 
439 /**
440   Check if the boot option is a legacy one.
441 
442   @param BootOptionVar   The boot option data payload.
443   @param BbsEntry        The BBS Table.
444   @param BbsIndex        The table index.
445 
446   @retval TRUE           It is a legacy boot option.
447   @retval FALSE          It is not a legacy boot option.
448 
449 **/
450 BOOLEAN
BdsIsLegacyBootOption(IN UINT8 * BootOptionVar,OUT BBS_TABLE ** BbsEntry,OUT UINT16 * BbsIndex)451 BdsIsLegacyBootOption (
452   IN UINT8                 *BootOptionVar,
453   OUT BBS_TABLE            **BbsEntry,
454   OUT UINT16               *BbsIndex
455   )
456 {
457   UINT8                     *Ptr;
458   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
459   BOOLEAN                   Ret;
460   UINT16                    DevPathLen;
461 
462   Ptr = BootOptionVar;
463   Ptr += sizeof (UINT32);
464   DevPathLen = *(UINT16 *) Ptr;
465   Ptr += sizeof (UINT16);
466   Ptr += StrSize ((UINT16 *) Ptr);
467   DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Ptr;
468   if ((BBS_DEVICE_PATH == DevicePath->Type) && (BBS_BBS_DP == DevicePath->SubType)) {
469     Ptr += DevPathLen;
470     *BbsEntry = (BBS_TABLE *) Ptr;
471     Ptr += sizeof (BBS_TABLE);
472     *BbsIndex = *(UINT16 *) Ptr;
473     Ret       = TRUE;
474   } else {
475     *BbsEntry = NULL;
476     Ret       = FALSE;
477   }
478 
479   return Ret;
480 }
481 
482 /**
483   Delete all the invalid legacy boot options.
484 
485   @retval EFI_SUCCESS             All invalide legacy boot options are deleted.
486   @retval EFI_OUT_OF_RESOURCES    Fail to allocate necessary memory.
487   @retval EFI_NOT_FOUND           Fail to retrive variable of boot order.
488 **/
489 EFI_STATUS
490 EFIAPI
BdsDeleteAllInvalidLegacyBootOptions(VOID)491 BdsDeleteAllInvalidLegacyBootOptions (
492   VOID
493   )
494 {
495   UINT16                    *BootOrder;
496   UINT8                     *BootOptionVar;
497   UINTN                     BootOrderSize;
498   UINTN                     BootOptionSize;
499   EFI_STATUS                Status;
500   UINT16                    HddCount;
501   UINT16                    BbsCount;
502   HDD_INFO                  *LocalHddInfo;
503   BBS_TABLE                 *LocalBbsTable;
504   BBS_TABLE                 *BbsEntry;
505   UINT16                    BbsIndex;
506   EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
507   UINTN                     Index;
508   UINT16                    BootOption[10];
509   UINT16                    BootDesc[100];
510   BOOLEAN                   DescStringMatch;
511 
512   Status        = EFI_SUCCESS;
513   BootOrder     = NULL;
514   BootOrderSize = 0;
515   HddCount      = 0;
516   BbsCount      = 0;
517   LocalHddInfo  = NULL;
518   LocalBbsTable = NULL;
519   BbsEntry      = NULL;
520 
521   Status        = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
522   if (EFI_ERROR (Status)) {
523     return Status;
524   }
525 
526   BootOrder = BdsLibGetVariableAndSize (
527                 L"BootOrder",
528                 &gEfiGlobalVariableGuid,
529                 &BootOrderSize
530                 );
531   if (BootOrder == NULL) {
532     return EFI_NOT_FOUND;
533   }
534 
535   LegacyBios->GetBbsInfo (
536                 LegacyBios,
537                 &HddCount,
538                 &LocalHddInfo,
539                 &BbsCount,
540                 &LocalBbsTable
541                 );
542 
543   Index = 0;
544   while (Index < BootOrderSize / sizeof (UINT16)) {
545     UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
546     BootOptionVar = BdsLibGetVariableAndSize (
547                       BootOption,
548                       &gEfiGlobalVariableGuid,
549                       &BootOptionSize
550                       );
551     if (NULL == BootOptionVar) {
552       BootOptionSize = 0;
553       Status = gRT->GetVariable (
554                       BootOption,
555                       &gEfiGlobalVariableGuid,
556                       NULL,
557                       &BootOptionSize,
558                       BootOptionVar
559                       );
560       if (Status == EFI_NOT_FOUND) {
561         //
562         // Update BootOrder
563         //
564         BdsDeleteBootOption (
565           BootOrder[Index],
566           BootOrder,
567           &BootOrderSize
568           );
569         continue;
570       } else {
571         FreePool (BootOrder);
572         return EFI_OUT_OF_RESOURCES;
573       }
574     }
575 
576     //
577     // Skip Non-Legacy boot option
578     //
579     if (!BdsIsLegacyBootOption (BootOptionVar, &BbsEntry, &BbsIndex)) {
580       if (BootOptionVar!= NULL) {
581         FreePool (BootOptionVar);
582       }
583       Index++;
584       continue;
585     }
586 
587     if (BbsIndex < BbsCount) {
588       //
589       // Check if BBS Description String is changed
590       //
591       DescStringMatch = FALSE;
592       BdsBuildLegacyDevNameString (
593         &LocalBbsTable[BbsIndex],
594         BbsIndex,
595         sizeof (BootDesc),
596         BootDesc
597         );
598 
599       if (StrCmp (BootDesc, (UINT16*)(BootOptionVar + sizeof (UINT32) + sizeof (UINT16))) == 0) {
600         DescStringMatch = TRUE;
601       }
602 
603       if (!((LocalBbsTable[BbsIndex].BootPriority == BBS_IGNORE_ENTRY) ||
604             (LocalBbsTable[BbsIndex].BootPriority == BBS_DO_NOT_BOOT_FROM)) &&
605           (LocalBbsTable[BbsIndex].DeviceType == BbsEntry->DeviceType) &&
606           DescStringMatch) {
607         Index++;
608         continue;
609       }
610     }
611 
612     if (BootOptionVar != NULL) {
613       FreePool (BootOptionVar);
614     }
615     //
616     // should delete
617     //
618     BdsDeleteBootOption (
619       BootOrder[Index],
620       BootOrder,
621       &BootOrderSize
622       );
623   }
624 
625   //
626   // Adjust the number of boot options.
627   //
628   Status = gRT->SetVariable (
629                   L"BootOrder",
630                   &gEfiGlobalVariableGuid,
631                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
632                   BootOrderSize,
633                   BootOrder
634                   );
635   //
636   // Shrinking variable with existing variable implementation shouldn't fail.
637   //
638   ASSERT_EFI_ERROR (Status);
639   FreePool (BootOrder);
640 
641   return Status;
642 }
643 
644 /**
645   Find all legacy boot option by device type.
646 
647   @param BootOrder       The boot order array.
648   @param BootOptionNum   The number of boot option.
649   @param DevType         Device type.
650   @param DevName         Device name.
651   @param Attribute       The boot option attribute.
652   @param BbsIndex        The BBS table index.
653   @param OptionNumber    The boot option index.
654 
655   @retval TRUE           The Legacy boot option is found.
656   @retval FALSE          The legacy boot option is not found.
657 
658 **/
659 BOOLEAN
BdsFindLegacyBootOptionByDevTypeAndName(IN UINT16 * BootOrder,IN UINTN BootOptionNum,IN UINT16 DevType,IN CHAR16 * DevName,OUT UINT32 * Attribute,OUT UINT16 * BbsIndex,OUT UINT16 * OptionNumber)660 BdsFindLegacyBootOptionByDevTypeAndName (
661   IN UINT16                 *BootOrder,
662   IN UINTN                  BootOptionNum,
663   IN UINT16                 DevType,
664   IN CHAR16                 *DevName,
665   OUT UINT32                *Attribute,
666   OUT UINT16                *BbsIndex,
667   OUT UINT16                *OptionNumber
668   )
669 {
670   UINTN     Index;
671   CHAR16    BootOption[9];
672   UINTN     BootOptionSize;
673   UINT8     *BootOptionVar;
674   BBS_TABLE *BbsEntry;
675   BOOLEAN   Found;
676 
677   BbsEntry  = NULL;
678   Found     = FALSE;
679 
680   if (NULL == BootOrder) {
681     return Found;
682   }
683 
684   //
685   // Loop all boot option from variable
686   //
687   for (Index = 0; Index < BootOptionNum; Index++) {
688     UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", (UINTN) BootOrder[Index]);
689     BootOptionVar = BdsLibGetVariableAndSize (
690                       BootOption,
691                       &gEfiGlobalVariableGuid,
692                       &BootOptionSize
693                       );
694     if (NULL == BootOptionVar) {
695       continue;
696     }
697 
698     //
699     // Skip Non-legacy boot option
700     //
701     if (!BdsIsLegacyBootOption (BootOptionVar, &BbsEntry, BbsIndex)) {
702       FreePool (BootOptionVar);
703       continue;
704     }
705 
706     if (
707         (BbsEntry->DeviceType != DevType) ||
708         (StrCmp (DevName, (CHAR16*)(BootOptionVar + sizeof (UINT32) + sizeof (UINT16))) != 0)
709        ) {
710       FreePool (BootOptionVar);
711       continue;
712     }
713 
714     *Attribute    = *(UINT32 *) BootOptionVar;
715     *OptionNumber = BootOrder[Index];
716     Found         = TRUE;
717     FreePool (BootOptionVar);
718     break;
719   }
720 
721   return Found;
722 }
723 
724 /**
725   Create a legacy boot option.
726 
727   @param BbsItem         The BBS Table entry.
728   @param Index           Index of the specified entry in BBS table.
729   @param BootOrderList   The boot order list.
730   @param BootOrderListSize The size of boot order list.
731 
732   @retval EFI_OUT_OF_RESOURCE  No enough memory.
733   @retval EFI_SUCCESS          The function complete successfully.
734   @return Other value if the legacy boot option is not created.
735 
736 **/
737 EFI_STATUS
BdsCreateOneLegacyBootOption(IN BBS_TABLE * BbsItem,IN UINTN Index,IN OUT UINT16 ** BootOrderList,IN OUT UINTN * BootOrderListSize)738 BdsCreateOneLegacyBootOption (
739   IN BBS_TABLE              *BbsItem,
740   IN UINTN                  Index,
741   IN OUT UINT16             **BootOrderList,
742   IN OUT UINTN              *BootOrderListSize
743   )
744 {
745   BBS_BBS_DEVICE_PATH       BbsDevPathNode;
746   EFI_STATUS                Status;
747   EFI_DEVICE_PATH_PROTOCOL  *DevPath;
748 
749   DevPath                       = NULL;
750 
751   //
752   // Create device path node.
753   //
754   BbsDevPathNode.Header.Type    = BBS_DEVICE_PATH;
755   BbsDevPathNode.Header.SubType = BBS_BBS_DP;
756   SetDevicePathNodeLength (&BbsDevPathNode.Header, sizeof (BBS_BBS_DEVICE_PATH));
757   BbsDevPathNode.DeviceType = BbsItem->DeviceType;
758   CopyMem (&BbsDevPathNode.StatusFlag, &BbsItem->StatusFlags, sizeof (UINT16));
759 
760   DevPath = AppendDevicePathNode (
761               NULL,
762               (EFI_DEVICE_PATH_PROTOCOL *) &BbsDevPathNode
763               );
764   if (NULL == DevPath) {
765     return EFI_OUT_OF_RESOURCES;
766   }
767 
768   Status = BdsCreateLegacyBootOption (
769             BbsItem,
770             DevPath,
771             Index,
772             BootOrderList,
773             BootOrderListSize
774             );
775   BbsItem->BootPriority = 0x00;
776 
777   FreePool (DevPath);
778 
779   return Status;
780 }
781 
782 /**
783   Add the legacy boot options from BBS table if they do not exist.
784 
785   @retval EFI_SUCCESS          The boot options are added successfully
786                                or they are already in boot options.
787   @retval EFI_NOT_FOUND        No legacy boot options is found.
788   @retval EFI_OUT_OF_RESOURCE  No enough memory.
789   @return Other value          LegacyBoot options are not added.
790 **/
791 EFI_STATUS
792 EFIAPI
BdsAddNonExistingLegacyBootOptions(VOID)793 BdsAddNonExistingLegacyBootOptions (
794   VOID
795   )
796 {
797   UINT16                    *BootOrder;
798   UINTN                     BootOrderSize;
799   EFI_STATUS                Status;
800   CHAR16                    Desc[100];
801   UINT16                    HddCount;
802   UINT16                    BbsCount;
803   HDD_INFO                  *LocalHddInfo;
804   BBS_TABLE                 *LocalBbsTable;
805   UINT16                    BbsIndex;
806   EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
807   UINT16                    Index;
808   UINT32                    Attribute;
809   UINT16                    OptionNumber;
810   BOOLEAN                   Exist;
811 
812   HddCount      = 0;
813   BbsCount      = 0;
814   LocalHddInfo  = NULL;
815   LocalBbsTable = NULL;
816 
817   Status        = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
818   if (EFI_ERROR (Status)) {
819     return Status;
820   }
821 
822   LegacyBios->GetBbsInfo (
823                 LegacyBios,
824                 &HddCount,
825                 &LocalHddInfo,
826                 &BbsCount,
827                 &LocalBbsTable
828                 );
829 
830   BootOrder = BdsLibGetVariableAndSize (
831                 L"BootOrder",
832                 &gEfiGlobalVariableGuid,
833                 &BootOrderSize
834                 );
835   if (BootOrder == NULL) {
836     BootOrderSize = 0;
837   }
838 
839   for (Index = 0; Index < BbsCount; Index++) {
840     if ((LocalBbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) ||
841         (LocalBbsTable[Index].BootPriority == BBS_DO_NOT_BOOT_FROM)
842         ) {
843       continue;
844     }
845 
846     BdsBuildLegacyDevNameString (&LocalBbsTable[Index], Index, sizeof (Desc), Desc);
847 
848     Exist = BdsFindLegacyBootOptionByDevTypeAndName (
849               BootOrder,
850               BootOrderSize / sizeof (UINT16),
851               LocalBbsTable[Index].DeviceType,
852               Desc,
853               &Attribute,
854               &BbsIndex,
855               &OptionNumber
856               );
857     if (!Exist) {
858       //
859       // Not found such type of legacy device in boot options or we found but it's disabled
860       // so we have to create one and put it to the tail of boot order list
861       //
862       Status = BdsCreateOneLegacyBootOption (
863                 &LocalBbsTable[Index],
864                 Index,
865                 &BootOrder,
866                 &BootOrderSize
867                 );
868       if (!EFI_ERROR (Status)) {
869         ASSERT (BootOrder != NULL);
870         BbsIndex     = Index;
871         OptionNumber = BootOrder[BootOrderSize / sizeof (UINT16) - 1];
872       }
873     }
874 
875     ASSERT (BbsIndex == Index);
876   }
877 
878   Status = gRT->SetVariable (
879                   L"BootOrder",
880                   &gEfiGlobalVariableGuid,
881                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
882                   BootOrderSize,
883                   BootOrder
884                   );
885   if (BootOrder != NULL) {
886     FreePool (BootOrder);
887   }
888 
889   return Status;
890 }
891 
892 /**
893   Fill the device order buffer.
894 
895   @param BbsTable        The BBS table.
896   @param BbsType         The BBS Type.
897   @param BbsCount        The BBS Count.
898   @param Buf             device order buffer.
899 
900   @return The device order buffer.
901 
902 **/
903 UINT16 *
BdsFillDevOrderBuf(IN BBS_TABLE * BbsTable,IN BBS_TYPE BbsType,IN UINTN BbsCount,OUT UINT16 * Buf)904 BdsFillDevOrderBuf (
905   IN BBS_TABLE                    *BbsTable,
906   IN BBS_TYPE                     BbsType,
907   IN UINTN                        BbsCount,
908   OUT UINT16                      *Buf
909   )
910 {
911   UINTN Index;
912 
913   for (Index = 0; Index < BbsCount; Index++) {
914     if (BbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) {
915       continue;
916     }
917 
918     if (BbsTable[Index].DeviceType != BbsType) {
919       continue;
920     }
921 
922     *Buf = (UINT16) (Index & 0xFF);
923     Buf++;
924   }
925 
926   return Buf;
927 }
928 
929 /**
930   Create the device order buffer.
931 
932   @param BbsTable        The BBS table.
933   @param BbsCount        The BBS Count.
934 
935   @retval EFI_SUCCES             The buffer is created and the EFI variable named
936                                  VAR_LEGACY_DEV_ORDER and gEfiLegacyDevOrderVariableGuid is
937                                  set correctly.
938   @retval EFI_OUT_OF_RESOURCES   Memmory or storage is not enough.
939   @retval EFI_DEVICE_ERROR       Fail to add the device order into EFI variable fail
940                                  because of hardware error.
941 **/
942 EFI_STATUS
BdsCreateDevOrder(IN BBS_TABLE * BbsTable,IN UINT16 BbsCount)943 BdsCreateDevOrder (
944   IN BBS_TABLE                  *BbsTable,
945   IN UINT16                     BbsCount
946   )
947 {
948   UINTN                       Index;
949   UINTN                       FDCount;
950   UINTN                       HDCount;
951   UINTN                       CDCount;
952   UINTN                       NETCount;
953   UINTN                       BEVCount;
954   UINTN                       TotalSize;
955   UINTN                       HeaderSize;
956   LEGACY_DEV_ORDER_ENTRY      *DevOrder;
957   LEGACY_DEV_ORDER_ENTRY      *DevOrderPtr;
958   EFI_STATUS                  Status;
959 
960   FDCount     = 0;
961   HDCount     = 0;
962   CDCount     = 0;
963   NETCount    = 0;
964   BEVCount    = 0;
965   TotalSize   = 0;
966   HeaderSize  = sizeof (BBS_TYPE) + sizeof (UINT16);
967   DevOrder    = NULL;
968   Status      = EFI_SUCCESS;
969 
970   //
971   // Count all boot devices
972   //
973   for (Index = 0; Index < BbsCount; Index++) {
974     if (BbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) {
975       continue;
976     }
977 
978     switch (BbsTable[Index].DeviceType) {
979     case BBS_FLOPPY:
980       FDCount++;
981       break;
982 
983     case BBS_HARDDISK:
984       HDCount++;
985       break;
986 
987     case BBS_CDROM:
988       CDCount++;
989       break;
990 
991     case BBS_EMBED_NETWORK:
992       NETCount++;
993       break;
994 
995     case BBS_BEV_DEVICE:
996       BEVCount++;
997       break;
998 
999     default:
1000       break;
1001     }
1002   }
1003 
1004   TotalSize += (HeaderSize + sizeof (UINT16) * FDCount);
1005   TotalSize += (HeaderSize + sizeof (UINT16) * HDCount);
1006   TotalSize += (HeaderSize + sizeof (UINT16) * CDCount);
1007   TotalSize += (HeaderSize + sizeof (UINT16) * NETCount);
1008   TotalSize += (HeaderSize + sizeof (UINT16) * BEVCount);
1009 
1010   //
1011   // Create buffer to hold all boot device order
1012   //
1013   DevOrder = AllocateZeroPool (TotalSize);
1014   if (NULL == DevOrder) {
1015     return EFI_OUT_OF_RESOURCES;
1016   }
1017   DevOrderPtr          = DevOrder;
1018 
1019   DevOrderPtr->BbsType = BBS_FLOPPY;
1020   DevOrderPtr->Length  = (UINT16) (sizeof (DevOrderPtr->Length) + FDCount * sizeof (UINT16));
1021   DevOrderPtr          = (LEGACY_DEV_ORDER_ENTRY *) BdsFillDevOrderBuf (BbsTable, BBS_FLOPPY, BbsCount, DevOrderPtr->Data);
1022 
1023   DevOrderPtr->BbsType = BBS_HARDDISK;
1024   DevOrderPtr->Length  = (UINT16) (sizeof (UINT16) + HDCount * sizeof (UINT16));
1025   DevOrderPtr          = (LEGACY_DEV_ORDER_ENTRY *) BdsFillDevOrderBuf (BbsTable, BBS_HARDDISK, BbsCount, DevOrderPtr->Data);
1026 
1027   DevOrderPtr->BbsType = BBS_CDROM;
1028   DevOrderPtr->Length  = (UINT16) (sizeof (UINT16) + CDCount * sizeof (UINT16));
1029   DevOrderPtr          = (LEGACY_DEV_ORDER_ENTRY *) BdsFillDevOrderBuf (BbsTable, BBS_CDROM, BbsCount, DevOrderPtr->Data);
1030 
1031   DevOrderPtr->BbsType = BBS_EMBED_NETWORK;
1032   DevOrderPtr->Length  = (UINT16) (sizeof (UINT16) + NETCount * sizeof (UINT16));
1033   DevOrderPtr          = (LEGACY_DEV_ORDER_ENTRY *) BdsFillDevOrderBuf (BbsTable, BBS_EMBED_NETWORK, BbsCount, DevOrderPtr->Data);
1034 
1035   DevOrderPtr->BbsType = BBS_BEV_DEVICE;
1036   DevOrderPtr->Length  = (UINT16) (sizeof (UINT16) + BEVCount * sizeof (UINT16));
1037   DevOrderPtr          = (LEGACY_DEV_ORDER_ENTRY *) BdsFillDevOrderBuf (BbsTable, BBS_BEV_DEVICE, BbsCount, DevOrderPtr->Data);
1038 
1039   ASSERT (TotalSize == (UINTN) ((UINT8 *) DevOrderPtr - (UINT8 *) DevOrder));
1040 
1041   //
1042   // Save device order for legacy boot device to variable.
1043   //
1044   Status = gRT->SetVariable (
1045                   VAR_LEGACY_DEV_ORDER,
1046                   &gEfiLegacyDevOrderVariableGuid,
1047                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
1048                   TotalSize,
1049                   DevOrder
1050                   );
1051   FreePool (DevOrder);
1052 
1053   return Status;
1054 }
1055 
1056 /**
1057   Add the legacy boot devices from BBS table into
1058   the legacy device boot order.
1059 
1060   @retval EFI_SUCCESS           The boot devices are added successfully.
1061   @retval EFI_NOT_FOUND         The legacy boot devices are not found.
1062   @retval EFI_OUT_OF_RESOURCES  Memmory or storage is not enough.
1063   @retval EFI_DEVICE_ERROR      Fail to add the legacy device boot order into EFI variable
1064                                 because of hardware error.
1065 **/
1066 EFI_STATUS
1067 EFIAPI
BdsUpdateLegacyDevOrder(VOID)1068 BdsUpdateLegacyDevOrder (
1069   VOID
1070   )
1071 {
1072   LEGACY_DEV_ORDER_ENTRY      *DevOrder;
1073   LEGACY_DEV_ORDER_ENTRY      *NewDevOrder;
1074   LEGACY_DEV_ORDER_ENTRY      *Ptr;
1075   LEGACY_DEV_ORDER_ENTRY      *NewPtr;
1076   UINTN                       DevOrderSize;
1077   EFI_LEGACY_BIOS_PROTOCOL    *LegacyBios;
1078   EFI_STATUS                  Status;
1079   UINT16                      HddCount;
1080   UINT16                      BbsCount;
1081   HDD_INFO                    *LocalHddInfo;
1082   BBS_TABLE                   *LocalBbsTable;
1083   UINTN                       Index;
1084   UINTN                       Index2;
1085   UINTN                       *Idx;
1086   UINTN                       FDCount;
1087   UINTN                       HDCount;
1088   UINTN                       CDCount;
1089   UINTN                       NETCount;
1090   UINTN                       BEVCount;
1091   UINTN                       TotalSize;
1092   UINTN                       HeaderSize;
1093   UINT16                      *NewFDPtr;
1094   UINT16                      *NewHDPtr;
1095   UINT16                      *NewCDPtr;
1096   UINT16                      *NewNETPtr;
1097   UINT16                      *NewBEVPtr;
1098   UINT16                      *NewDevPtr;
1099   UINTN                       FDIndex;
1100   UINTN                       HDIndex;
1101   UINTN                       CDIndex;
1102   UINTN                       NETIndex;
1103   UINTN                       BEVIndex;
1104 
1105   Idx           = NULL;
1106   FDCount       = 0;
1107   HDCount       = 0;
1108   CDCount       = 0;
1109   NETCount      = 0;
1110   BEVCount      = 0;
1111   TotalSize     = 0;
1112   HeaderSize    = sizeof (BBS_TYPE) + sizeof (UINT16);
1113   FDIndex       = 0;
1114   HDIndex       = 0;
1115   CDIndex       = 0;
1116   NETIndex      = 0;
1117   BEVIndex      = 0;
1118   NewDevPtr     = NULL;
1119 
1120   Status        = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
1121   if (EFI_ERROR (Status)) {
1122     return Status;
1123   }
1124 
1125   Status = LegacyBios->GetBbsInfo (
1126                          LegacyBios,
1127                          &HddCount,
1128                          &LocalHddInfo,
1129                          &BbsCount,
1130                          &LocalBbsTable
1131                          );
1132   if (EFI_ERROR (Status)) {
1133     return Status;
1134   }
1135 
1136   DevOrder = BdsLibGetVariableAndSize (
1137                VAR_LEGACY_DEV_ORDER,
1138                &gEfiLegacyDevOrderVariableGuid,
1139                &DevOrderSize
1140                );
1141   if (NULL == DevOrder) {
1142     return BdsCreateDevOrder (LocalBbsTable, BbsCount);
1143   }
1144   //
1145   // First we figure out how many boot devices with same device type respectively
1146   //
1147   for (Index = 0; Index < BbsCount; Index++) {
1148     if ((LocalBbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) ||
1149         (LocalBbsTable[Index].BootPriority == BBS_DO_NOT_BOOT_FROM)
1150         ) {
1151       continue;
1152     }
1153 
1154     switch (LocalBbsTable[Index].DeviceType) {
1155     case BBS_FLOPPY:
1156       FDCount++;
1157       break;
1158 
1159     case BBS_HARDDISK:
1160       HDCount++;
1161       break;
1162 
1163     case BBS_CDROM:
1164       CDCount++;
1165       break;
1166 
1167     case BBS_EMBED_NETWORK:
1168       NETCount++;
1169       break;
1170 
1171     case BBS_BEV_DEVICE:
1172       BEVCount++;
1173       break;
1174 
1175     default:
1176       break;
1177     }
1178   }
1179 
1180   TotalSize += (HeaderSize + FDCount * sizeof (UINT16));
1181   TotalSize += (HeaderSize + HDCount * sizeof (UINT16));
1182   TotalSize += (HeaderSize + CDCount * sizeof (UINT16));
1183   TotalSize += (HeaderSize + NETCount * sizeof (UINT16));
1184   TotalSize += (HeaderSize + BEVCount * sizeof (UINT16));
1185 
1186   NewDevOrder = AllocateZeroPool (TotalSize);
1187   if (NULL == NewDevOrder) {
1188     return EFI_OUT_OF_RESOURCES;
1189   }
1190 
1191 
1192 
1193   //
1194   // copy FD
1195   //
1196   Ptr             = DevOrder;
1197   NewPtr          = NewDevOrder;
1198   NewPtr->BbsType = Ptr->BbsType;
1199   NewPtr->Length  = (UINT16) (sizeof (UINT16) + FDCount * sizeof (UINT16));
1200   for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
1201     if (LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_IGNORE_ENTRY ||
1202         LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_DO_NOT_BOOT_FROM ||
1203         LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_FLOPPY
1204         ) {
1205       continue;
1206     }
1207 
1208     NewPtr->Data[FDIndex] = Ptr->Data[Index];
1209     FDIndex++;
1210   }
1211   NewFDPtr = NewPtr->Data;
1212 
1213   //
1214   // copy HD
1215   //
1216   Ptr             = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
1217   NewPtr          = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
1218   NewPtr->BbsType = Ptr->BbsType;
1219   NewPtr->Length  = (UINT16) (sizeof (UINT16) + HDCount * sizeof (UINT16));
1220   for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
1221     if (LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_IGNORE_ENTRY ||
1222         LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_DO_NOT_BOOT_FROM ||
1223         LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_LOWEST_PRIORITY ||
1224         LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_HARDDISK
1225         ) {
1226       continue;
1227     }
1228 
1229     NewPtr->Data[HDIndex] = Ptr->Data[Index];
1230     HDIndex++;
1231   }
1232   NewHDPtr = NewPtr->Data;
1233 
1234   //
1235   // copy CD
1236   //
1237   Ptr    = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
1238   NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
1239   NewPtr->BbsType = Ptr->BbsType;
1240   NewPtr->Length  = (UINT16) (sizeof (UINT16) + CDCount * sizeof (UINT16));
1241   for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
1242     if (LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_IGNORE_ENTRY ||
1243         LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_DO_NOT_BOOT_FROM ||
1244         LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_LOWEST_PRIORITY ||
1245         LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_CDROM
1246         ) {
1247       continue;
1248     }
1249 
1250     NewPtr->Data[CDIndex] = Ptr->Data[Index];
1251     CDIndex++;
1252   }
1253   NewCDPtr = NewPtr->Data;
1254 
1255   //
1256   // copy NET
1257   //
1258   Ptr    = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
1259   NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
1260   NewPtr->BbsType = Ptr->BbsType;
1261   NewPtr->Length  = (UINT16) (sizeof (UINT16) + NETCount * sizeof (UINT16));
1262   for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
1263     if (LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_IGNORE_ENTRY ||
1264         LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_DO_NOT_BOOT_FROM ||
1265         LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_LOWEST_PRIORITY ||
1266         LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_EMBED_NETWORK
1267         ) {
1268       continue;
1269     }
1270 
1271     NewPtr->Data[NETIndex] = Ptr->Data[Index];
1272     NETIndex++;
1273   }
1274   NewNETPtr = NewPtr->Data;
1275 
1276   //
1277   // copy BEV
1278   //
1279   Ptr    = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
1280   NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
1281   NewPtr->BbsType = Ptr->BbsType;
1282   NewPtr->Length  = (UINT16) (sizeof (UINT16) + BEVCount * sizeof (UINT16));
1283   for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
1284     if (LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_IGNORE_ENTRY ||
1285         LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_DO_NOT_BOOT_FROM ||
1286         LocalBbsTable[Ptr->Data[Index] & 0xFF].BootPriority == BBS_LOWEST_PRIORITY ||
1287         LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_BEV_DEVICE
1288         ) {
1289       continue;
1290     }
1291 
1292     NewPtr->Data[BEVIndex] = Ptr->Data[Index];
1293     BEVIndex++;
1294   }
1295   NewBEVPtr = NewPtr->Data;
1296 
1297   for (Index = 0; Index < BbsCount; Index++) {
1298     if ((LocalBbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) ||
1299         (LocalBbsTable[Index].BootPriority == BBS_DO_NOT_BOOT_FROM)
1300         ) {
1301       continue;
1302     }
1303 
1304     switch (LocalBbsTable[Index].DeviceType) {
1305     case BBS_FLOPPY:
1306       Idx       = &FDIndex;
1307       NewDevPtr = NewFDPtr;
1308       break;
1309 
1310     case BBS_HARDDISK:
1311       Idx       = &HDIndex;
1312       NewDevPtr = NewHDPtr;
1313       break;
1314 
1315     case BBS_CDROM:
1316       Idx       = &CDIndex;
1317       NewDevPtr = NewCDPtr;
1318       break;
1319 
1320     case BBS_EMBED_NETWORK:
1321       Idx       = &NETIndex;
1322       NewDevPtr = NewNETPtr;
1323       break;
1324 
1325     case BBS_BEV_DEVICE:
1326       Idx       = &BEVIndex;
1327       NewDevPtr = NewBEVPtr;
1328       break;
1329 
1330     default:
1331       Idx = NULL;
1332       break;
1333     }
1334     //
1335     // at this point we have copied those valid indexes to new buffer
1336     // and we should check if there is any new appeared boot device
1337     //
1338     if (Idx != NULL) {
1339       for (Index2 = 0; Index2 < *Idx; Index2++) {
1340         if ((NewDevPtr[Index2] & 0xFF) == (UINT16) Index) {
1341           break;
1342         }
1343       }
1344 
1345       if (Index2 == *Idx) {
1346         //
1347         // Index2 == *Idx means we didn't find Index
1348         // so Index is a new appeared device's index in BBS table
1349         // insert it before disabled indexes.
1350         //
1351         for (Index2 = 0; Index2 < *Idx; Index2++) {
1352           if ((NewDevPtr[Index2] & 0xFF00) == 0xFF00) {
1353             break;
1354           }
1355         }
1356         CopyMem (&NewDevPtr[Index2 + 1], &NewDevPtr[Index2], (*Idx - Index2) * sizeof (UINT16));
1357         NewDevPtr[Index2] = (UINT16) (Index & 0xFF);
1358         (*Idx)++;
1359       }
1360     }
1361   }
1362 
1363   FreePool (DevOrder);
1364 
1365   Status = gRT->SetVariable (
1366                   VAR_LEGACY_DEV_ORDER,
1367                   &gEfiLegacyDevOrderVariableGuid,
1368                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
1369                   TotalSize,
1370                   NewDevOrder
1371                   );
1372   FreePool (NewDevOrder);
1373 
1374   return Status;
1375 }
1376 
1377 /**
1378   Set Boot Priority for specified device type.
1379 
1380   @param DeviceType      The device type.
1381   @param BbsIndex        The BBS index to set the highest priority. Ignore when -1.
1382   @param LocalBbsTable   The BBS table.
1383   @param Priority        The prority table.
1384 
1385   @retval EFI_SUCCESS           The function completes successfully.
1386   @retval EFI_NOT_FOUND         Failed to find device.
1387   @retval EFI_OUT_OF_RESOURCES  Failed to get the efi variable of device order.
1388 
1389 **/
1390 EFI_STATUS
BdsSetBootPriority4SameTypeDev(IN UINT16 DeviceType,IN UINTN BbsIndex,IN OUT BBS_TABLE * LocalBbsTable,IN OUT UINT16 * Priority)1391 BdsSetBootPriority4SameTypeDev (
1392   IN UINT16                                              DeviceType,
1393   IN UINTN                                               BbsIndex,
1394   IN OUT BBS_TABLE                                       *LocalBbsTable,
1395   IN OUT UINT16                                          *Priority
1396   )
1397 {
1398   LEGACY_DEV_ORDER_ENTRY      *DevOrder;
1399   LEGACY_DEV_ORDER_ENTRY      *DevOrderPtr;
1400   UINTN                       DevOrderSize;
1401   UINTN                       Index;
1402 
1403   DevOrder = BdsLibGetVariableAndSize (
1404                VAR_LEGACY_DEV_ORDER,
1405                &gEfiLegacyDevOrderVariableGuid,
1406                &DevOrderSize
1407                );
1408   if (NULL == DevOrder) {
1409     return EFI_OUT_OF_RESOURCES;
1410   }
1411 
1412   DevOrderPtr = DevOrder;
1413   while ((UINT8 *) DevOrderPtr < (UINT8 *) DevOrder + DevOrderSize) {
1414     if (DevOrderPtr->BbsType == DeviceType) {
1415       break;
1416     }
1417 
1418     DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) ((UINTN) DevOrderPtr + sizeof (BBS_TYPE) + DevOrderPtr->Length);
1419   }
1420 
1421   if ((UINT8 *) DevOrderPtr >= (UINT8 *) DevOrder + DevOrderSize) {
1422     FreePool (DevOrder);
1423     return EFI_NOT_FOUND;
1424   }
1425 
1426   if (BbsIndex != (UINTN) -1) {
1427     LocalBbsTable[BbsIndex].BootPriority = *Priority;
1428     (*Priority)++;
1429   }
1430   //
1431   // If the high byte of the DevIndex is 0xFF, it indicates that this device has been disabled.
1432   //
1433   for (Index = 0; Index < DevOrderPtr->Length / sizeof (UINT16) - 1; Index++) {
1434     if ((DevOrderPtr->Data[Index] & 0xFF00) == 0xFF00) {
1435       //
1436       // LocalBbsTable[DevIndex[Index] & 0xFF].BootPriority = BBS_DISABLED_ENTRY;
1437       //
1438     } else if (DevOrderPtr->Data[Index] != BbsIndex) {
1439       LocalBbsTable[DevOrderPtr->Data[Index]].BootPriority = *Priority;
1440       (*Priority)++;
1441     }
1442   }
1443 
1444   FreePool (DevOrder);
1445   return EFI_SUCCESS;
1446 }
1447 
1448 /**
1449   Print the BBS Table.
1450 
1451   @param LocalBbsTable   The BBS table.
1452   @param BbsCount        The count of entry in BBS table.
1453 **/
1454 VOID
PrintBbsTable(IN BBS_TABLE * LocalBbsTable,IN UINT16 BbsCount)1455 PrintBbsTable (
1456   IN BBS_TABLE  *LocalBbsTable,
1457   IN UINT16     BbsCount
1458   )
1459 {
1460   UINT16  Idx;
1461 
1462   DEBUG ((DEBUG_ERROR, "\n"));
1463   DEBUG ((DEBUG_ERROR, " NO  Prio bb/dd/ff cl/sc Type Stat segm:offs\n"));
1464   DEBUG ((DEBUG_ERROR, "=============================================\n"));
1465   for (Idx = 0; Idx < BbsCount; Idx++) {
1466     if ((LocalBbsTable[Idx].BootPriority == BBS_IGNORE_ENTRY) ||
1467         (LocalBbsTable[Idx].BootPriority == BBS_DO_NOT_BOOT_FROM) ||
1468         (LocalBbsTable[Idx].BootPriority == BBS_LOWEST_PRIORITY)
1469         ) {
1470       continue;
1471     }
1472 
1473     DEBUG (
1474       (DEBUG_ERROR,
1475       " %02x: %04x %02x/%02x/%02x %02x/%02x %04x %04x %04x:%04x\n",
1476       (UINTN) Idx,
1477       (UINTN) LocalBbsTable[Idx].BootPriority,
1478       (UINTN) LocalBbsTable[Idx].Bus,
1479       (UINTN) LocalBbsTable[Idx].Device,
1480       (UINTN) LocalBbsTable[Idx].Function,
1481       (UINTN) LocalBbsTable[Idx].Class,
1482       (UINTN) LocalBbsTable[Idx].SubClass,
1483       (UINTN) LocalBbsTable[Idx].DeviceType,
1484       (UINTN) * (UINT16 *) &LocalBbsTable[Idx].StatusFlags,
1485       (UINTN) LocalBbsTable[Idx].BootHandlerSegment,
1486       (UINTN) LocalBbsTable[Idx].BootHandlerOffset,
1487       (UINTN) ((LocalBbsTable[Idx].MfgStringSegment << 4) + LocalBbsTable[Idx].MfgStringOffset),
1488       (UINTN) ((LocalBbsTable[Idx].DescStringSegment << 4) + LocalBbsTable[Idx].DescStringOffset))
1489       );
1490   }
1491 
1492   DEBUG ((DEBUG_ERROR, "\n"));
1493 }
1494 
1495 /**
1496   Set the boot priority for BBS entries based on boot option entry and boot order.
1497 
1498   @param  Entry             The boot option is to be checked for refresh BBS table.
1499 
1500   @retval EFI_SUCCESS           The boot priority for BBS entries is refreshed successfully.
1501   @retval EFI_NOT_FOUND         BBS entries can't be found.
1502   @retval EFI_OUT_OF_RESOURCES  Failed to get the legacy device boot order.
1503 **/
1504 EFI_STATUS
1505 EFIAPI
BdsRefreshBbsTableForBoot(IN BDS_COMMON_OPTION * Entry)1506 BdsRefreshBbsTableForBoot (
1507   IN BDS_COMMON_OPTION        *Entry
1508   )
1509 {
1510   EFI_STATUS                Status;
1511   UINT16                    BbsIndex;
1512   UINT16                    HddCount;
1513   UINT16                    BbsCount;
1514   HDD_INFO                  *LocalHddInfo;
1515   BBS_TABLE                 *LocalBbsTable;
1516   UINT16                    DevType;
1517   EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
1518   UINTN                     Index;
1519   UINT16                    Priority;
1520   UINT16                    *BootOrder;
1521   UINTN                     BootOrderSize;
1522   UINT8                     *BootOptionVar;
1523   UINTN                     BootOptionSize;
1524   CHAR16                    BootOption[9];
1525   UINT8                     *Ptr;
1526   UINT16                    DevPathLen;
1527   EFI_DEVICE_PATH_PROTOCOL  *DevPath;
1528   UINT16                    *DeviceType;
1529   UINTN                     DeviceTypeCount;
1530   UINTN                     DeviceTypeIndex;
1531 
1532   HddCount      = 0;
1533   BbsCount      = 0;
1534   LocalHddInfo  = NULL;
1535   LocalBbsTable = NULL;
1536   DevType       = BBS_UNKNOWN;
1537 
1538   Status        = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
1539   if (EFI_ERROR (Status)) {
1540     return Status;
1541   }
1542 
1543   LegacyBios->GetBbsInfo (
1544                 LegacyBios,
1545                 &HddCount,
1546                 &LocalHddInfo,
1547                 &BbsCount,
1548                 &LocalBbsTable
1549                 );
1550   //
1551   // First, set all the present devices' boot priority to BBS_UNPRIORITIZED_ENTRY
1552   // We will set them according to the settings setup by user
1553   //
1554   for (Index = 0; Index < BbsCount; Index++) {
1555     if (!((BBS_IGNORE_ENTRY == LocalBbsTable[Index].BootPriority) ||
1556         (BBS_DO_NOT_BOOT_FROM == LocalBbsTable[Index].BootPriority) ||
1557          (BBS_LOWEST_PRIORITY == LocalBbsTable[Index].BootPriority))) {
1558       LocalBbsTable[Index].BootPriority = BBS_UNPRIORITIZED_ENTRY;
1559     }
1560   }
1561   //
1562   // boot priority always starts at 0
1563   //
1564   Priority = 0;
1565   if (Entry->LoadOptionsSize == sizeof (BBS_TABLE) + sizeof (UINT16)) {
1566     //
1567     // If Entry stands for a legacy boot option, we prioritize the devices with the same type first.
1568     //
1569     DevType  = ((BBS_TABLE *) Entry->LoadOptions)->DeviceType;
1570     BbsIndex = *(UINT16 *) ((BBS_TABLE *) Entry->LoadOptions + 1);
1571     Status = BdsSetBootPriority4SameTypeDev (
1572               DevType,
1573               BbsIndex,
1574               LocalBbsTable,
1575               &Priority
1576               );
1577     if (EFI_ERROR (Status)) {
1578       return Status;
1579     }
1580   }
1581   //
1582   // we have to set the boot priority for other BBS entries with different device types
1583   //
1584   BootOrder = BdsLibGetVariableAndSize (
1585                 L"BootOrder",
1586                 &gEfiGlobalVariableGuid,
1587                 &BootOrderSize
1588                 );
1589   DeviceType = AllocatePool (BootOrderSize + sizeof (UINT16));
1590   ASSERT (DeviceType != NULL);
1591 
1592   DeviceType[0]   = DevType;
1593   DeviceTypeCount = 1;
1594   for (Index = 0; ((BootOrder != NULL) && (Index < BootOrderSize / sizeof (UINT16))); Index++) {
1595     UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
1596     BootOptionVar = BdsLibGetVariableAndSize (
1597                       BootOption,
1598                       &gEfiGlobalVariableGuid,
1599                       &BootOptionSize
1600                       );
1601     if (NULL == BootOptionVar) {
1602       continue;
1603     }
1604 
1605     Ptr = BootOptionVar;
1606 
1607     Ptr += sizeof (UINT32);
1608     DevPathLen = *(UINT16 *) Ptr;
1609     Ptr += sizeof (UINT16);
1610     Ptr += StrSize ((UINT16 *) Ptr);
1611     DevPath = (EFI_DEVICE_PATH_PROTOCOL *) Ptr;
1612     if (BBS_DEVICE_PATH != DevPath->Type || BBS_BBS_DP != DevPath->SubType) {
1613       FreePool (BootOptionVar);
1614       continue;
1615     }
1616 
1617     Ptr += DevPathLen;
1618     DevType = ((BBS_TABLE *) Ptr)->DeviceType;
1619     for (DeviceTypeIndex = 0; DeviceTypeIndex < DeviceTypeCount; DeviceTypeIndex++) {
1620       if (DeviceType[DeviceTypeIndex] == DevType) {
1621         break;
1622       }
1623     }
1624     if (DeviceTypeIndex < DeviceTypeCount) {
1625       //
1626       // We don't want to process twice for a device type
1627       //
1628       FreePool (BootOptionVar);
1629       continue;
1630     }
1631 
1632     DeviceType[DeviceTypeCount] = DevType;
1633     DeviceTypeCount++;
1634 
1635     Status = BdsSetBootPriority4SameTypeDev (
1636               DevType,
1637               (UINTN) -1,
1638               LocalBbsTable,
1639               &Priority
1640               );
1641     FreePool (BootOptionVar);
1642     if (EFI_ERROR (Status)) {
1643       break;
1644     }
1645   }
1646 
1647   FreePool (DeviceType);
1648 
1649   if (BootOrder != NULL) {
1650     FreePool (BootOrder);
1651   }
1652 
1653   DEBUG_CODE_BEGIN();
1654     PrintBbsTable (LocalBbsTable, BbsCount);
1655   DEBUG_CODE_END();
1656 
1657   return Status;
1658 }
1659 
1660 /**
1661   Boot the legacy system with the boot option
1662 
1663   @param  Option                 The legacy boot option which have BBS device path
1664 
1665   @retval EFI_UNSUPPORTED        There is no legacybios protocol, do not support
1666                                  legacy boot.
1667   @retval EFI_STATUS             Return the status of LegacyBios->LegacyBoot ().
1668 
1669 **/
1670 EFI_STATUS
BdsLibDoLegacyBoot(IN BDS_COMMON_OPTION * Option)1671 BdsLibDoLegacyBoot (
1672   IN  BDS_COMMON_OPTION           *Option
1673   )
1674 {
1675   EFI_STATUS                Status;
1676   EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
1677   EFI_EVENT                 LegacyBootEvent;
1678 
1679   Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
1680   if (EFI_ERROR (Status)) {
1681     //
1682     // If no LegacyBios protocol we do not support legacy boot
1683     //
1684     return EFI_UNSUPPORTED;
1685   }
1686   //
1687   // Notes: if we separate the int 19, then we don't need to refresh BBS
1688   //
1689   BdsRefreshBbsTableForBoot (Option);
1690 
1691   //
1692   // Write boot to OS performance data for legacy boot.
1693   //
1694   PERF_CODE (
1695     //
1696     // Create an event to be signalled when Legacy Boot occurs to write performance data.
1697     //
1698     Status = EfiCreateEventLegacyBootEx(
1699                TPL_NOTIFY,
1700                WriteBootToOsPerformanceData,
1701                NULL,
1702                &LegacyBootEvent
1703                );
1704     ASSERT_EFI_ERROR (Status);
1705   );
1706 
1707   DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Legacy Boot: %S\n", Option->Description));
1708   return LegacyBios->LegacyBoot (
1709                       LegacyBios,
1710                       (BBS_BBS_DEVICE_PATH *) Option->DevicePath,
1711                       Option->LoadOptionsSize,
1712                       Option->LoadOptions
1713                       );
1714 }
1715 
1716 /**
1717   Internal function to check if the input boot option is a valid EFI NV Boot####.
1718 
1719   @param OptionToCheck  Boot option to be checked.
1720 
1721   @retval TRUE      This boot option matches a valid EFI NV Boot####.
1722   @retval FALSE     If not.
1723 
1724 **/
1725 BOOLEAN
IsBootOptionValidNVVarialbe(IN BDS_COMMON_OPTION * OptionToCheck)1726 IsBootOptionValidNVVarialbe (
1727   IN  BDS_COMMON_OPTION             *OptionToCheck
1728   )
1729 {
1730   LIST_ENTRY        TempList;
1731   BDS_COMMON_OPTION *BootOption;
1732   BOOLEAN           Valid;
1733   CHAR16            OptionName[20];
1734 
1735   Valid = FALSE;
1736 
1737   InitializeListHead (&TempList);
1738   UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", OptionToCheck->BootCurrent);
1739 
1740   BootOption = BdsLibVariableToOption (&TempList, OptionName);
1741   if (BootOption == NULL) {
1742     return FALSE;
1743   }
1744 
1745   //
1746   // If the Boot Option Number and Device Path matches, OptionToCheck matches a
1747   // valid EFI NV Boot####.
1748   //
1749   if ((OptionToCheck->BootCurrent == BootOption->BootCurrent) &&
1750       (CompareMem (OptionToCheck->DevicePath, BootOption->DevicePath, GetDevicePathSize (OptionToCheck->DevicePath)) == 0))
1751       {
1752     Valid = TRUE;
1753   }
1754 
1755   FreePool (BootOption);
1756 
1757   return Valid;
1758 }
1759 
1760 /**
1761   Check whether a USB device match the specified USB Class device path. This
1762   function follows "Load Option Processing" behavior in UEFI specification.
1763 
1764   @param UsbIo       USB I/O protocol associated with the USB device.
1765   @param UsbClass    The USB Class device path to match.
1766 
1767   @retval TRUE       The USB device match the USB Class device path.
1768   @retval FALSE      The USB device does not match the USB Class device path.
1769 
1770 **/
1771 BOOLEAN
BdsMatchUsbClass(IN EFI_USB_IO_PROTOCOL * UsbIo,IN USB_CLASS_DEVICE_PATH * UsbClass)1772 BdsMatchUsbClass (
1773   IN EFI_USB_IO_PROTOCOL        *UsbIo,
1774   IN USB_CLASS_DEVICE_PATH      *UsbClass
1775   )
1776 {
1777   EFI_STATUS                    Status;
1778   EFI_USB_DEVICE_DESCRIPTOR     DevDesc;
1779   EFI_USB_INTERFACE_DESCRIPTOR  IfDesc;
1780   UINT8                         DeviceClass;
1781   UINT8                         DeviceSubClass;
1782   UINT8                         DeviceProtocol;
1783 
1784   if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) ||
1785       (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)){
1786     return FALSE;
1787   }
1788 
1789   //
1790   // Check Vendor Id and Product Id.
1791   //
1792   Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
1793   if (EFI_ERROR (Status)) {
1794     return FALSE;
1795   }
1796 
1797   if ((UsbClass->VendorId != 0xffff) &&
1798       (UsbClass->VendorId != DevDesc.IdVendor)) {
1799     return FALSE;
1800   }
1801 
1802   if ((UsbClass->ProductId != 0xffff) &&
1803       (UsbClass->ProductId != DevDesc.IdProduct)) {
1804     return FALSE;
1805   }
1806 
1807   DeviceClass    = DevDesc.DeviceClass;
1808   DeviceSubClass = DevDesc.DeviceSubClass;
1809   DeviceProtocol = DevDesc.DeviceProtocol;
1810   if (DeviceClass == 0) {
1811     //
1812     // If Class in Device Descriptor is set to 0, use the Class, SubClass and
1813     // Protocol in Interface Descriptor instead.
1814     //
1815     Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);
1816     if (EFI_ERROR (Status)) {
1817       return FALSE;
1818     }
1819 
1820     DeviceClass    = IfDesc.InterfaceClass;
1821     DeviceSubClass = IfDesc.InterfaceSubClass;
1822     DeviceProtocol = IfDesc.InterfaceProtocol;
1823   }
1824 
1825   //
1826   // Check Class, SubClass and Protocol.
1827   //
1828   if ((UsbClass->DeviceClass != 0xff) &&
1829       (UsbClass->DeviceClass != DeviceClass)) {
1830     return FALSE;
1831   }
1832 
1833   if ((UsbClass->DeviceSubClass != 0xff) &&
1834       (UsbClass->DeviceSubClass != DeviceSubClass)) {
1835     return FALSE;
1836   }
1837 
1838   if ((UsbClass->DeviceProtocol != 0xff) &&
1839       (UsbClass->DeviceProtocol != DeviceProtocol)) {
1840     return FALSE;
1841   }
1842 
1843   return TRUE;
1844 }
1845 
1846 /**
1847   Check whether a USB device match the specified USB WWID device path. This
1848   function follows "Load Option Processing" behavior in UEFI specification.
1849 
1850   @param UsbIo       USB I/O protocol associated with the USB device.
1851   @param UsbWwid     The USB WWID device path to match.
1852 
1853   @retval TRUE       The USB device match the USB WWID device path.
1854   @retval FALSE      The USB device does not match the USB WWID device path.
1855 
1856 **/
1857 BOOLEAN
BdsMatchUsbWwid(IN EFI_USB_IO_PROTOCOL * UsbIo,IN USB_WWID_DEVICE_PATH * UsbWwid)1858 BdsMatchUsbWwid (
1859   IN EFI_USB_IO_PROTOCOL        *UsbIo,
1860   IN USB_WWID_DEVICE_PATH       *UsbWwid
1861   )
1862 {
1863   EFI_STATUS                   Status;
1864   EFI_USB_DEVICE_DESCRIPTOR    DevDesc;
1865   EFI_USB_INTERFACE_DESCRIPTOR IfDesc;
1866   UINT16                       *LangIdTable;
1867   UINT16                       TableSize;
1868   UINT16                       Index;
1869   CHAR16                       *CompareStr;
1870   UINTN                        CompareLen;
1871   CHAR16                       *SerialNumberStr;
1872   UINTN                        Length;
1873 
1874   if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) ||
1875       (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP )){
1876     return FALSE;
1877   }
1878 
1879   //
1880   // Check Vendor Id and Product Id.
1881   //
1882   Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
1883   if (EFI_ERROR (Status)) {
1884     return FALSE;
1885   }
1886   if ((DevDesc.IdVendor != UsbWwid->VendorId) ||
1887       (DevDesc.IdProduct != UsbWwid->ProductId)) {
1888     return FALSE;
1889   }
1890 
1891   //
1892   // Check Interface Number.
1893   //
1894   Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);
1895   if (EFI_ERROR (Status)) {
1896     return FALSE;
1897   }
1898   if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) {
1899     return FALSE;
1900   }
1901 
1902   //
1903   // Check Serial Number.
1904   //
1905   if (DevDesc.StrSerialNumber == 0) {
1906     return FALSE;
1907   }
1908 
1909   //
1910   // Get all supported languages.
1911   //
1912   TableSize = 0;
1913   LangIdTable = NULL;
1914   Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize);
1915   if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) {
1916     return FALSE;
1917   }
1918 
1919   //
1920   // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters.
1921   //
1922   CompareStr = (CHAR16 *) (UINTN) (UsbWwid + 1);
1923   CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16);
1924   if (CompareStr[CompareLen - 1] == L'\0') {
1925     CompareLen--;
1926   }
1927 
1928   //
1929   // Compare serial number in each supported language.
1930   //
1931   for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) {
1932     SerialNumberStr = NULL;
1933     Status = UsbIo->UsbGetStringDescriptor (
1934                       UsbIo,
1935                       LangIdTable[Index],
1936                       DevDesc.StrSerialNumber,
1937                       &SerialNumberStr
1938                       );
1939     if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) {
1940       continue;
1941     }
1942 
1943     Length = StrLen (SerialNumberStr);
1944     if ((Length >= CompareLen) &&
1945         (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) {
1946       FreePool (SerialNumberStr);
1947       return TRUE;
1948     }
1949 
1950     FreePool (SerialNumberStr);
1951   }
1952 
1953   return FALSE;
1954 }
1955 
1956 /**
1957   Find a USB device path which match the specified short-form device path start
1958   with USB Class or USB WWID device path and load the boot file then return the
1959   image handle. If ParentDevicePath is NULL, this function will search in all USB
1960   devices of the platform. If ParentDevicePath is not NULL,this function will only
1961   search in its child devices.
1962 
1963   @param ParentDevicePath      The device path of the parent.
1964   @param ShortFormDevicePath   The USB Class or USB WWID device path to match.
1965 
1966   @return  The image Handle if find load file from specified short-form device path
1967            or NULL if not found.
1968 
1969 **/
1970 EFI_HANDLE *
BdsFindUsbDevice(IN EFI_DEVICE_PATH_PROTOCOL * ParentDevicePath,IN EFI_DEVICE_PATH_PROTOCOL * ShortFormDevicePath)1971 BdsFindUsbDevice (
1972   IN EFI_DEVICE_PATH_PROTOCOL   *ParentDevicePath,
1973   IN EFI_DEVICE_PATH_PROTOCOL   *ShortFormDevicePath
1974   )
1975 {
1976   EFI_STATUS                Status;
1977   UINTN                     UsbIoHandleCount;
1978   EFI_HANDLE                *UsbIoHandleBuffer;
1979   EFI_DEVICE_PATH_PROTOCOL  *UsbIoDevicePath;
1980   EFI_USB_IO_PROTOCOL       *UsbIo;
1981   UINTN                     Index;
1982   UINTN                     ParentSize;
1983   UINTN                     Size;
1984   EFI_HANDLE                ImageHandle;
1985   EFI_HANDLE                Handle;
1986   EFI_DEVICE_PATH_PROTOCOL  *FullDevicePath;
1987   EFI_DEVICE_PATH_PROTOCOL  *NextDevicePath;
1988 
1989   FullDevicePath = NULL;
1990   ImageHandle    = NULL;
1991 
1992   //
1993   // Get all UsbIo Handles.
1994   //
1995   UsbIoHandleCount = 0;
1996   UsbIoHandleBuffer = NULL;
1997   Status = gBS->LocateHandleBuffer (
1998                   ByProtocol,
1999                   &gEfiUsbIoProtocolGuid,
2000                   NULL,
2001                   &UsbIoHandleCount,
2002                   &UsbIoHandleBuffer
2003                   );
2004   if (EFI_ERROR (Status) || (UsbIoHandleCount == 0) || (UsbIoHandleBuffer == NULL)) {
2005     return NULL;
2006   }
2007 
2008   ParentSize = (ParentDevicePath == NULL) ? 0 : GetDevicePathSize (ParentDevicePath);
2009   for (Index = 0; Index < UsbIoHandleCount; Index++) {
2010     //
2011     // Get the Usb IO interface.
2012     //
2013     Status = gBS->HandleProtocol(
2014                     UsbIoHandleBuffer[Index],
2015                     &gEfiUsbIoProtocolGuid,
2016                     (VOID **) &UsbIo
2017                     );
2018     if (EFI_ERROR (Status)) {
2019       continue;
2020     }
2021 
2022     UsbIoDevicePath = DevicePathFromHandle (UsbIoHandleBuffer[Index]);
2023     if (UsbIoDevicePath == NULL) {
2024       continue;
2025     }
2026 
2027     if (ParentDevicePath != NULL) {
2028       //
2029       // Compare starting part of UsbIoHandle's device path with ParentDevicePath.
2030       //
2031       Size = GetDevicePathSize (UsbIoDevicePath);
2032       if ((Size < ParentSize) ||
2033           (CompareMem (UsbIoDevicePath, ParentDevicePath, ParentSize - END_DEVICE_PATH_LENGTH) != 0)) {
2034         continue;
2035       }
2036     }
2037 
2038     if (BdsMatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *) ShortFormDevicePath) ||
2039         BdsMatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *) ShortFormDevicePath)) {
2040       //
2041       // Try to find if there is the boot file in this DevicePath
2042       //
2043       NextDevicePath = NextDevicePathNode (ShortFormDevicePath);
2044       if (!IsDevicePathEnd (NextDevicePath)) {
2045         FullDevicePath = AppendDevicePath (UsbIoDevicePath, NextDevicePath);
2046         //
2047         // Connect the full device path, so that Simple File System protocol
2048         // could be installed for this USB device.
2049         //
2050         BdsLibConnectDevicePath (FullDevicePath);
2051         REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));
2052         Status = gBS->LoadImage (
2053                        TRUE,
2054                        gImageHandle,
2055                        FullDevicePath,
2056                        NULL,
2057                        0,
2058                        &ImageHandle
2059                        );
2060         FreePool (FullDevicePath);
2061       } else {
2062         FullDevicePath = UsbIoDevicePath;
2063         Status = EFI_NOT_FOUND;
2064       }
2065 
2066       //
2067       // If we didn't find an image directly, we need to try as if it is a removable device boot option
2068       // and load the image according to the default boot behavior for removable device.
2069       //
2070       if (EFI_ERROR (Status)) {
2071         //
2072         // check if there is a bootable removable media could be found in this device path ,
2073         // and get the bootable media handle
2074         //
2075         Handle = BdsLibGetBootableHandle(UsbIoDevicePath);
2076         if (Handle == NULL) {
2077           continue;
2078         }
2079         //
2080         // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media
2081         //  machinename is ia32, ia64, x64, ...
2082         //
2083         FullDevicePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
2084         if (FullDevicePath != NULL) {
2085           REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));
2086           Status = gBS->LoadImage (
2087                           TRUE,
2088                           gImageHandle,
2089                           FullDevicePath,
2090                           NULL,
2091                           0,
2092                           &ImageHandle
2093                           );
2094           if (EFI_ERROR (Status)) {
2095             //
2096             // The DevicePath failed, and it's not a valid
2097             // removable media device.
2098             //
2099             continue;
2100           }
2101         } else {
2102           continue;
2103         }
2104       }
2105       break;
2106     }
2107   }
2108 
2109   FreePool (UsbIoHandleBuffer);
2110   return ImageHandle;
2111 }
2112 
2113 /**
2114   Expand USB Class or USB WWID device path node to be full device path of a USB
2115   device in platform then load the boot file on this full device path and return the
2116   image handle.
2117 
2118   This function support following 4 cases:
2119   1) Boot Option device path starts with a USB Class or USB WWID device path,
2120      and there is no Media FilePath device path in the end.
2121      In this case, it will follow Removable Media Boot Behavior.
2122   2) Boot Option device path starts with a USB Class or USB WWID device path,
2123      and ended with Media FilePath device path.
2124   3) Boot Option device path starts with a full device path to a USB Host Controller,
2125      contains a USB Class or USB WWID device path node, while not ended with Media
2126      FilePath device path. In this case, it will follow Removable Media Boot Behavior.
2127   4) Boot Option device path starts with a full device path to a USB Host Controller,
2128      contains a USB Class or USB WWID device path node, and ended with Media
2129      FilePath device path.
2130 
2131   @param  DevicePath    The Boot Option device path.
2132 
2133   @return  The image handle of boot file, or NULL if there is no boot file found in
2134            the specified USB Class or USB WWID device path.
2135 
2136 **/
2137 EFI_HANDLE *
BdsExpandUsbShortFormDevicePath(IN EFI_DEVICE_PATH_PROTOCOL * DevicePath)2138 BdsExpandUsbShortFormDevicePath (
2139   IN EFI_DEVICE_PATH_PROTOCOL       *DevicePath
2140   )
2141 {
2142   EFI_HANDLE                *ImageHandle;
2143   EFI_DEVICE_PATH_PROTOCOL  *TempDevicePath;
2144   EFI_DEVICE_PATH_PROTOCOL  *ShortFormDevicePath;
2145 
2146   //
2147   // Search for USB Class or USB WWID device path node.
2148   //
2149   ShortFormDevicePath = NULL;
2150   ImageHandle         = NULL;
2151   TempDevicePath      = DevicePath;
2152   while (!IsDevicePathEnd (TempDevicePath)) {
2153     if ((DevicePathType (TempDevicePath) == MESSAGING_DEVICE_PATH) &&
2154         ((DevicePathSubType (TempDevicePath) == MSG_USB_CLASS_DP) ||
2155          (DevicePathSubType (TempDevicePath) == MSG_USB_WWID_DP))) {
2156       ShortFormDevicePath = TempDevicePath;
2157       break;
2158     }
2159     TempDevicePath = NextDevicePathNode (TempDevicePath);
2160   }
2161 
2162   if (ShortFormDevicePath == NULL) {
2163     //
2164     // No USB Class or USB WWID device path node found, do nothing.
2165     //
2166     return NULL;
2167   }
2168 
2169   if (ShortFormDevicePath == DevicePath) {
2170     //
2171     // Boot Option device path starts with USB Class or USB WWID device path.
2172     //
2173     ImageHandle = BdsFindUsbDevice (NULL, ShortFormDevicePath);
2174     if (ImageHandle == NULL) {
2175       //
2176       // Failed to find a match in existing devices, connect the short form USB
2177       // device path and try again.
2178       //
2179       BdsLibConnectUsbDevByShortFormDP (0xff, ShortFormDevicePath);
2180       ImageHandle = BdsFindUsbDevice (NULL, ShortFormDevicePath);
2181     }
2182   } else {
2183     //
2184     // Boot Option device path contains USB Class or USB WWID device path node.
2185     //
2186 
2187     //
2188     // Prepare the parent device path for search.
2189     //
2190     TempDevicePath = DuplicateDevicePath (DevicePath);
2191     ASSERT (TempDevicePath != NULL);
2192     SetDevicePathEndNode (((UINT8 *) TempDevicePath) + ((UINTN) ShortFormDevicePath - (UINTN) DevicePath));
2193 
2194     //
2195     // The USB Host Controller device path is already in Boot Option device path
2196     // and USB Bus driver already support RemainingDevicePath starts with USB
2197     // Class or USB WWID device path, so just search in existing USB devices and
2198     // doesn't perform ConnectController here.
2199     //
2200     ImageHandle = BdsFindUsbDevice (TempDevicePath, ShortFormDevicePath);
2201     FreePool (TempDevicePath);
2202   }
2203 
2204   return ImageHandle;
2205 }
2206 
2207 /**
2208   Process the boot option follow the UEFI specification and
2209   special treat the legacy boot option with BBS_DEVICE_PATH.
2210 
2211   @param  Option                 The boot option need to be processed
2212   @param  DevicePath             The device path which describe where to load the
2213                                  boot image or the legacy BBS device path to boot
2214                                  the legacy OS
2215   @param  ExitDataSize           The size of exit data.
2216   @param  ExitData               Data returned when Boot image failed.
2217 
2218   @retval EFI_SUCCESS            Boot from the input boot option successfully.
2219   @retval EFI_NOT_FOUND          If the Device Path is not found in the system
2220 
2221 **/
2222 EFI_STATUS
2223 EFIAPI
BdsLibBootViaBootOption(IN BDS_COMMON_OPTION * Option,IN EFI_DEVICE_PATH_PROTOCOL * DevicePath,OUT UINTN * ExitDataSize,OUT CHAR16 ** ExitData OPTIONAL)2224 BdsLibBootViaBootOption (
2225   IN  BDS_COMMON_OPTION             *Option,
2226   IN  EFI_DEVICE_PATH_PROTOCOL      *DevicePath,
2227   OUT UINTN                         *ExitDataSize,
2228   OUT CHAR16                        **ExitData OPTIONAL
2229   )
2230 {
2231   EFI_STATUS                Status;
2232   EFI_STATUS                StatusLogo;
2233   EFI_HANDLE                Handle;
2234   EFI_HANDLE                ImageHandle;
2235   EFI_DEVICE_PATH_PROTOCOL  *FilePath;
2236   EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
2237   EFI_DEVICE_PATH_PROTOCOL  *WorkingDevicePath;
2238   EFI_ACPI_S3_SAVE_PROTOCOL *AcpiS3Save;
2239   LIST_ENTRY                TempBootLists;
2240   EFI_BOOT_LOGO_PROTOCOL    *BootLogo;
2241 
2242   *ExitDataSize = 0;
2243   *ExitData     = NULL;
2244 
2245   //
2246   // Notes: this code can be remove after the s3 script table
2247   // hook on the event EVT_SIGNAL_READY_TO_BOOT or
2248   // EVT_SIGNAL_LEGACY_BOOT
2249   //
2250   Status = gBS->LocateProtocol (&gEfiAcpiS3SaveProtocolGuid, NULL, (VOID **) &AcpiS3Save);
2251   if (!EFI_ERROR (Status)) {
2252     AcpiS3Save->S3Save (AcpiS3Save, NULL);
2253   }
2254   //
2255   // If it's Device Path that starts with a hard drive path, append it with the front part to compose a
2256   // full device path
2257   //
2258   WorkingDevicePath = NULL;
2259   if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
2260       (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)) {
2261     WorkingDevicePath = BdsExpandPartitionPartialDevicePathToFull (
2262                           (HARDDRIVE_DEVICE_PATH *)DevicePath
2263                           );
2264     if (WorkingDevicePath != NULL) {
2265       DevicePath = WorkingDevicePath;
2266     }
2267   }
2268 
2269   //
2270   // Set Boot Current
2271   //
2272   if (IsBootOptionValidNVVarialbe (Option)) {
2273     //
2274     // For a temporary boot (i.e. a boot by selected a EFI Shell using "Boot From File"), Boot Current is actually not valid.
2275     // In this case, "BootCurrent" is not created.
2276     // Only create the BootCurrent variable when it points to a valid Boot#### variable.
2277     //
2278     SetVariableAndReportStatusCodeOnError (
2279           L"BootCurrent",
2280           &gEfiGlobalVariableGuid,
2281           EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
2282           sizeof (UINT16),
2283           &Option->BootCurrent
2284           );
2285   }
2286 
2287   //
2288   // Report Status Code to indicate ReadyToBoot event will be signalled
2289   //
2290   REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));
2291 
2292   //
2293   // Signal the EVT_SIGNAL_READY_TO_BOOT event
2294   //
2295   EfiSignalEventReadyToBoot();
2296 
2297   //
2298   // Expand USB Class or USB WWID device path node to be full device path of a USB
2299   // device in platform then load the boot file on this full device path and get the
2300   // image handle.
2301   //
2302   ImageHandle = BdsExpandUsbShortFormDevicePath (DevicePath);
2303 
2304   //
2305   // Adjust the different type memory page number just before booting
2306   // and save the updated info into the variable for next boot to use
2307   //
2308   BdsSetMemoryTypeInformationVariable ();
2309 
2310   //
2311   // By expanding the USB Class or WWID device path, the ImageHandle has returnned.
2312   // Here get the ImageHandle for the non USB class or WWID device path.
2313   //
2314   if (ImageHandle == NULL) {
2315     ASSERT (Option->DevicePath != NULL);
2316     if ((DevicePathType (Option->DevicePath) == BBS_DEVICE_PATH) &&
2317         (DevicePathSubType (Option->DevicePath) == BBS_BBS_DP)
2318        ) {
2319       //
2320       // Check to see if we should legacy BOOT. If yes then do the legacy boot
2321       //
2322       return BdsLibDoLegacyBoot (Option);
2323     }
2324 
2325     //
2326     // If the boot option point to Internal FV shell, make sure it is valid
2327     //
2328     Status = BdsLibUpdateFvFileDevicePath (&DevicePath, PcdGetPtr(PcdShellFile));
2329     if (!EFI_ERROR(Status)) {
2330       if (Option->DevicePath != NULL) {
2331         FreePool(Option->DevicePath);
2332       }
2333       Option->DevicePath  = AllocateZeroPool (GetDevicePathSize (DevicePath));
2334       ASSERT(Option->DevicePath != NULL);
2335       CopyMem (Option->DevicePath, DevicePath, GetDevicePathSize (DevicePath));
2336       //
2337       // Update the shell boot option
2338       //
2339       InitializeListHead (&TempBootLists);
2340       BdsLibRegisterNewOption (&TempBootLists, DevicePath, L"EFI Internal Shell", L"BootOrder");
2341 
2342       //
2343       // free the temporary device path created by BdsLibUpdateFvFileDevicePath()
2344       //
2345       FreePool (DevicePath);
2346       DevicePath = Option->DevicePath;
2347     }
2348 
2349     DEBUG_CODE_BEGIN();
2350 
2351     if (Option->Description == NULL) {
2352       DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Booting from unknown device path\n"));
2353     } else {
2354       DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Booting %S\n", Option->Description));
2355     }
2356 
2357     DEBUG_CODE_END();
2358 
2359     //
2360     // Report status code for OS Loader LoadImage.
2361     //
2362     REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));
2363     Status = gBS->LoadImage (
2364                     TRUE,
2365                     gImageHandle,
2366                     DevicePath,
2367                     NULL,
2368                     0,
2369                     &ImageHandle
2370                     );
2371 
2372     //
2373     // If we didn't find an image directly, we need to try as if it is a removable device boot option
2374     // and load the image according to the default boot behavior for removable device.
2375     //
2376     if (EFI_ERROR (Status)) {
2377       //
2378       // check if there is a bootable removable media could be found in this device path ,
2379       // and get the bootable media handle
2380       //
2381       Handle = BdsLibGetBootableHandle(DevicePath);
2382       if (Handle != NULL) {
2383         //
2384         // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media
2385         //  machinename is ia32, ia64, x64, ...
2386         //
2387         FilePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
2388         if (FilePath != NULL) {
2389           REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));
2390           Status = gBS->LoadImage (
2391                           TRUE,
2392                           gImageHandle,
2393                           FilePath,
2394                           NULL,
2395                           0,
2396                           &ImageHandle
2397                           );
2398         }
2399       }
2400     }
2401   }
2402   //
2403   // Provide the image with it's load options
2404   //
2405   if ((ImageHandle == NULL) || (EFI_ERROR(Status))) {
2406     //
2407     // Report Status Code to indicate that the failure to load boot option
2408     //
2409     REPORT_STATUS_CODE (
2410       EFI_ERROR_CODE | EFI_ERROR_MINOR,
2411       (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR)
2412       );
2413     goto Done;
2414   }
2415 
2416   Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);
2417   ASSERT_EFI_ERROR (Status);
2418 
2419   if (Option->LoadOptionsSize != 0) {
2420     ImageInfo->LoadOptionsSize  = Option->LoadOptionsSize;
2421     ImageInfo->LoadOptions      = Option->LoadOptions;
2422   }
2423 
2424   //
2425   // Clean to NULL because the image is loaded directly from the firmwares boot manager.
2426   //
2427   ImageInfo->ParentHandle = NULL;
2428 
2429   //
2430   // Before calling the image, enable the Watchdog Timer for
2431   // the 5 Minute period
2432   //
2433   gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
2434 
2435   //
2436   // Write boot to OS performance data for UEFI boot
2437   //
2438   PERF_CODE (
2439     WriteBootToOsPerformanceData (NULL, NULL);
2440   );
2441 
2442   //
2443   // Report status code for OS Loader StartImage.
2444   //
2445   REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart));
2446 
2447   Status = gBS->StartImage (ImageHandle, ExitDataSize, ExitData);
2448   DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status));
2449   if (EFI_ERROR (Status)) {
2450     //
2451     // Report Status Code to indicate that boot failure
2452     //
2453     REPORT_STATUS_CODE (
2454       EFI_ERROR_CODE | EFI_ERROR_MINOR,
2455       (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED)
2456       );
2457   }
2458 
2459   //
2460   // Clear the Watchdog Timer after the image returns
2461   //
2462   gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
2463 
2464 Done:
2465   //
2466   // Set Logo status invalid after trying one boot option
2467   //
2468   BootLogo = NULL;
2469   StatusLogo = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo);
2470   if (!EFI_ERROR (StatusLogo) && (BootLogo != NULL)) {
2471     BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0);
2472   }
2473 
2474   //
2475   // Clear Boot Current
2476   // Deleting variable with current implementation shouldn't fail.
2477   //
2478   gRT->SetVariable (
2479         L"BootCurrent",
2480         &gEfiGlobalVariableGuid,
2481         EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
2482         0,
2483         NULL
2484         );
2485 
2486   return Status;
2487 }
2488 
2489 
2490 /**
2491   Expand a device path that starts with a hard drive media device path node to be a
2492   full device path that includes the full hardware path to the device. We need
2493   to do this so it can be booted. As an optimization the front match (the part point
2494   to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable
2495   so a connect all is not required on every boot. All successful history device path
2496   which point to partition node (the front part) will be saved.
2497 
2498   @param  HardDriveDevicePath    EFI Device Path to boot, if it starts with a hard
2499                                  drive media device path.
2500   @return A Pointer to the full device path or NULL if a valid Hard Drive devic path
2501           cannot be found.
2502 
2503 **/
2504 EFI_DEVICE_PATH_PROTOCOL *
2505 EFIAPI
BdsExpandPartitionPartialDevicePathToFull(IN HARDDRIVE_DEVICE_PATH * HardDriveDevicePath)2506 BdsExpandPartitionPartialDevicePathToFull (
2507   IN  HARDDRIVE_DEVICE_PATH      *HardDriveDevicePath
2508   )
2509 {
2510   EFI_STATUS                Status;
2511   UINTN                     BlockIoHandleCount;
2512   EFI_HANDLE                *BlockIoBuffer;
2513   EFI_DEVICE_PATH_PROTOCOL  *FullDevicePath;
2514   EFI_DEVICE_PATH_PROTOCOL  *BlockIoDevicePath;
2515   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
2516   UINTN                     Index;
2517   UINTN                     InstanceNum;
2518   EFI_DEVICE_PATH_PROTOCOL  *CachedDevicePath;
2519   EFI_DEVICE_PATH_PROTOCOL  *TempNewDevicePath;
2520   UINTN                     CachedDevicePathSize;
2521   BOOLEAN                   DeviceExist;
2522   BOOLEAN                   NeedAdjust;
2523   EFI_DEVICE_PATH_PROTOCOL  *Instance;
2524   UINTN                     Size;
2525 
2526   FullDevicePath = NULL;
2527   //
2528   // Check if there is prestore HD_BOOT_DEVICE_PATH_VARIABLE_NAME variable.
2529   // If exist, search the front path which point to partition node in the variable instants.
2530   // If fail to find or HD_BOOT_DEVICE_PATH_VARIABLE_NAME not exist, reconnect all and search in all system
2531   //
2532   GetVariable2 (
2533     HD_BOOT_DEVICE_PATH_VARIABLE_NAME,
2534     &gHdBootDevicePathVariablGuid,
2535     (VOID **) &CachedDevicePath,
2536     &CachedDevicePathSize
2537     );
2538 
2539   //
2540   // Delete the invalid HD_BOOT_DEVICE_PATH_VARIABLE_NAME variable.
2541   //
2542   if ((CachedDevicePath != NULL) && !IsDevicePathValid (CachedDevicePath, CachedDevicePathSize)) {
2543     FreePool (CachedDevicePath);
2544     CachedDevicePath = NULL;
2545     Status = gRT->SetVariable (
2546                     HD_BOOT_DEVICE_PATH_VARIABLE_NAME,
2547                     &gHdBootDevicePathVariablGuid,
2548                     0,
2549                     0,
2550                     NULL
2551                     );
2552     ASSERT_EFI_ERROR (Status);
2553   }
2554 
2555   if (CachedDevicePath != NULL) {
2556     TempNewDevicePath = CachedDevicePath;
2557     DeviceExist = FALSE;
2558     NeedAdjust = FALSE;
2559     do {
2560       //
2561       // Check every instance of the variable
2562       // First, check whether the instance contain the partition node, which is needed for distinguishing  multi
2563       // partial partition boot option. Second, check whether the instance could be connected.
2564       //
2565       Instance  = GetNextDevicePathInstance (&TempNewDevicePath, &Size);
2566       if (MatchPartitionDevicePathNode (Instance, HardDriveDevicePath)) {
2567         //
2568         // Connect the device path instance, the device path point to hard drive media device path node
2569         // e.g. ACPI() /PCI()/ATA()/Partition()
2570         //
2571         Status = BdsLibConnectDevicePath (Instance);
2572         if (!EFI_ERROR (Status)) {
2573           DeviceExist = TRUE;
2574           break;
2575         }
2576       }
2577       //
2578       // Come here means the first instance is not matched
2579       //
2580       NeedAdjust = TRUE;
2581       FreePool(Instance);
2582     } while (TempNewDevicePath != NULL);
2583 
2584     if (DeviceExist) {
2585       //
2586       // Find the matched device path.
2587       // Append the file path information from the boot option and return the fully expanded device path.
2588       //
2589       DevicePath     = NextDevicePathNode ((EFI_DEVICE_PATH_PROTOCOL *) HardDriveDevicePath);
2590       FullDevicePath = AppendDevicePath (Instance, DevicePath);
2591 
2592       //
2593       // Adjust the HD_BOOT_DEVICE_PATH_VARIABLE_NAME instances sequence if the matched one is not first one.
2594       //
2595       if (NeedAdjust) {
2596         //
2597         // First delete the matched instance.
2598         //
2599         TempNewDevicePath = CachedDevicePath;
2600         CachedDevicePath  = BdsLibDelPartMatchInstance (CachedDevicePath, Instance );
2601         FreePool (TempNewDevicePath);
2602 
2603         //
2604         // Second, append the remaining path after the matched instance
2605         //
2606         TempNewDevicePath = CachedDevicePath;
2607         CachedDevicePath = AppendDevicePathInstance (Instance, CachedDevicePath );
2608         FreePool (TempNewDevicePath);
2609         //
2610         // Save the matching Device Path so we don't need to do a connect all next time
2611         // Failure to set the variable only impacts the performance when next time expanding the short-form device path.
2612         //
2613         Status = gRT->SetVariable (
2614                         HD_BOOT_DEVICE_PATH_VARIABLE_NAME,
2615                         &gHdBootDevicePathVariablGuid,
2616                         EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
2617                         GetDevicePathSize (CachedDevicePath),
2618                         CachedDevicePath
2619                         );
2620       }
2621 
2622       FreePool (Instance);
2623       FreePool (CachedDevicePath);
2624       return FullDevicePath;
2625     }
2626   }
2627 
2628   //
2629   // If we get here we fail to find or HD_BOOT_DEVICE_PATH_VARIABLE_NAME not exist, and now we need
2630   // to search all devices in the system for a matched partition
2631   //
2632   BdsLibConnectAllDriversToAllControllers ();
2633   Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer);
2634   if (EFI_ERROR (Status) || BlockIoHandleCount == 0 || BlockIoBuffer == NULL) {
2635     //
2636     // If there was an error or there are no device handles that support
2637     // the BLOCK_IO Protocol, then return.
2638     //
2639     return NULL;
2640   }
2641   //
2642   // Loop through all the device handles that support the BLOCK_IO Protocol
2643   //
2644   for (Index = 0; Index < BlockIoHandleCount; Index++) {
2645 
2646     Status = gBS->HandleProtocol (BlockIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID *) &BlockIoDevicePath);
2647     if (EFI_ERROR (Status) || BlockIoDevicePath == NULL) {
2648       continue;
2649     }
2650 
2651     if (MatchPartitionDevicePathNode (BlockIoDevicePath, HardDriveDevicePath)) {
2652       //
2653       // Find the matched partition device path
2654       //
2655       DevicePath    = NextDevicePathNode ((EFI_DEVICE_PATH_PROTOCOL *) HardDriveDevicePath);
2656       FullDevicePath = AppendDevicePath (BlockIoDevicePath, DevicePath);
2657 
2658       //
2659       // Save the matched partition device path in HD_BOOT_DEVICE_PATH_VARIABLE_NAME variable
2660       //
2661       if (CachedDevicePath != NULL) {
2662         //
2663         // Save the matched partition device path as first instance of HD_BOOT_DEVICE_PATH_VARIABLE_NAME variable
2664         //
2665         if (BdsLibMatchDevicePaths (CachedDevicePath, BlockIoDevicePath)) {
2666           TempNewDevicePath = CachedDevicePath;
2667           CachedDevicePath = BdsLibDelPartMatchInstance (CachedDevicePath, BlockIoDevicePath);
2668           FreePool(TempNewDevicePath);
2669         }
2670 
2671         if (CachedDevicePath != NULL) {
2672           TempNewDevicePath = CachedDevicePath;
2673           CachedDevicePath = AppendDevicePathInstance (BlockIoDevicePath, CachedDevicePath);
2674           FreePool(TempNewDevicePath);
2675         } else {
2676           CachedDevicePath = DuplicateDevicePath (BlockIoDevicePath);
2677         }
2678 
2679         //
2680         // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller
2681         // If the user try to boot many OS in different HDs or partitions, in theory,
2682         // the HD_BOOT_DEVICE_PATH_VARIABLE_NAME variable maybe become larger and larger.
2683         //
2684         InstanceNum = 0;
2685         ASSERT (CachedDevicePath != NULL);
2686         TempNewDevicePath = CachedDevicePath;
2687         while (!IsDevicePathEnd (TempNewDevicePath)) {
2688           TempNewDevicePath = NextDevicePathNode (TempNewDevicePath);
2689           //
2690           // Parse one instance
2691           //
2692           while (!IsDevicePathEndType (TempNewDevicePath)) {
2693             TempNewDevicePath = NextDevicePathNode (TempNewDevicePath);
2694           }
2695           InstanceNum++;
2696           //
2697           // If the CachedDevicePath variable contain too much instance, only remain 12 instances.
2698           //
2699           if (InstanceNum >= 12) {
2700             SetDevicePathEndNode (TempNewDevicePath);
2701             break;
2702           }
2703         }
2704       } else {
2705         CachedDevicePath = DuplicateDevicePath (BlockIoDevicePath);
2706       }
2707 
2708       //
2709       // Save the matching Device Path so we don't need to do a connect all next time
2710       // Failure to set the variable only impacts the performance when next time expanding the short-form device path.
2711       //
2712       Status = gRT->SetVariable (
2713                       HD_BOOT_DEVICE_PATH_VARIABLE_NAME,
2714                       &gHdBootDevicePathVariablGuid,
2715                       EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
2716                       GetDevicePathSize (CachedDevicePath),
2717                       CachedDevicePath
2718                       );
2719 
2720       break;
2721     }
2722   }
2723 
2724   if (CachedDevicePath != NULL) {
2725     FreePool (CachedDevicePath);
2726   }
2727   if (BlockIoBuffer != NULL) {
2728     FreePool (BlockIoBuffer);
2729   }
2730   return FullDevicePath;
2731 }
2732 
2733 /**
2734   Check whether there is a instance in BlockIoDevicePath, which contain multi device path
2735   instances, has the same partition node with HardDriveDevicePath device path
2736 
2737   @param  BlockIoDevicePath      Multi device path instances which need to check
2738   @param  HardDriveDevicePath    A device path which starts with a hard drive media
2739                                  device path.
2740 
2741   @retval TRUE                   There is a matched device path instance.
2742   @retval FALSE                  There is no matched device path instance.
2743 
2744 **/
2745 BOOLEAN
2746 EFIAPI
MatchPartitionDevicePathNode(IN EFI_DEVICE_PATH_PROTOCOL * BlockIoDevicePath,IN HARDDRIVE_DEVICE_PATH * HardDriveDevicePath)2747 MatchPartitionDevicePathNode (
2748   IN  EFI_DEVICE_PATH_PROTOCOL   *BlockIoDevicePath,
2749   IN  HARDDRIVE_DEVICE_PATH      *HardDriveDevicePath
2750   )
2751 {
2752   HARDDRIVE_DEVICE_PATH     *TmpHdPath;
2753   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
2754   BOOLEAN                   Match;
2755   EFI_DEVICE_PATH_PROTOCOL  *BlockIoHdDevicePathNode;
2756 
2757   if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) {
2758     return FALSE;
2759   }
2760 
2761   //
2762   // Make PreviousDevicePath == the device path node before the end node
2763   //
2764   DevicePath              = BlockIoDevicePath;
2765   BlockIoHdDevicePathNode = NULL;
2766 
2767   //
2768   // find the partition device path node
2769   //
2770   while (!IsDevicePathEnd (DevicePath)) {
2771     if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
2772         (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)
2773         ) {
2774       BlockIoHdDevicePathNode = DevicePath;
2775       break;
2776     }
2777 
2778     DevicePath = NextDevicePathNode (DevicePath);
2779   }
2780 
2781   if (BlockIoHdDevicePathNode == NULL) {
2782     return FALSE;
2783   }
2784   //
2785   // See if the harddrive device path in blockio matches the orig Hard Drive Node
2786   //
2787   TmpHdPath = (HARDDRIVE_DEVICE_PATH *) BlockIoHdDevicePathNode;
2788   Match = FALSE;
2789 
2790   //
2791   // Check for the match
2792   //
2793   if ((TmpHdPath->MBRType == HardDriveDevicePath->MBRType) &&
2794       (TmpHdPath->SignatureType == HardDriveDevicePath->SignatureType)) {
2795     switch (TmpHdPath->SignatureType) {
2796     case SIGNATURE_TYPE_GUID:
2797       Match = CompareGuid ((EFI_GUID *)TmpHdPath->Signature, (EFI_GUID *)HardDriveDevicePath->Signature);
2798       break;
2799     case SIGNATURE_TYPE_MBR:
2800       Match = (BOOLEAN)(*((UINT32 *)(&(TmpHdPath->Signature[0]))) == ReadUnaligned32((UINT32 *)(&(HardDriveDevicePath->Signature[0]))));
2801       break;
2802     default:
2803       Match = FALSE;
2804       break;
2805     }
2806   }
2807 
2808   return Match;
2809 }
2810 
2811 /**
2812   Delete the boot option associated with the handle passed in.
2813 
2814   @param  Handle                 The handle which present the device path to create
2815                                  boot option
2816 
2817   @retval EFI_SUCCESS            Delete the boot option success
2818   @retval EFI_NOT_FOUND          If the Device Path is not found in the system
2819   @retval EFI_OUT_OF_RESOURCES   Lack of memory resource
2820   @retval Other                  Error return value from SetVariable()
2821 
2822 **/
2823 EFI_STATUS
BdsLibDeleteOptionFromHandle(IN EFI_HANDLE Handle)2824 BdsLibDeleteOptionFromHandle (
2825   IN  EFI_HANDLE                 Handle
2826   )
2827 {
2828   UINT16                    *BootOrder;
2829   UINT8                     *BootOptionVar;
2830   UINTN                     BootOrderSize;
2831   UINTN                     BootOptionSize;
2832   EFI_STATUS                Status;
2833   UINTN                     Index;
2834   UINT16                    BootOption[BOOT_OPTION_MAX_CHAR];
2835   UINTN                     DevicePathSize;
2836   UINTN                     OptionDevicePathSize;
2837   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
2838   EFI_DEVICE_PATH_PROTOCOL  *OptionDevicePath;
2839   UINT8                     *TempPtr;
2840 
2841   Status        = EFI_SUCCESS;
2842   BootOrder     = NULL;
2843   BootOrderSize = 0;
2844 
2845   //
2846   // Check "BootOrder" variable, if no, means there is no any boot order.
2847   //
2848   BootOrder = BdsLibGetVariableAndSize (
2849                 L"BootOrder",
2850                 &gEfiGlobalVariableGuid,
2851                 &BootOrderSize
2852                 );
2853   if (BootOrder == NULL) {
2854     return EFI_NOT_FOUND;
2855   }
2856 
2857   //
2858   // Convert device handle to device path protocol instance
2859   //
2860   DevicePath = DevicePathFromHandle (Handle);
2861   if (DevicePath == NULL) {
2862     return EFI_NOT_FOUND;
2863   }
2864   DevicePathSize = GetDevicePathSize (DevicePath);
2865 
2866   //
2867   // Loop all boot order variable and find the matching device path
2868   //
2869   Index = 0;
2870   while (Index < BootOrderSize / sizeof (UINT16)) {
2871     UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
2872     BootOptionVar = BdsLibGetVariableAndSize (
2873                       BootOption,
2874                       &gEfiGlobalVariableGuid,
2875                       &BootOptionSize
2876                       );
2877 
2878     if (BootOptionVar == NULL) {
2879       FreePool (BootOrder);
2880       return EFI_OUT_OF_RESOURCES;
2881     }
2882 
2883     if (!ValidateOption(BootOptionVar, BootOptionSize)) {
2884       BdsDeleteBootOption (BootOrder[Index], BootOrder, &BootOrderSize);
2885       FreePool (BootOptionVar);
2886       Index++;
2887       continue;
2888     }
2889 
2890     TempPtr = BootOptionVar;
2891     TempPtr += sizeof (UINT32) + sizeof (UINT16);
2892     TempPtr += StrSize ((CHAR16 *) TempPtr);
2893     OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
2894     OptionDevicePathSize = GetDevicePathSize (OptionDevicePath);
2895 
2896     //
2897     // Check whether the device path match
2898     //
2899     if ((OptionDevicePathSize == DevicePathSize) &&
2900         (CompareMem (DevicePath, OptionDevicePath, DevicePathSize) == 0)) {
2901       BdsDeleteBootOption (BootOrder[Index], BootOrder, &BootOrderSize);
2902       FreePool (BootOptionVar);
2903       break;
2904     }
2905 
2906     FreePool (BootOptionVar);
2907     Index++;
2908   }
2909 
2910   //
2911   // Adjust number of boot option for "BootOrder" variable.
2912   //
2913   Status = gRT->SetVariable (
2914                   L"BootOrder",
2915                   &gEfiGlobalVariableGuid,
2916                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
2917                   BootOrderSize,
2918                   BootOrder
2919                   );
2920   //
2921   // Shrinking variable with existing variable implementation shouldn't fail.
2922   //
2923   ASSERT_EFI_ERROR (Status);
2924 
2925   FreePool (BootOrder);
2926 
2927   return Status;
2928 }
2929 
2930 
2931 /**
2932   Delete all invalid EFI boot options.
2933 
2934   @retval EFI_SUCCESS            Delete all invalid boot option success
2935   @retval EFI_NOT_FOUND          Variable "BootOrder" is not found
2936   @retval EFI_OUT_OF_RESOURCES   Lack of memory resource
2937   @retval Other                  Error return value from SetVariable()
2938 
2939 **/
2940 EFI_STATUS
BdsDeleteAllInvalidEfiBootOption(VOID)2941 BdsDeleteAllInvalidEfiBootOption (
2942   VOID
2943   )
2944 {
2945   UINT16                    *BootOrder;
2946   UINT8                     *BootOptionVar;
2947   UINTN                     BootOrderSize;
2948   UINTN                     BootOptionSize;
2949   EFI_STATUS                Status;
2950   UINTN                     Index;
2951   UINTN                     Index2;
2952   UINT16                    BootOption[BOOT_OPTION_MAX_CHAR];
2953   EFI_DEVICE_PATH_PROTOCOL  *OptionDevicePath;
2954   UINT8                     *TempPtr;
2955   CHAR16                    *Description;
2956   BOOLEAN                   Corrupted;
2957 
2958   Status           = EFI_SUCCESS;
2959   BootOrder        = NULL;
2960   Description      = NULL;
2961   OptionDevicePath = NULL;
2962   BootOrderSize    = 0;
2963   Corrupted        = FALSE;
2964 
2965   //
2966   // Check "BootOrder" variable firstly, this variable hold the number of boot options
2967   //
2968   BootOrder = BdsLibGetVariableAndSize (
2969                 L"BootOrder",
2970                 &gEfiGlobalVariableGuid,
2971                 &BootOrderSize
2972                 );
2973   if (NULL == BootOrder) {
2974     return EFI_NOT_FOUND;
2975   }
2976 
2977   Index = 0;
2978   while (Index < BootOrderSize / sizeof (UINT16)) {
2979     UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
2980     BootOptionVar = BdsLibGetVariableAndSize (
2981                       BootOption,
2982                       &gEfiGlobalVariableGuid,
2983                       &BootOptionSize
2984                       );
2985     if (NULL == BootOptionVar) {
2986       FreePool (BootOrder);
2987       return EFI_OUT_OF_RESOURCES;
2988     }
2989 
2990     if (!ValidateOption(BootOptionVar, BootOptionSize)) {
2991       Corrupted = TRUE;
2992     } else {
2993       TempPtr = BootOptionVar;
2994       TempPtr += sizeof (UINT32) + sizeof (UINT16);
2995       Description = (CHAR16 *) TempPtr;
2996       TempPtr += StrSize ((CHAR16 *) TempPtr);
2997       OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
2998 
2999       //
3000       // Skip legacy boot option (BBS boot device)
3001       //
3002       if ((DevicePathType (OptionDevicePath) == BBS_DEVICE_PATH) &&
3003           (DevicePathSubType (OptionDevicePath) == BBS_BBS_DP)) {
3004         FreePool (BootOptionVar);
3005         Index++;
3006         continue;
3007       }
3008     }
3009 
3010     if (Corrupted || !BdsLibIsValidEFIBootOptDevicePathExt (OptionDevicePath, FALSE, Description)) {
3011       //
3012       // Delete this invalid boot option "Boot####"
3013       //
3014       Status = gRT->SetVariable (
3015                       BootOption,
3016                       &gEfiGlobalVariableGuid,
3017                       EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
3018                       0,
3019                       NULL
3020                       );
3021       //
3022       // Deleting variable with current variable implementation shouldn't fail.
3023       //
3024       ASSERT_EFI_ERROR (Status);
3025       //
3026       // Mark this boot option in boot order as deleted
3027       //
3028       BootOrder[Index] = 0xffff;
3029       Corrupted        = FALSE;
3030     }
3031 
3032     FreePool (BootOptionVar);
3033     Index++;
3034   }
3035 
3036   //
3037   // Adjust boot order array
3038   //
3039   Index2 = 0;
3040   for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
3041     if (BootOrder[Index] != 0xffff) {
3042       BootOrder[Index2] = BootOrder[Index];
3043       Index2 ++;
3044     }
3045   }
3046   Status = gRT->SetVariable (
3047                   L"BootOrder",
3048                   &gEfiGlobalVariableGuid,
3049                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
3050                   Index2 * sizeof (UINT16),
3051                   BootOrder
3052                   );
3053   //
3054   // Shrinking variable with current variable implementation shouldn't fail.
3055   //
3056   ASSERT_EFI_ERROR (Status);
3057 
3058   FreePool (BootOrder);
3059 
3060   return Status;
3061 }
3062 
3063 
3064 /**
3065   For EFI boot option, BDS separate them as six types:
3066   1. Network - The boot option points to the SimpleNetworkProtocol device.
3067                Bds will try to automatically create this type boot option when enumerate.
3068   2. Shell   - The boot option points to internal flash shell.
3069                Bds will try to automatically create this type boot option when enumerate.
3070   3. Removable BlockIo      - The boot option only points to the removable media
3071                               device, like USB flash disk, DVD, Floppy etc.
3072                               These device should contain a *removable* blockIo
3073                               protocol in their device handle.
3074                               Bds will try to automatically create this type boot option
3075                               when enumerate.
3076   4. Fixed BlockIo          - The boot option only points to a Fixed blockIo device,
3077                               like HardDisk.
3078                               These device should contain a *fixed* blockIo
3079                               protocol in their device handle.
3080                               BDS will skip fixed blockIo devices, and NOT
3081                               automatically create boot option for them. But BDS
3082                               will help to delete those fixed blockIo boot option,
3083                               whose description rule conflict with other auto-created
3084                               boot options.
3085   5. Non-BlockIo Simplefile - The boot option points to a device whose handle
3086                               has SimpleFileSystem Protocol, but has no blockio
3087                               protocol. These devices do not offer blockIo
3088                               protocol, but BDS still can get the
3089                               \EFI\BOOT\boot{machinename}.EFI by SimpleFileSystem
3090                               Protocol.
3091   6. File    - The boot option points to a file. These boot options are usually
3092                created by user manually or OS loader. BDS will not delete or modify
3093                these boot options.
3094 
3095   This function will enumerate all possible boot device in the system, and
3096   automatically create boot options for Network, Shell, Removable BlockIo,
3097   and Non-BlockIo Simplefile devices.
3098   It will only execute once of every boot.
3099 
3100   @param  BdsBootOptionList      The header of the link list which indexed all
3101                                  current boot options
3102 
3103   @retval EFI_SUCCESS            Finished all the boot device enumerate and create
3104                                  the boot option base on that boot device
3105 
3106   @retval EFI_OUT_OF_RESOURCES   Failed to enumerate the boot device and create the boot option list
3107 **/
3108 EFI_STATUS
3109 EFIAPI
BdsLibEnumerateAllBootOption(IN OUT LIST_ENTRY * BdsBootOptionList)3110 BdsLibEnumerateAllBootOption (
3111   IN OUT LIST_ENTRY          *BdsBootOptionList
3112   )
3113 {
3114   EFI_STATUS                    Status;
3115   UINT16                        FloppyNumber;
3116   UINT16                        HarddriveNumber;
3117   UINT16                        CdromNumber;
3118   UINT16                        UsbNumber;
3119   UINT16                        MiscNumber;
3120   UINT16                        ScsiNumber;
3121   UINT16                        NonBlockNumber;
3122   UINTN                         NumberBlockIoHandles;
3123   EFI_HANDLE                    *BlockIoHandles;
3124   EFI_BLOCK_IO_PROTOCOL         *BlkIo;
3125   BOOLEAN                       Removable[2];
3126   UINTN                         RemovableIndex;
3127   UINTN                         Index;
3128   UINTN                         NumOfLoadFileHandles;
3129   EFI_HANDLE                    *LoadFileHandles;
3130   UINTN                         FvHandleCount;
3131   EFI_HANDLE                    *FvHandleBuffer;
3132   EFI_FV_FILETYPE               Type;
3133   UINTN                         Size;
3134   EFI_FV_FILE_ATTRIBUTES        Attributes;
3135   UINT32                        AuthenticationStatus;
3136   EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
3137   EFI_DEVICE_PATH_PROTOCOL      *DevicePath;
3138   UINTN                         DevicePathType;
3139   CHAR16                        Buffer[40];
3140   EFI_HANDLE                    *FileSystemHandles;
3141   UINTN                         NumberFileSystemHandles;
3142   BOOLEAN                       NeedDelete;
3143   EFI_IMAGE_DOS_HEADER          DosHeader;
3144   CHAR8                         *PlatLang;
3145   CHAR8                         *LastLang;
3146   EFI_IMAGE_OPTIONAL_HEADER_UNION       HdrData;
3147   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION   Hdr;
3148   CHAR16                        *MacStr;
3149   CHAR16                        *IPverStr;
3150   EFI_HANDLE                    *NetworkHandles;
3151   UINTN                         BufferSize;
3152 
3153   FloppyNumber    = 0;
3154   HarddriveNumber = 0;
3155   CdromNumber     = 0;
3156   UsbNumber       = 0;
3157   MiscNumber      = 0;
3158   ScsiNumber      = 0;
3159   PlatLang        = NULL;
3160   LastLang        = NULL;
3161   ZeroMem (Buffer, sizeof (Buffer));
3162 
3163   //
3164   // If the boot device enumerate happened, just get the boot
3165   // device from the boot order variable
3166   //
3167   if (mEnumBootDevice) {
3168     GetVariable2 (LAST_ENUM_LANGUAGE_VARIABLE_NAME, &gLastEnumLangGuid, (VOID**)&LastLang, NULL);
3169     GetEfiGlobalVariable2 (L"PlatformLang", (VOID**)&PlatLang, NULL);
3170     ASSERT (PlatLang != NULL);
3171     if ((LastLang != NULL) && (AsciiStrCmp (LastLang, PlatLang) == 0)) {
3172       Status = BdsLibBuildOptionFromVar (BdsBootOptionList, L"BootOrder");
3173       FreePool (LastLang);
3174       FreePool (PlatLang);
3175       return Status;
3176     } else {
3177       Status = gRT->SetVariable (
3178         LAST_ENUM_LANGUAGE_VARIABLE_NAME,
3179         &gLastEnumLangGuid,
3180         EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
3181         AsciiStrSize (PlatLang),
3182         PlatLang
3183         );
3184       //
3185       // Failure to set the variable only impacts the performance next time enumerating the boot options.
3186       //
3187 
3188       if (LastLang != NULL) {
3189         FreePool (LastLang);
3190       }
3191       FreePool (PlatLang);
3192     }
3193   }
3194 
3195   //
3196   // Notes: this dirty code is to get the legacy boot option from the
3197   // BBS table and create to variable as the EFI boot option, it should
3198   // be removed after the CSM can provide legacy boot option directly
3199   //
3200   REFRESH_LEGACY_BOOT_OPTIONS;
3201 
3202   //
3203   // Delete invalid boot option
3204   //
3205   BdsDeleteAllInvalidEfiBootOption ();
3206 
3207   //
3208   // Parse removable media followed by fixed media.
3209   // The Removable[] array is used by the for-loop below to create removable media boot options
3210   // at first, and then to create fixed media boot options.
3211   //
3212   Removable[0]  = FALSE;
3213   Removable[1]  = TRUE;
3214 
3215   gBS->LocateHandleBuffer (
3216         ByProtocol,
3217         &gEfiBlockIoProtocolGuid,
3218         NULL,
3219         &NumberBlockIoHandles,
3220         &BlockIoHandles
3221         );
3222 
3223   for (RemovableIndex = 0; RemovableIndex < 2; RemovableIndex++) {
3224     for (Index = 0; Index < NumberBlockIoHandles; Index++) {
3225       Status = gBS->HandleProtocol (
3226                       BlockIoHandles[Index],
3227                       &gEfiBlockIoProtocolGuid,
3228                       (VOID **) &BlkIo
3229                       );
3230       //
3231       // skip the logical partition
3232       //
3233       if (EFI_ERROR (Status) || BlkIo->Media->LogicalPartition) {
3234         continue;
3235       }
3236 
3237       //
3238       // firstly fixed block io then the removable block io
3239       //
3240       if (BlkIo->Media->RemovableMedia == Removable[RemovableIndex]) {
3241         continue;
3242       }
3243       DevicePath  = DevicePathFromHandle (BlockIoHandles[Index]);
3244       DevicePathType = BdsGetBootTypeFromDevicePath (DevicePath);
3245 
3246       switch (DevicePathType) {
3247       case BDS_EFI_ACPI_FLOPPY_BOOT:
3248         if (FloppyNumber != 0) {
3249           UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_FLOPPY)), FloppyNumber);
3250         } else {
3251           UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_FLOPPY)));
3252         }
3253         BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);
3254         FloppyNumber++;
3255         break;
3256 
3257       //
3258       // Assume a removable SATA device should be the DVD/CD device, a fixed SATA device should be the Hard Drive device.
3259       //
3260       case BDS_EFI_MESSAGE_ATAPI_BOOT:
3261       case BDS_EFI_MESSAGE_SATA_BOOT:
3262         if (BlkIo->Media->RemovableMedia) {
3263           if (CdromNumber != 0) {
3264             UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_CD_DVD)), CdromNumber);
3265           } else {
3266             UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_CD_DVD)));
3267           }
3268           CdromNumber++;
3269         } else {
3270           if (HarddriveNumber != 0) {
3271             UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_HARDDRIVE)), HarddriveNumber);
3272           } else {
3273             UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_HARDDRIVE)));
3274           }
3275           HarddriveNumber++;
3276         }
3277         DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Buffer: %S\n", Buffer));
3278         BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);
3279         break;
3280 
3281       case BDS_EFI_MESSAGE_USB_DEVICE_BOOT:
3282         if (UsbNumber != 0) {
3283           UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_USB)), UsbNumber);
3284         } else {
3285           UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_USB)));
3286         }
3287         BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);
3288         UsbNumber++;
3289         break;
3290 
3291       case BDS_EFI_MESSAGE_SCSI_BOOT:
3292         if (ScsiNumber != 0) {
3293           UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_SCSI)), ScsiNumber);
3294         } else {
3295           UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_SCSI)));
3296         }
3297         BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);
3298         ScsiNumber++;
3299         break;
3300 
3301       case BDS_EFI_MESSAGE_MISC_BOOT:
3302       default:
3303         if (MiscNumber != 0) {
3304           UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_MISC)), MiscNumber);
3305         } else {
3306           UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_MISC)));
3307         }
3308         BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);
3309         MiscNumber++;
3310         break;
3311       }
3312     }
3313   }
3314 
3315   if (NumberBlockIoHandles != 0) {
3316     FreePool (BlockIoHandles);
3317   }
3318 
3319   //
3320   // If there is simple file protocol which does not consume block Io protocol, create a boot option for it here.
3321   //
3322   NonBlockNumber = 0;
3323   gBS->LocateHandleBuffer (
3324         ByProtocol,
3325         &gEfiSimpleFileSystemProtocolGuid,
3326         NULL,
3327         &NumberFileSystemHandles,
3328         &FileSystemHandles
3329         );
3330   for (Index = 0; Index < NumberFileSystemHandles; Index++) {
3331     Status = gBS->HandleProtocol (
3332                     FileSystemHandles[Index],
3333                     &gEfiBlockIoProtocolGuid,
3334                     (VOID **) &BlkIo
3335                     );
3336      if (!EFI_ERROR (Status)) {
3337       //
3338       //  Skip if the file system handle supports a BlkIo protocol,
3339       //
3340       continue;
3341     }
3342 
3343     //
3344     // Do the removable Media thing. \EFI\BOOT\boot{machinename}.EFI
3345     //  machinename is ia32, ia64, x64, ...
3346     //
3347     Hdr.Union  = &HdrData;
3348     NeedDelete = TRUE;
3349     Status     = BdsLibGetImageHeader (
3350                    FileSystemHandles[Index],
3351                    EFI_REMOVABLE_MEDIA_FILE_NAME,
3352                    &DosHeader,
3353                    Hdr
3354                    );
3355     if (!EFI_ERROR (Status) &&
3356         EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Hdr.Pe32->FileHeader.Machine) &&
3357         Hdr.Pe32->OptionalHeader.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) {
3358       NeedDelete = FALSE;
3359     }
3360 
3361     if (NeedDelete) {
3362       //
3363       // No such file or the file is not a EFI application, delete this boot option
3364       //
3365       BdsLibDeleteOptionFromHandle (FileSystemHandles[Index]);
3366     } else {
3367       if (NonBlockNumber != 0) {
3368         UnicodeSPrint (Buffer, sizeof (Buffer), L"%s %d", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_NON_BLOCK)), NonBlockNumber);
3369       } else {
3370         UnicodeSPrint (Buffer, sizeof (Buffer), L"%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_NON_BLOCK)));
3371       }
3372       BdsLibBuildOptionFromHandle (FileSystemHandles[Index], BdsBootOptionList, Buffer);
3373       NonBlockNumber++;
3374     }
3375   }
3376 
3377   if (NumberFileSystemHandles != 0) {
3378     FreePool (FileSystemHandles);
3379   }
3380 
3381   //
3382   // Parse Network Boot Device
3383   //
3384   NumOfLoadFileHandles = 0;
3385   //
3386   // Search Load File protocol for PXE boot option.
3387   //
3388   gBS->LocateHandleBuffer (
3389         ByProtocol,
3390         &gEfiLoadFileProtocolGuid,
3391         NULL,
3392         &NumOfLoadFileHandles,
3393         &LoadFileHandles
3394         );
3395 
3396   for (Index = 0; Index < NumOfLoadFileHandles; Index++) {
3397 
3398 //
3399 //Locate EFI_DEVICE_PATH_PROTOCOL to dynamically get IPv4/IPv6 protocol information.
3400 //
3401 
3402  Status = gBS->HandleProtocol (
3403                   LoadFileHandles[Index],
3404                   &gEfiDevicePathProtocolGuid,
3405                   (VOID **) &DevicePath
3406                   );
3407 
3408  ASSERT_EFI_ERROR (Status);
3409 
3410   while (!IsDevicePathEnd (DevicePath)) {
3411     if ((DevicePath->Type == MESSAGING_DEVICE_PATH) &&
3412         (DevicePath->SubType == MSG_IPv4_DP)) {
3413 
3414   //
3415   //Get handle infomation
3416   //
3417   BufferSize = 0;
3418   NetworkHandles = NULL;
3419   Status = gBS->LocateHandle (
3420                   ByProtocol,
3421                   &gEfiSimpleNetworkProtocolGuid,
3422                   NULL,
3423                   &BufferSize,
3424                   NetworkHandles
3425                   );
3426 
3427   if (Status == EFI_BUFFER_TOO_SMALL) {
3428     NetworkHandles = AllocateZeroPool(BufferSize);
3429     if (NetworkHandles == NULL) {
3430       return (EFI_OUT_OF_RESOURCES);
3431     }
3432     Status = gBS->LocateHandle(
3433                     ByProtocol,
3434                     &gEfiSimpleNetworkProtocolGuid,
3435                     NULL,
3436                     &BufferSize,
3437                     NetworkHandles
3438                     );
3439  }
3440 
3441   //
3442   //Get the MAC string
3443   //
3444   Status = NetLibGetMacString (
3445              *NetworkHandles,
3446              NULL,
3447              &MacStr
3448              );
3449   if (EFI_ERROR (Status)) {
3450     return Status;
3451   }
3452   IPverStr = L" IPv4";
3453   UnicodeSPrint (Buffer, sizeof (Buffer), L"%s%s%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_NETWORK)),MacStr,IPverStr);
3454   break;
3455   }
3456     if((DevicePath->Type == MESSAGING_DEVICE_PATH) &&
3457         (DevicePath->SubType == MSG_IPv6_DP)) {
3458 
3459   //
3460   //Get handle infomation
3461   //
3462   BufferSize = 0;
3463   NetworkHandles = NULL;
3464   Status = gBS->LocateHandle (
3465                   ByProtocol,
3466                   &gEfiSimpleNetworkProtocolGuid,
3467                   NULL,
3468                   &BufferSize,
3469                   NetworkHandles
3470                   );
3471 
3472   if (Status == EFI_BUFFER_TOO_SMALL) {
3473     NetworkHandles = AllocateZeroPool(BufferSize);
3474     if (NetworkHandles == NULL) {
3475        return (EFI_OUT_OF_RESOURCES);
3476     }
3477     Status = gBS->LocateHandle(
3478                     ByProtocol,
3479                     &gEfiSimpleNetworkProtocolGuid,
3480                     NULL,
3481                     &BufferSize,
3482                     NetworkHandles
3483                     );
3484  }
3485 
3486   //
3487   //Get the MAC string
3488   //
3489   Status = NetLibGetMacString (
3490              *NetworkHandles,
3491              NULL,
3492              &MacStr
3493              );
3494   if (EFI_ERROR (Status)) {
3495     return Status;
3496   }
3497       IPverStr = L" IPv6";
3498       UnicodeSPrint (Buffer, sizeof (Buffer), L"%s%s%s", BdsLibGetStringById (STRING_TOKEN (STR_DESCRIPTION_NETWORK)),MacStr,IPverStr);
3499       break;
3500     }
3501     DevicePath = NextDevicePathNode (DevicePath);
3502   }
3503 
3504     BdsLibBuildOptionFromHandle (LoadFileHandles[Index], BdsBootOptionList, Buffer);
3505   }
3506 
3507   if (NumOfLoadFileHandles != 0) {
3508     FreePool (LoadFileHandles);
3509   }
3510 
3511   //
3512   // Check if we have on flash shell
3513   //
3514   gBS->LocateHandleBuffer (
3515         ByProtocol,
3516         &gEfiFirmwareVolume2ProtocolGuid,
3517         NULL,
3518         &FvHandleCount,
3519         &FvHandleBuffer
3520         );
3521   for (Index = 0; Index < FvHandleCount; Index++) {
3522     gBS->HandleProtocol (
3523           FvHandleBuffer[Index],
3524           &gEfiFirmwareVolume2ProtocolGuid,
3525           (VOID **) &Fv
3526           );
3527 
3528     Status = Fv->ReadFile (
3529                   Fv,
3530                   PcdGetPtr(PcdShellFile),
3531                   NULL,
3532                   &Size,
3533                   &Type,
3534                   &Attributes,
3535                   &AuthenticationStatus
3536                   );
3537     if (EFI_ERROR (Status)) {
3538       //
3539       // Skip if no shell file in the FV
3540       //
3541       continue;
3542     }
3543     //
3544     // Build the shell boot option
3545     //
3546     BdsLibBuildOptionFromShell (FvHandleBuffer[Index], BdsBootOptionList);
3547   }
3548 
3549   if (FvHandleCount != 0) {
3550     FreePool (FvHandleBuffer);
3551   }
3552   //
3553   // Make sure every boot only have one time
3554   // boot device enumerate
3555   //
3556   Status = BdsLibBuildOptionFromVar (BdsBootOptionList, L"BootOrder");
3557   mEnumBootDevice = TRUE;
3558 
3559   return Status;
3560 }
3561 
3562 /**
3563   Build the boot option with the handle parsed in
3564 
3565   @param  Handle                 The handle which present the device path to create
3566                                  boot option
3567   @param  BdsBootOptionList      The header of the link list which indexed all
3568                                  current boot options
3569   @param  String                 The description of the boot option.
3570 
3571 **/
3572 VOID
3573 EFIAPI
BdsLibBuildOptionFromHandle(IN EFI_HANDLE Handle,IN LIST_ENTRY * BdsBootOptionList,IN CHAR16 * String)3574 BdsLibBuildOptionFromHandle (
3575   IN  EFI_HANDLE                 Handle,
3576   IN  LIST_ENTRY                 *BdsBootOptionList,
3577   IN  CHAR16                     *String
3578   )
3579 {
3580   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
3581 
3582   DevicePath = DevicePathFromHandle (Handle);
3583 
3584   //
3585   // Create and register new boot option
3586   //
3587   BdsLibRegisterNewOption (BdsBootOptionList, DevicePath, String, L"BootOrder");
3588 }
3589 
3590 
3591 /**
3592   Build the on flash shell boot option with the handle parsed in.
3593 
3594   @param  Handle                 The handle which present the device path to create
3595                                  on flash shell boot option
3596   @param  BdsBootOptionList      The header of the link list which indexed all
3597                                  current boot options
3598 
3599 **/
3600 VOID
3601 EFIAPI
BdsLibBuildOptionFromShell(IN EFI_HANDLE Handle,IN OUT LIST_ENTRY * BdsBootOptionList)3602 BdsLibBuildOptionFromShell (
3603   IN EFI_HANDLE                  Handle,
3604   IN OUT LIST_ENTRY              *BdsBootOptionList
3605   )
3606 {
3607   EFI_DEVICE_PATH_PROTOCOL          *DevicePath;
3608   MEDIA_FW_VOL_FILEPATH_DEVICE_PATH ShellNode;
3609 
3610   DevicePath = DevicePathFromHandle (Handle);
3611 
3612   //
3613   // Build the shell device path
3614   //
3615   EfiInitializeFwVolDevicepathNode (&ShellNode, PcdGetPtr(PcdShellFile));
3616 
3617   DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &ShellNode);
3618 
3619   //
3620   // Create and register the shell boot option
3621   //
3622   BdsLibRegisterNewOption (BdsBootOptionList, DevicePath, L"EFI Internal Shell", L"BootOrder");
3623 
3624 }
3625 
3626 /**
3627   Boot from the UEFI spec defined "BootNext" variable.
3628 
3629 **/
3630 VOID
3631 EFIAPI
BdsLibBootNext(VOID)3632 BdsLibBootNext (
3633   VOID
3634   )
3635 {
3636   EFI_STATUS        Status;
3637   UINT16            *BootNext;
3638   UINTN             BootNextSize;
3639   CHAR16            Buffer[20];
3640   BDS_COMMON_OPTION *BootOption;
3641   LIST_ENTRY        TempList;
3642   UINTN             ExitDataSize;
3643   CHAR16            *ExitData;
3644 
3645   //
3646   // Init the boot option name buffer and temp link list
3647   //
3648   InitializeListHead (&TempList);
3649   ZeroMem (Buffer, sizeof (Buffer));
3650 
3651   BootNext = BdsLibGetVariableAndSize (
3652               L"BootNext",
3653               &gEfiGlobalVariableGuid,
3654               &BootNextSize
3655               );
3656 
3657   //
3658   // Clear the boot next variable first
3659   //
3660   if (BootNext != NULL) {
3661     Status = gRT->SetVariable (
3662                     L"BootNext",
3663                     &gEfiGlobalVariableGuid,
3664                     EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
3665                     0,
3666                     NULL
3667                     );
3668     //
3669     // Deleting variable with current variable implementation shouldn't fail.
3670     //
3671     ASSERT_EFI_ERROR (Status);
3672 
3673     //
3674     // Start to build the boot option and try to boot
3675     //
3676     UnicodeSPrint (Buffer, sizeof (Buffer), L"Boot%04x", *BootNext);
3677     BootOption = BdsLibVariableToOption (&TempList, Buffer);
3678     ASSERT (BootOption != NULL);
3679     BdsLibConnectDevicePath (BootOption->DevicePath);
3680     BdsLibBootViaBootOption (BootOption, BootOption->DevicePath, &ExitDataSize, &ExitData);
3681     FreePool(BootOption);
3682     FreePool(BootNext);
3683   }
3684 
3685 }
3686 
3687 /**
3688   Return the bootable media handle.
3689   First, check the device is connected
3690   Second, check whether the device path point to a device which support SimpleFileSystemProtocol,
3691   Third, detect the the default boot file in the Media, and return the removable Media handle.
3692 
3693   @param  DevicePath  Device Path to a  bootable device
3694 
3695   @return  The bootable media handle. If the media on the DevicePath is not bootable, NULL will return.
3696 
3697 **/
3698 EFI_HANDLE
3699 EFIAPI
BdsLibGetBootableHandle(IN EFI_DEVICE_PATH_PROTOCOL * DevicePath)3700 BdsLibGetBootableHandle (
3701   IN  EFI_DEVICE_PATH_PROTOCOL      *DevicePath
3702   )
3703 {
3704   EFI_STATUS                      Status;
3705   EFI_TPL                         OldTpl;
3706   EFI_DEVICE_PATH_PROTOCOL        *UpdatedDevicePath;
3707   EFI_DEVICE_PATH_PROTOCOL        *DupDevicePath;
3708   EFI_HANDLE                      Handle;
3709   EFI_BLOCK_IO_PROTOCOL           *BlockIo;
3710   VOID                            *Buffer;
3711   EFI_DEVICE_PATH_PROTOCOL        *TempDevicePath;
3712   UINTN                           Size;
3713   UINTN                           TempSize;
3714   EFI_HANDLE                      ReturnHandle;
3715   EFI_HANDLE                      *SimpleFileSystemHandles;
3716 
3717   UINTN                           NumberSimpleFileSystemHandles;
3718   UINTN                           Index;
3719   EFI_IMAGE_DOS_HEADER            DosHeader;
3720   EFI_IMAGE_OPTIONAL_HEADER_UNION       HdrData;
3721   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION   Hdr;
3722 
3723   UpdatedDevicePath = DevicePath;
3724 
3725   //
3726   // Enter to critical section to protect the acquired BlockIo instance
3727   // from getting released due to the USB mass storage hotplug event
3728   //
3729   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
3730 
3731   //
3732   // Check whether the device is connected
3733   //
3734   Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &UpdatedDevicePath, &Handle);
3735   if (EFI_ERROR (Status)) {
3736     //
3737     // Skip the case that the boot option point to a simple file protocol which does not consume block Io protocol,
3738     //
3739     Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &UpdatedDevicePath, &Handle);
3740     if (EFI_ERROR (Status)) {
3741       //
3742       // Fail to find the proper BlockIo and simple file protocol, maybe because device not present,  we need to connect it firstly
3743       //
3744       UpdatedDevicePath = DevicePath;
3745       Status            = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &UpdatedDevicePath, &Handle);
3746       gBS->ConnectController (Handle, NULL, NULL, TRUE);
3747     }
3748   } else {
3749     //
3750     // For removable device boot option, its contained device path only point to the removable device handle,
3751     // should make sure all its children handles (its child partion or media handles) are created and connected.
3752     //
3753     gBS->ConnectController (Handle, NULL, NULL, TRUE);
3754     //
3755     // Get BlockIo protocol and check removable attribute
3756     //
3757     Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);
3758     ASSERT_EFI_ERROR (Status);
3759 
3760     //
3761     // Issue a dummy read to the device to check for media change.
3762     // When the removable media is changed, any Block IO read/write will
3763     // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is
3764     // returned. After the Block IO protocol is reinstalled, subsequent
3765     // Block IO read/write will success.
3766     //
3767     Buffer = AllocatePool (BlockIo->Media->BlockSize);
3768     if (Buffer != NULL) {
3769       BlockIo->ReadBlocks (
3770                BlockIo,
3771                BlockIo->Media->MediaId,
3772                0,
3773                BlockIo->Media->BlockSize,
3774                Buffer
3775                );
3776       FreePool(Buffer);
3777     }
3778   }
3779 
3780   //
3781   // Detect the the default boot file from removable Media
3782   //
3783 
3784   //
3785   // 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
3786   // Try to locate the USB node device path first, if fail then use its previous PCI node to search
3787   //
3788   DupDevicePath = DuplicateDevicePath (DevicePath);
3789   ASSERT (DupDevicePath != NULL);
3790 
3791   UpdatedDevicePath = DupDevicePath;
3792   Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &UpdatedDevicePath, &Handle);
3793   //
3794   // 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
3795   // Acpi()/Pci()/Usb() --> Acpi()/Pci()
3796   //
3797   if ((DevicePathType (UpdatedDevicePath) == MESSAGING_DEVICE_PATH) &&
3798       (DevicePathSubType (UpdatedDevicePath) == MSG_USB_DP)) {
3799     //
3800     // Remove the usb node, let the device path only point to PCI node
3801     //
3802     SetDevicePathEndNode (UpdatedDevicePath);
3803     UpdatedDevicePath = DupDevicePath;
3804   } else {
3805     UpdatedDevicePath = DevicePath;
3806   }
3807 
3808   //
3809   // Get the device path size of boot option
3810   //
3811   Size = GetDevicePathSize(UpdatedDevicePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL); // minus the end node
3812   ReturnHandle = NULL;
3813   gBS->LocateHandleBuffer (
3814       ByProtocol,
3815       &gEfiSimpleFileSystemProtocolGuid,
3816       NULL,
3817       &NumberSimpleFileSystemHandles,
3818       &SimpleFileSystemHandles
3819       );
3820   for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {
3821     //
3822     // Get the device path size of SimpleFileSystem handle
3823     //
3824     TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);
3825     TempSize = GetDevicePathSize (TempDevicePath)- sizeof (EFI_DEVICE_PATH_PROTOCOL); // minus the end node
3826     //
3827     // Check whether the device path of boot option is part of the  SimpleFileSystem handle's device path
3828     //
3829     if (Size <= TempSize && CompareMem (TempDevicePath, UpdatedDevicePath, Size)==0) {
3830       //
3831       // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media
3832       //  machinename is ia32, ia64, x64, ...
3833       //
3834       Hdr.Union = &HdrData;
3835       Status = BdsLibGetImageHeader (
3836                  SimpleFileSystemHandles[Index],
3837                  EFI_REMOVABLE_MEDIA_FILE_NAME,
3838                  &DosHeader,
3839                  Hdr
3840                  );
3841       if (!EFI_ERROR (Status) &&
3842         EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Hdr.Pe32->FileHeader.Machine) &&
3843         Hdr.Pe32->OptionalHeader.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) {
3844         ReturnHandle = SimpleFileSystemHandles[Index];
3845         break;
3846       }
3847     }
3848   }
3849 
3850   FreePool(DupDevicePath);
3851 
3852   if (SimpleFileSystemHandles != NULL) {
3853     FreePool(SimpleFileSystemHandles);
3854   }
3855 
3856   gBS->RestoreTPL (OldTpl);
3857 
3858   return ReturnHandle;
3859 }
3860 
3861 /**
3862   Check to see if the network cable is plugged in. If the DevicePath is not
3863   connected it will be connected.
3864 
3865   @param  DevicePath             Device Path to check
3866 
3867   @retval TRUE                   DevicePath points to an Network that is connected
3868   @retval FALSE                  DevicePath does not point to a bootable network
3869 
3870 **/
3871 BOOLEAN
BdsLibNetworkBootWithMediaPresent(IN EFI_DEVICE_PATH_PROTOCOL * DevicePath)3872 BdsLibNetworkBootWithMediaPresent (
3873   IN  EFI_DEVICE_PATH_PROTOCOL      *DevicePath
3874   )
3875 {
3876   EFI_STATUS                      Status;
3877   EFI_DEVICE_PATH_PROTOCOL        *UpdatedDevicePath;
3878   EFI_HANDLE                      Handle;
3879   EFI_SIMPLE_NETWORK_PROTOCOL     *Snp;
3880   BOOLEAN                         MediaPresent;
3881   UINT32                          InterruptStatus;
3882 
3883   MediaPresent = FALSE;
3884 
3885   UpdatedDevicePath = DevicePath;
3886   //
3887   // Locate Load File Protocol for PXE boot option first
3888   //
3889   Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &UpdatedDevicePath, &Handle);
3890   if (EFI_ERROR (Status)) {
3891     //
3892     // Device not present so see if we need to connect it
3893     //
3894     Status = BdsLibConnectDevicePath (DevicePath);
3895     if (!EFI_ERROR (Status)) {
3896       //
3897       // This one should work after we did the connect
3898       //
3899       Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &UpdatedDevicePath, &Handle);
3900     }
3901   }
3902 
3903   if (!EFI_ERROR (Status)) {
3904     Status = gBS->HandleProtocol (Handle, &gEfiSimpleNetworkProtocolGuid, (VOID **)&Snp);
3905     if (EFI_ERROR (Status)) {
3906       //
3907       // Failed to open SNP from this handle, try to get SNP from parent handle
3908       //
3909       UpdatedDevicePath = DevicePathFromHandle (Handle);
3910       if (UpdatedDevicePath != NULL) {
3911         Status = gBS->LocateDevicePath (&gEfiSimpleNetworkProtocolGuid, &UpdatedDevicePath, &Handle);
3912         if (!EFI_ERROR (Status)) {
3913           //
3914           // SNP handle found, get SNP from it
3915           //
3916           Status = gBS->HandleProtocol (Handle, &gEfiSimpleNetworkProtocolGuid, (VOID **) &Snp);
3917         }
3918       }
3919     }
3920 
3921     if (!EFI_ERROR (Status)) {
3922       if (Snp->Mode->MediaPresentSupported) {
3923         if (Snp->Mode->State == EfiSimpleNetworkInitialized) {
3924           //
3925           // Invoke Snp->GetStatus() to refresh the media status
3926           //
3927           Snp->GetStatus (Snp, &InterruptStatus, NULL);
3928 
3929           //
3930           // In case some one else is using the SNP check to see if it's connected
3931           //
3932           MediaPresent = Snp->Mode->MediaPresent;
3933         } else {
3934           //
3935           // No one is using SNP so we need to Start and Initialize so
3936           // MediaPresent will be valid.
3937           //
3938           Status = Snp->Start (Snp);
3939           if (!EFI_ERROR (Status)) {
3940             Status = Snp->Initialize (Snp, 0, 0);
3941             if (!EFI_ERROR (Status)) {
3942               MediaPresent = Snp->Mode->MediaPresent;
3943               Snp->Shutdown (Snp);
3944             }
3945             Snp->Stop (Snp);
3946           }
3947         }
3948       } else {
3949         MediaPresent = TRUE;
3950       }
3951     }
3952   }
3953 
3954   return MediaPresent;
3955 }
3956 
3957 /**
3958   For a bootable Device path, return its boot type.
3959 
3960   @param  DevicePath                      The bootable device Path to check
3961 
3962   @retval BDS_EFI_MEDIA_HD_BOOT           If given device path contains MEDIA_DEVICE_PATH type device path node
3963                                           which subtype is MEDIA_HARDDRIVE_DP
3964   @retval BDS_EFI_MEDIA_CDROM_BOOT        If given device path contains MEDIA_DEVICE_PATH type device path node
3965                                           which subtype is MEDIA_CDROM_DP
3966   @retval BDS_EFI_ACPI_FLOPPY_BOOT        If given device path contains ACPI_DEVICE_PATH type device path node
3967                                           which HID is floppy device.
3968   @retval BDS_EFI_MESSAGE_ATAPI_BOOT      If given device path contains MESSAGING_DEVICE_PATH type device path node
3969                                           and its last device path node's subtype is MSG_ATAPI_DP.
3970   @retval BDS_EFI_MESSAGE_SCSI_BOOT       If given device path contains MESSAGING_DEVICE_PATH type device path node
3971                                           and its last device path node's subtype is MSG_SCSI_DP.
3972   @retval BDS_EFI_MESSAGE_USB_DEVICE_BOOT If given device path contains MESSAGING_DEVICE_PATH type device path node
3973                                           and its last device path node's subtype is MSG_USB_DP.
3974   @retval BDS_EFI_MESSAGE_MISC_BOOT       If the device path not contains any media device path node,  and
3975                                           its last device path node point to a message device path node.
3976   @retval BDS_LEGACY_BBS_BOOT             If given device path contains BBS_DEVICE_PATH type device path node.
3977   @retval BDS_EFI_UNSUPPORT               An EFI Removable BlockIO device path not point to a media and message device,
3978 
3979 **/
3980 UINT32
3981 EFIAPI
BdsGetBootTypeFromDevicePath(IN EFI_DEVICE_PATH_PROTOCOL * DevicePath)3982 BdsGetBootTypeFromDevicePath (
3983   IN  EFI_DEVICE_PATH_PROTOCOL     *DevicePath
3984   )
3985 {
3986   ACPI_HID_DEVICE_PATH          *Acpi;
3987   EFI_DEVICE_PATH_PROTOCOL      *TempDevicePath;
3988   EFI_DEVICE_PATH_PROTOCOL      *LastDeviceNode;
3989   UINT32                        BootType;
3990 
3991   if (NULL == DevicePath) {
3992     return BDS_EFI_UNSUPPORT;
3993   }
3994 
3995   TempDevicePath = DevicePath;
3996 
3997   while (!IsDevicePathEndType (TempDevicePath)) {
3998     switch (DevicePathType (TempDevicePath)) {
3999       case BBS_DEVICE_PATH:
4000          return BDS_LEGACY_BBS_BOOT;
4001       case MEDIA_DEVICE_PATH:
4002         if (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP) {
4003           return BDS_EFI_MEDIA_HD_BOOT;
4004         } else if (DevicePathSubType (TempDevicePath) == MEDIA_CDROM_DP) {
4005           return BDS_EFI_MEDIA_CDROM_BOOT;
4006         }
4007         break;
4008       case ACPI_DEVICE_PATH:
4009         Acpi = (ACPI_HID_DEVICE_PATH *) TempDevicePath;
4010         if (EISA_ID_TO_NUM (Acpi->HID) == 0x0604) {
4011           return BDS_EFI_ACPI_FLOPPY_BOOT;
4012         }
4013         break;
4014       case MESSAGING_DEVICE_PATH:
4015         //
4016         // Get the last device path node
4017         //
4018         LastDeviceNode = NextDevicePathNode (TempDevicePath);
4019         if (DevicePathSubType(LastDeviceNode) == MSG_DEVICE_LOGICAL_UNIT_DP) {
4020           //
4021           // if the next node type is Device Logical Unit, which specify the Logical Unit Number (LUN),
4022           // skip it
4023           //
4024           LastDeviceNode = NextDevicePathNode (LastDeviceNode);
4025         }
4026         //
4027         // if the device path not only point to driver device, it is not a messaging device path,
4028         //
4029         if (!IsDevicePathEndType (LastDeviceNode)) {
4030           break;
4031         }
4032 
4033         switch (DevicePathSubType (TempDevicePath)) {
4034         case MSG_ATAPI_DP:
4035           BootType = BDS_EFI_MESSAGE_ATAPI_BOOT;
4036           break;
4037 
4038         case MSG_USB_DP:
4039           BootType = BDS_EFI_MESSAGE_USB_DEVICE_BOOT;
4040           break;
4041 
4042         case MSG_SCSI_DP:
4043           BootType = BDS_EFI_MESSAGE_SCSI_BOOT;
4044           break;
4045 
4046         case MSG_SATA_DP:
4047           BootType = BDS_EFI_MESSAGE_SATA_BOOT;
4048           break;
4049 
4050         case MSG_MAC_ADDR_DP:
4051         case MSG_VLAN_DP:
4052         case MSG_IPv4_DP:
4053         case MSG_IPv6_DP:
4054           BootType = BDS_EFI_MESSAGE_MAC_BOOT;
4055           break;
4056 
4057         default:
4058           BootType = BDS_EFI_MESSAGE_MISC_BOOT;
4059           break;
4060         }
4061         return BootType;
4062 
4063       default:
4064         break;
4065     }
4066     TempDevicePath = NextDevicePathNode (TempDevicePath);
4067   }
4068 
4069   return BDS_EFI_UNSUPPORT;
4070 }
4071 
4072 /**
4073   Check whether the Device path in a boot option point to a valid bootable device,
4074   And if CheckMedia is true, check the device is ready to boot now.
4075 
4076   @param  DevPath     the Device path in a boot option
4077   @param  CheckMedia  if true, check the device is ready to boot now.
4078 
4079   @retval TRUE        the Device path  is valid
4080   @retval FALSE       the Device path  is invalid .
4081 
4082 **/
4083 BOOLEAN
4084 EFIAPI
BdsLibIsValidEFIBootOptDevicePath(IN EFI_DEVICE_PATH_PROTOCOL * DevPath,IN BOOLEAN CheckMedia)4085 BdsLibIsValidEFIBootOptDevicePath (
4086   IN EFI_DEVICE_PATH_PROTOCOL     *DevPath,
4087   IN BOOLEAN                      CheckMedia
4088   )
4089 {
4090   return BdsLibIsValidEFIBootOptDevicePathExt (DevPath, CheckMedia, NULL);
4091 }
4092 
4093 /**
4094   Check whether the Device path in a boot option point to a valid bootable device,
4095   And if CheckMedia is true, check the device is ready to boot now.
4096   If Description is not NULL and the device path point to a fixed BlockIo
4097   device, check the description whether conflict with other auto-created
4098   boot options.
4099 
4100   @param  DevPath     the Device path in a boot option
4101   @param  CheckMedia  if true, check the device is ready to boot now.
4102   @param  Description the description in a boot option
4103 
4104   @retval TRUE        the Device path  is valid
4105   @retval FALSE       the Device path  is invalid .
4106 
4107 **/
4108 BOOLEAN
4109 EFIAPI
BdsLibIsValidEFIBootOptDevicePathExt(IN EFI_DEVICE_PATH_PROTOCOL * DevPath,IN BOOLEAN CheckMedia,IN CHAR16 * Description)4110 BdsLibIsValidEFIBootOptDevicePathExt (
4111   IN EFI_DEVICE_PATH_PROTOCOL     *DevPath,
4112   IN BOOLEAN                      CheckMedia,
4113   IN CHAR16                       *Description
4114   )
4115 {
4116   EFI_STATUS                Status;
4117   EFI_HANDLE                Handle;
4118   EFI_DEVICE_PATH_PROTOCOL  *TempDevicePath;
4119   EFI_DEVICE_PATH_PROTOCOL  *LastDeviceNode;
4120   EFI_BLOCK_IO_PROTOCOL     *BlockIo;
4121 
4122   TempDevicePath = DevPath;
4123   LastDeviceNode = DevPath;
4124 
4125   //
4126   // Check if it's a valid boot option for network boot device.
4127   // Check if there is EfiLoadFileProtocol installed.
4128   // If yes, that means there is a boot option for network.
4129   //
4130   Status = gBS->LocateDevicePath (
4131                   &gEfiLoadFileProtocolGuid,
4132                   &TempDevicePath,
4133                   &Handle
4134                   );
4135   if (EFI_ERROR (Status)) {
4136     //
4137     // Device not present so see if we need to connect it
4138     //
4139     TempDevicePath = DevPath;
4140     BdsLibConnectDevicePath (TempDevicePath);
4141     Status = gBS->LocateDevicePath (
4142                     &gEfiLoadFileProtocolGuid,
4143                     &TempDevicePath,
4144                     &Handle
4145                     );
4146   }
4147 
4148   if (!EFI_ERROR (Status)) {
4149     if (!IsDevicePathEnd (TempDevicePath)) {
4150       //
4151       // LoadFile protocol is not installed on handle with exactly the same DevPath
4152       //
4153       return FALSE;
4154     }
4155 
4156     if (CheckMedia) {
4157       //
4158       // Test if it is ready to boot now
4159       //
4160       if (BdsLibNetworkBootWithMediaPresent(DevPath)) {
4161         return TRUE;
4162       }
4163     } else {
4164       return TRUE;
4165     }
4166   }
4167 
4168   //
4169   // If the boot option point to a file, it is a valid EFI boot option,
4170   // and assume it is ready to boot now
4171   //
4172   while (!IsDevicePathEnd (TempDevicePath)) {
4173     //
4174     // If there is USB Class or USB WWID device path node, treat it as valid EFI
4175     // Boot Option. BdsExpandUsbShortFormDevicePath () will be used to expand it
4176     // to full device path.
4177     //
4178     if ((DevicePathType (TempDevicePath) == MESSAGING_DEVICE_PATH) &&
4179         ((DevicePathSubType (TempDevicePath) == MSG_USB_CLASS_DP) ||
4180          (DevicePathSubType (TempDevicePath) == MSG_USB_WWID_DP))) {
4181       return TRUE;
4182     }
4183 
4184     LastDeviceNode = TempDevicePath;
4185     TempDevicePath = NextDevicePathNode (TempDevicePath);
4186   }
4187   if ((DevicePathType (LastDeviceNode) == MEDIA_DEVICE_PATH) &&
4188     (DevicePathSubType (LastDeviceNode) == MEDIA_FILEPATH_DP)) {
4189     return TRUE;
4190   }
4191 
4192   //
4193   // Check if it's a valid boot option for internal FV application
4194   //
4195   if (EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode) != NULL) {
4196     //
4197     // If the boot option point to internal FV application, make sure it is valid
4198     //
4199     TempDevicePath = DevPath;
4200     Status = BdsLibUpdateFvFileDevicePath (
4201                &TempDevicePath,
4202                EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode)
4203                );
4204     if (Status == EFI_ALREADY_STARTED) {
4205       return TRUE;
4206     } else {
4207       if (Status == EFI_SUCCESS) {
4208         FreePool (TempDevicePath);
4209       }
4210       return FALSE;
4211     }
4212   }
4213 
4214   //
4215   // If the boot option point to a blockIO device:
4216   //    if it is a removable blockIo device, it is valid.
4217   //    if it is a fixed blockIo device, check its description confliction.
4218   //
4219   TempDevicePath = DevPath;
4220   Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);
4221   if (EFI_ERROR (Status)) {
4222     //
4223     // Device not present so see if we need to connect it
4224     //
4225     Status = BdsLibConnectDevicePath (DevPath);
4226     if (!EFI_ERROR (Status)) {
4227       //
4228       // Try again to get the Block Io protocol after we did the connect
4229       //
4230       TempDevicePath = DevPath;
4231       Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);
4232     }
4233   }
4234 
4235   if (!EFI_ERROR (Status)) {
4236     Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);
4237     if (!EFI_ERROR (Status)) {
4238       if (CheckMedia) {
4239         //
4240         // Test if it is ready to boot now
4241         //
4242         if (BdsLibGetBootableHandle (DevPath) != NULL) {
4243           return TRUE;
4244         }
4245       } else {
4246         return TRUE;
4247       }
4248     }
4249   } else {
4250     //
4251     // 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,
4252     //
4253     Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle);
4254     if (!EFI_ERROR (Status)) {
4255       if (CheckMedia) {
4256         //
4257         // Test if it is ready to boot now
4258         //
4259         if (BdsLibGetBootableHandle (DevPath) != NULL) {
4260           return TRUE;
4261         }
4262       } else {
4263         return TRUE;
4264       }
4265     }
4266   }
4267 
4268   return FALSE;
4269 }
4270 
4271 
4272 /**
4273   According to a file guild, check a Fv file device path is valid. If it is invalid,
4274   try to return the valid device path.
4275   FV address maybe changes for memory layout adjust from time to time, use this function
4276   could promise the Fv file device path is right.
4277 
4278   @param  DevicePath             on input, the Fv file device path need to check on
4279                                  output, the updated valid Fv file device path
4280   @param  FileGuid               the Fv file guild
4281 
4282   @retval EFI_INVALID_PARAMETER  the input DevicePath or FileGuid is invalid
4283                                  parameter
4284   @retval EFI_UNSUPPORTED        the input DevicePath does not contain Fv file
4285                                  guild at all
4286   @retval EFI_ALREADY_STARTED    the input DevicePath has pointed to Fv file, it is
4287                                  valid
4288   @retval EFI_SUCCESS            has successfully updated the invalid DevicePath,
4289                                  and return the updated device path in DevicePath
4290 
4291 **/
4292 EFI_STATUS
4293 EFIAPI
BdsLibUpdateFvFileDevicePath(IN OUT EFI_DEVICE_PATH_PROTOCOL ** DevicePath,IN EFI_GUID * FileGuid)4294 BdsLibUpdateFvFileDevicePath (
4295   IN  OUT EFI_DEVICE_PATH_PROTOCOL      ** DevicePath,
4296   IN  EFI_GUID                          *FileGuid
4297   )
4298 {
4299   EFI_DEVICE_PATH_PROTOCOL      *TempDevicePath;
4300   EFI_DEVICE_PATH_PROTOCOL      *LastDeviceNode;
4301   EFI_STATUS                    Status;
4302   EFI_GUID                      *GuidPoint;
4303   UINTN                         Index;
4304   UINTN                         FvHandleCount;
4305   EFI_HANDLE                    *FvHandleBuffer;
4306   EFI_FV_FILETYPE               Type;
4307   UINTN                         Size;
4308   EFI_FV_FILE_ATTRIBUTES        Attributes;
4309   UINT32                        AuthenticationStatus;
4310   BOOLEAN                       FindFvFile;
4311   EFI_LOADED_IMAGE_PROTOCOL     *LoadedImage;
4312   EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
4313   MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FvFileNode;
4314   EFI_HANDLE                    FoundFvHandle;
4315   EFI_DEVICE_PATH_PROTOCOL      *NewDevicePath;
4316 
4317   if ((DevicePath == NULL) || (*DevicePath == NULL)) {
4318     return EFI_INVALID_PARAMETER;
4319   }
4320   if (FileGuid == NULL) {
4321     return EFI_INVALID_PARAMETER;
4322   }
4323 
4324   //
4325   // Check whether the device path point to the default the input Fv file
4326   //
4327   TempDevicePath = *DevicePath;
4328   LastDeviceNode = TempDevicePath;
4329   while (!IsDevicePathEnd (TempDevicePath)) {
4330      LastDeviceNode = TempDevicePath;
4331      TempDevicePath = NextDevicePathNode (TempDevicePath);
4332   }
4333   GuidPoint = EfiGetNameGuidFromFwVolDevicePathNode (
4334                 (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode
4335                 );
4336   if (GuidPoint == NULL) {
4337     //
4338     // if this option does not points to a Fv file, just return EFI_UNSUPPORTED
4339     //
4340     return EFI_UNSUPPORTED;
4341   }
4342   if (!CompareGuid (GuidPoint, FileGuid)) {
4343     //
4344     // If the Fv file is not the input file guid, just return EFI_UNSUPPORTED
4345     //
4346     return EFI_UNSUPPORTED;
4347   }
4348 
4349   //
4350   // Check whether the input Fv file device path is valid
4351   //
4352   TempDevicePath = *DevicePath;
4353   FoundFvHandle = NULL;
4354   Status = gBS->LocateDevicePath (
4355                   &gEfiFirmwareVolume2ProtocolGuid,
4356                   &TempDevicePath,
4357                   &FoundFvHandle
4358                   );
4359   if (!EFI_ERROR (Status)) {
4360     Status = gBS->HandleProtocol (
4361                     FoundFvHandle,
4362                     &gEfiFirmwareVolume2ProtocolGuid,
4363                     (VOID **) &Fv
4364                     );
4365     if (!EFI_ERROR (Status)) {
4366       //
4367       // Set FV ReadFile Buffer as NULL, only need to check whether input Fv file exist there
4368       //
4369       Status = Fv->ReadFile (
4370                     Fv,
4371                     FileGuid,
4372                     NULL,
4373                     &Size,
4374                     &Type,
4375                     &Attributes,
4376                     &AuthenticationStatus
4377                     );
4378       if (!EFI_ERROR (Status)) {
4379         return EFI_ALREADY_STARTED;
4380       }
4381     }
4382   }
4383 
4384   //
4385   // Look for the input wanted FV file in current FV
4386   // First, try to look for in Bds own FV. Bds and input wanted FV file usually are in the same FV
4387   //
4388   FindFvFile = FALSE;
4389   FoundFvHandle = NULL;
4390   Status = gBS->HandleProtocol (
4391              gImageHandle,
4392              &gEfiLoadedImageProtocolGuid,
4393              (VOID **) &LoadedImage
4394              );
4395   if (!EFI_ERROR (Status)) {
4396     Status = gBS->HandleProtocol (
4397                     LoadedImage->DeviceHandle,
4398                     &gEfiFirmwareVolume2ProtocolGuid,
4399                     (VOID **) &Fv
4400                     );
4401     if (!EFI_ERROR (Status)) {
4402       Status = Fv->ReadFile (
4403                     Fv,
4404                     FileGuid,
4405                     NULL,
4406                     &Size,
4407                     &Type,
4408                     &Attributes,
4409                     &AuthenticationStatus
4410                     );
4411       if (!EFI_ERROR (Status)) {
4412         FindFvFile = TRUE;
4413         FoundFvHandle = LoadedImage->DeviceHandle;
4414       }
4415     }
4416   }
4417   //
4418   // Second, if fail to find, try to enumerate all FV
4419   //
4420   if (!FindFvFile) {
4421     FvHandleBuffer = NULL;
4422     gBS->LocateHandleBuffer (
4423           ByProtocol,
4424           &gEfiFirmwareVolume2ProtocolGuid,
4425           NULL,
4426           &FvHandleCount,
4427           &FvHandleBuffer
4428           );
4429     for (Index = 0; Index < FvHandleCount; Index++) {
4430       gBS->HandleProtocol (
4431             FvHandleBuffer[Index],
4432             &gEfiFirmwareVolume2ProtocolGuid,
4433             (VOID **) &Fv
4434             );
4435 
4436       Status = Fv->ReadFile (
4437                     Fv,
4438                     FileGuid,
4439                     NULL,
4440                     &Size,
4441                     &Type,
4442                     &Attributes,
4443                     &AuthenticationStatus
4444                     );
4445       if (EFI_ERROR (Status)) {
4446         //
4447         // Skip if input Fv file not in the FV
4448         //
4449         continue;
4450       }
4451       FindFvFile = TRUE;
4452       FoundFvHandle = FvHandleBuffer[Index];
4453       break;
4454     }
4455 
4456     if (FvHandleBuffer != NULL) {
4457       FreePool (FvHandleBuffer);
4458     }
4459   }
4460 
4461   if (FindFvFile) {
4462     //
4463     // Build the shell device path
4464     //
4465     NewDevicePath = DevicePathFromHandle (FoundFvHandle);
4466     EfiInitializeFwVolDevicepathNode (&FvFileNode, FileGuid);
4467     NewDevicePath = AppendDevicePathNode (NewDevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &FvFileNode);
4468     ASSERT (NewDevicePath != NULL);
4469     *DevicePath = NewDevicePath;
4470     return EFI_SUCCESS;
4471   }
4472   return EFI_NOT_FOUND;
4473 }
4474