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