1 /** @file
2   This function deal with the legacy boot option, it create, delete
3   and manage the legacy boot option, all legacy boot option is getting from
4   the legacy BBS table.
5 
6 Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution.  The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11 
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14 
15 **/
16 
17 #include "InternalLegacyBm.h"
18 
19 #define  LEGACY_BM_BOOT_DESCRIPTION_LENGTH  32
20 
21 /**
22   Initialize legacy boot manager library by call EfiBootManagerRegisterLegacyBootSupport
23   function to export two function pointer.
24 
25   @param ImageHandle     The image handle.
26   @param SystemTable     The system table.
27 
28   @retval EFI_SUCCESS    The legacy boot manager library is initialized correctly.
29   @return Other value if failed to initialize the legacy boot manager library.
30 **/
31 EFI_STATUS
32 EFIAPI
LegacyBootManagerLibConstructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)33 LegacyBootManagerLibConstructor (
34   IN EFI_HANDLE                            ImageHandle,
35   IN EFI_SYSTEM_TABLE                      *SystemTable
36 )
37 {
38   EfiBootManagerRegisterLegacyBootSupport (
39     LegacyBmRefreshAllBootOption,
40     LegacyBmBoot
41     );
42   return EFI_SUCCESS;
43 }
44 
45 /**
46   Get the device type from the input legacy device path.
47 
48   @param DevicePath     The legacy device path.
49 
50   @retval               The legacy device type.
51 **/
52 UINT16
LegacyBmDeviceType(EFI_DEVICE_PATH_PROTOCOL * DevicePath)53 LegacyBmDeviceType (
54   EFI_DEVICE_PATH_PROTOCOL *DevicePath
55   )
56 {
57   ASSERT ((DevicePathType (DevicePath) == BBS_DEVICE_PATH) &&
58           (DevicePathSubType (DevicePath) == BBS_BBS_DP));
59   return ((BBS_BBS_DEVICE_PATH *) DevicePath)->DeviceType;
60 }
61 
62 /**
63   Validate the BbsEntry base on the Boot Priority info in the BbsEntry.
64 
65   @param BbsEntry       The input bbs entry info.
66 
67   @retval TRUE          The BbsEntry is valid.
68   @retval FALSE         The BbsEntry is invalid.
69 **/
70 BOOLEAN
LegacyBmValidBbsEntry(IN BBS_TABLE * BbsEntry)71 LegacyBmValidBbsEntry (
72   IN BBS_TABLE   *BbsEntry
73   )
74 {
75   switch (BbsEntry->BootPriority) {
76     case BBS_IGNORE_ENTRY:
77     case BBS_DO_NOT_BOOT_FROM:
78     case BBS_LOWEST_PRIORITY:
79       return FALSE;
80     default:
81       return TRUE;
82   }
83 }
84 
85 /**
86   Build Legacy Device Name String according.
87 
88   @param CurBBSEntry     BBS Table.
89   @param Index           Index.
90   @param BufSize         The buffer size.
91   @param BootString      The output string.
92 
93 **/
94 VOID
LegacyBmBuildLegacyDevNameString(IN BBS_TABLE * CurBBSEntry,IN UINTN Index,IN UINTN BufSize,OUT CHAR16 * BootString)95 LegacyBmBuildLegacyDevNameString (
96   IN  BBS_TABLE                 *CurBBSEntry,
97   IN  UINTN                     Index,
98   IN  UINTN                     BufSize,
99   OUT CHAR16                    *BootString
100   )
101 {
102   CHAR16  *Fmt;
103   CHAR16  *Type;
104   CHAR8   *StringDesc;
105   CHAR8   StringBufferA[LEGACY_BM_BOOT_DESCRIPTION_LENGTH + 1];
106   CHAR16  StringBufferU[LEGACY_BM_BOOT_DESCRIPTION_LENGTH + 1];
107 
108   switch (Index) {
109   //
110   // Primary Master
111   //
112   case 1:
113     Fmt = L"Primary Master %s";
114     break;
115 
116  //
117  // Primary Slave
118  //
119   case 2:
120     Fmt = L"Primary Slave %s";
121     break;
122 
123   //
124   // Secondary Master
125   //
126   case 3:
127     Fmt = L"Secondary Master %s";
128     break;
129 
130   //
131   // Secondary Slave
132   //
133   case 4:
134     Fmt = L"Secondary Slave %s";
135     break;
136 
137   default:
138     Fmt = L"%s";
139     break;
140   }
141 
142   switch (CurBBSEntry->DeviceType) {
143   case BBS_FLOPPY:
144     Type = L"Floppy";
145     break;
146 
147   case BBS_HARDDISK:
148     Type = L"Harddisk";
149     break;
150 
151   case BBS_CDROM:
152     Type = L"CDROM";
153     break;
154 
155   case BBS_PCMCIA:
156     Type = L"PCMCIAe";
157     break;
158 
159   case BBS_USB:
160     Type = L"USB";
161     break;
162 
163   case BBS_EMBED_NETWORK:
164     Type = L"Network";
165     break;
166 
167   case BBS_BEV_DEVICE:
168     Type = L"BEVe";
169     break;
170 
171   case BBS_UNKNOWN:
172   default:
173     Type = L"Unknown";
174     break;
175   }
176   //
177   // If current BBS entry has its description then use it.
178   //
179   StringDesc = (CHAR8 *) (UINTN) ((CurBBSEntry->DescStringSegment << 4) + CurBBSEntry->DescStringOffset);
180   if (NULL != StringDesc) {
181     //
182     // Only get fisrt 32 characters, this is suggested by BBS spec
183     //
184     CopyMem (StringBufferA, StringDesc, LEGACY_BM_BOOT_DESCRIPTION_LENGTH);
185     StringBufferA[LEGACY_BM_BOOT_DESCRIPTION_LENGTH] = 0;
186     AsciiStrToUnicodeStrS (StringBufferA, StringBufferU, ARRAY_SIZE (StringBufferU));
187     Fmt   = L"%s";
188     Type  = StringBufferU;
189   }
190 
191   //
192   // BbsTable 16 entries are for onboard IDE.
193   // Set description string for SATA harddisks, Harddisk 0 ~ Harddisk 11
194   //
195   if (Index >= 5 && Index <= 16 && (CurBBSEntry->DeviceType == BBS_HARDDISK || CurBBSEntry->DeviceType == BBS_CDROM)) {
196     Fmt = L"%s %d";
197     UnicodeSPrint (BootString, BufSize, Fmt, Type, Index - 5);
198   } else {
199     UnicodeSPrint (BootString, BufSize, Fmt, Type);
200   }
201 }
202 
203 /**
204   Get the Bbs index for the input boot option.
205 
206   @param BootOption     The input boot option info.
207   @param BbsTable       The input Bbs table.
208   @param BbsCount       The input total bbs entry number.
209   @param BbsIndexUsed   The array shows how many BBS table indexs have been used.
210 
211   @retval The index for the input boot option.
212 **/
213 UINT16
LegacyBmFuzzyMatch(EFI_BOOT_MANAGER_LOAD_OPTION * BootOption,BBS_TABLE * BbsTable,UINT16 BbsCount,BOOLEAN * BbsIndexUsed)214 LegacyBmFuzzyMatch (
215   EFI_BOOT_MANAGER_LOAD_OPTION   *BootOption,
216   BBS_TABLE                      *BbsTable,
217   UINT16                         BbsCount,
218   BOOLEAN                        *BbsIndexUsed
219   )
220 {
221   UINT16                         Index;
222   LEGACY_BM_BOOT_OPTION_BBS_DATA *BbsData;
223   CHAR16                         Description[LEGACY_BM_BOOT_DESCRIPTION_LENGTH + 1];
224 
225   BbsData = (LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption->OptionalData;
226 
227   //
228   // Directly check the BBS index stored in BootOption
229   //
230   if ((BbsData->BbsIndex < BbsCount) &&
231       (LegacyBmDeviceType (BootOption->FilePath) == BbsTable[BbsData->BbsIndex].DeviceType)) {
232     LegacyBmBuildLegacyDevNameString (
233       &BbsTable[BbsData->BbsIndex],
234       BbsData->BbsIndex,
235       sizeof (Description),
236       Description
237       );
238     if ((StrCmp (Description, BootOption->Description) == 0) && !BbsIndexUsed[BbsData->BbsIndex]) {
239       //
240       // If devices with the same description string are connected,
241       // the BbsIndex of the first device is returned for the other device also.
242       // So, check if the BbsIndex is already being used, before assigning the BbsIndex.
243       //
244       BbsIndexUsed[BbsData->BbsIndex] = TRUE;
245       return BbsData->BbsIndex;
246     }
247   }
248 
249   //
250   // BBS table could be changed (entry removed/moved)
251   // find the correct BBS index
252   //
253   for (Index = 0; Index < BbsCount; Index++) {
254     if (!LegacyBmValidBbsEntry (&BbsTable[Index]) ||
255         (BbsTable[Index].DeviceType != LegacyBmDeviceType (BootOption->FilePath))) {
256       continue;
257     }
258 
259     LegacyBmBuildLegacyDevNameString (
260       &BbsTable[Index],
261       Index,
262       sizeof (Description),
263       Description
264       );
265     if ((StrCmp (Description, BootOption->Description) == 0) && !BbsIndexUsed[Index]) {
266       //
267       // If devices with the same description string are connected,
268       // the BbsIndex of the first device is assigned for the other device also.
269       // So, check if the BbsIndex is already being used, before assigning the corrected BbsIndex.
270       //
271       break;
272     }
273   }
274 
275   //
276   // Add the corrected BbsIndex in the UsedBbsIndex Buffer
277   //
278   if (Index != BbsCount) {
279     BbsIndexUsed[Index] = TRUE;
280   }
281 
282   return Index;
283 }
284 
285 /**
286 
287   Update legacy device order base on the input info.
288 
289   @param   LegacyDevOrder     Legacy device order data buffer.
290   @param   LegacyDevOrderSize Legacy device order data buffer size.
291   @param   DeviceType         Device type which need to check.
292   @param   OldBbsIndex        Old Bds Index.
293   @param   NewBbsIndex        New Bds Index, if it is -1,means remove this option.
294 
295 **/
296 VOID
LegacyBmUpdateBbsIndex(LEGACY_DEV_ORDER_ENTRY * LegacyDevOrder,UINTN * LegacyDevOrderSize,UINT16 DeviceType,UINT16 OldBbsIndex,UINT16 NewBbsIndex)297 LegacyBmUpdateBbsIndex (
298   LEGACY_DEV_ORDER_ENTRY   *LegacyDevOrder,
299   UINTN                    *LegacyDevOrderSize,
300   UINT16                   DeviceType,
301   UINT16                   OldBbsIndex,
302   UINT16                   NewBbsIndex // Delete entry if -1
303   )
304 {
305   LEGACY_DEV_ORDER_ENTRY   *Entry;
306   UINTN                    Index;
307 
308   ASSERT (((LegacyDevOrder == NULL) && (*LegacyDevOrderSize == 0)) ||
309           ((LegacyDevOrder != NULL) && (*LegacyDevOrderSize != 0))
310          );
311 
312   for (Entry = LegacyDevOrder;
313        Entry < (LEGACY_DEV_ORDER_ENTRY *) ((UINT8 *) LegacyDevOrder + *LegacyDevOrderSize);
314        Entry = (LEGACY_DEV_ORDER_ENTRY *) ((UINTN) Entry + sizeof (BBS_TYPE) + Entry->Length)
315        ) {
316     if (Entry->BbsType == DeviceType) {
317       for (Index = 0; Index < Entry->Length / sizeof (UINT16) - 1; Index++) {
318         if (Entry->Data[Index] == OldBbsIndex) {
319           if (NewBbsIndex == (UINT16) -1) {
320             //
321             // Delete the old entry
322             //
323             CopyMem (
324               &Entry->Data[Index],
325               &Entry->Data[Index + 1],
326               (UINT8 *) LegacyDevOrder + *LegacyDevOrderSize - (UINT8 *) &Entry->Data[Index + 1]
327               );
328             Entry->Length       -= sizeof (UINT16);
329             *LegacyDevOrderSize -= sizeof(UINT16);
330           } else {
331             Entry->Data[Index]   = NewBbsIndex;
332           }
333           break;
334         }
335       }
336       break;
337     }
338   }
339 }
340 
341 /**
342   Delete all the legacy boot options.
343 
344   @retval EFI_SUCCESS            All legacy boot options are deleted.
345 **/
346 EFI_STATUS
LegacyBmDeleteAllBootOptions(VOID)347 LegacyBmDeleteAllBootOptions (
348   VOID
349   )
350 {
351   EFI_STATUS                    Status;
352   UINTN                         Index;
353   EFI_BOOT_MANAGER_LOAD_OPTION  *BootOption;
354   UINTN                         BootOptionCount;
355 
356   BootOption = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
357   for (Index = 0; Index < BootOptionCount; Index++) {
358     if ((DevicePathType (BootOption[Index].FilePath) == BBS_DEVICE_PATH) &&
359         (DevicePathSubType (BootOption[Index].FilePath) == BBS_BBS_DP)) {
360       Status = EfiBootManagerDeleteLoadOptionVariable (BootOption[Index].OptionNumber, BootOption[Index].OptionType);
361       //
362       // Deleting variable with current variable implementation shouldn't fail.
363       //
364       ASSERT_EFI_ERROR (Status);
365     }
366   }
367 
368   Status = gRT->SetVariable (
369                   VAR_LEGACY_DEV_ORDER,
370                   &gEfiLegacyDevOrderVariableGuid,
371                   0,
372                   0,
373                   NULL
374                   );
375   //
376   // Deleting variable with current variable implementation shouldn't fail.
377   //
378   ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);
379 
380   return EFI_SUCCESS;
381 }
382 
383 
384 /**
385   Delete all the invalid legacy boot options.
386 
387   @retval EFI_SUCCESS             All invalide legacy boot options are deleted.
388   @retval EFI_OUT_OF_RESOURCES    Fail to allocate necessary memory.
389   @retval EFI_NOT_FOUND           Fail to retrive variable of boot order.
390 **/
391 EFI_STATUS
LegacyBmDeleteAllInvalidBootOptions(VOID)392 LegacyBmDeleteAllInvalidBootOptions (
393   VOID
394   )
395 {
396   EFI_STATUS                    Status;
397   UINT16                        HddCount;
398   UINT16                        BbsCount;
399   HDD_INFO                      *HddInfo;
400   BBS_TABLE                     *BbsTable;
401   UINT16                        BbsIndex;
402   EFI_LEGACY_BIOS_PROTOCOL      *LegacyBios;
403   UINTN                         Index;
404   EFI_BOOT_MANAGER_LOAD_OPTION  *BootOption;
405   UINTN                         BootOptionCount;
406   LEGACY_DEV_ORDER_ENTRY        *LegacyDevOrder;
407   UINTN                         LegacyDevOrderSize;
408   BOOLEAN                       *BbsIndexUsed;
409 
410   HddCount      = 0;
411   BbsCount      = 0;
412   HddInfo       = NULL;
413   BbsTable      = NULL;
414 
415   Status        = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
416   if (EFI_ERROR (Status)) {
417     return Status;
418   }
419 
420   Status = LegacyBios->GetBbsInfo (
421                          LegacyBios,
422                          &HddCount,
423                          &HddInfo,
424                          &BbsCount,
425                          &BbsTable
426                          );
427   if (EFI_ERROR (Status)) {
428     return Status;
429   }
430 
431   GetVariable2 (VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, (VOID **) &LegacyDevOrder, &LegacyDevOrderSize);
432 
433   BootOption = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
434 
435   BbsIndexUsed = AllocateZeroPool (BbsCount * sizeof (BOOLEAN));
436   ASSERT (BbsIndexUsed != NULL);
437 
438   for (Index = 0; Index < BootOptionCount; Index++) {
439     //
440     // Skip non legacy boot option
441     //
442     if ((DevicePathType (BootOption[Index].FilePath) != BBS_DEVICE_PATH) ||
443         (DevicePathSubType (BootOption[Index].FilePath) != BBS_BBS_DP)) {
444       continue;
445     }
446 
447     BbsIndex = LegacyBmFuzzyMatch (&BootOption[Index], BbsTable, BbsCount, BbsIndexUsed);
448     if (BbsIndex == BbsCount) {
449       DEBUG ((EFI_D_INFO, "[LegacyBds] Delete Boot Option Boot%04x: %s\n", (UINTN) BootOption[Index].OptionNumber, BootOption[Index].Description));
450       //
451       // Delete entry from LegacyDevOrder
452       //
453       LegacyBmUpdateBbsIndex (
454         LegacyDevOrder,
455         &LegacyDevOrderSize,
456         LegacyBmDeviceType (BootOption[Index].FilePath),
457         ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex,
458         (UINT16) -1
459         );
460       EfiBootManagerDeleteLoadOptionVariable (BootOption[Index].OptionNumber, BootOption[Index].OptionType);
461     } else {
462       if (((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex != BbsIndex) {
463         DEBUG ((EFI_D_INFO, "[LegacyBds] Update Boot Option Boot%04x: %s Bbs0x%04x->Bbs0x%04x\n", (UINTN) BootOption[Index].OptionNumber, BootOption[Index].Description,
464                 (UINTN) ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex, (UINTN) BbsIndex));
465         //
466         // Update the BBS index in LegacyDevOrder
467         //
468         LegacyBmUpdateBbsIndex (
469           LegacyDevOrder,
470           &LegacyDevOrderSize,
471           LegacyBmDeviceType (BootOption[Index].FilePath),
472           ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex,
473           BbsIndex
474           );
475 
476         //
477         // Update the OptionalData in the Boot#### variable
478         //
479         ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex = BbsIndex;
480         EfiBootManagerLoadOptionToVariable (&BootOption[Index]);
481       }
482     }
483   }
484   EfiBootManagerFreeLoadOptions (BootOption, BootOptionCount);
485 
486   if (LegacyDevOrder != NULL) {
487     Status = gRT->SetVariable (
488                     VAR_LEGACY_DEV_ORDER,
489                     &gEfiLegacyDevOrderVariableGuid,
490                     EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
491                     LegacyDevOrderSize,
492                     LegacyDevOrder
493                     );
494     //
495     // Shrink variable with current variable implementation shouldn't fail.
496     //
497     ASSERT_EFI_ERROR (Status);
498 
499     FreePool (LegacyDevOrder);
500   }
501   FreePool(BbsIndexUsed);
502   return Status;
503 }
504 
505 /**
506   Create legacy boot option.
507 
508   @param BootOption        Ponter to the boot option which will be crated.
509   @param BbsEntry          The input bbs entry info.
510   @param BbsIndex          The BBS index.
511 
512   @retval EFI_SUCCESS            Create legacy boot option successfully.
513   @retval EFI_INVALID_PARAMETER  Invalid input parameter.
514 
515 **/
516 EFI_STATUS
LegacyBmCreateLegacyBootOption(IN OUT EFI_BOOT_MANAGER_LOAD_OPTION * BootOption,IN BBS_TABLE * BbsEntry,IN UINT16 BbsIndex)517 LegacyBmCreateLegacyBootOption (
518   IN OUT EFI_BOOT_MANAGER_LOAD_OPTION  *BootOption,
519   IN BBS_TABLE                         *BbsEntry,
520   IN UINT16                            BbsIndex
521   )
522 {
523   EFI_STATUS                   Status;
524   EFI_DEVICE_PATH_PROTOCOL     *DevicePath;
525   CHAR16                       Description[LEGACY_BM_BOOT_DESCRIPTION_LENGTH + 1];
526   CHAR8                        HelpString[LEGACY_BM_BOOT_DESCRIPTION_LENGTH + 1];
527   UINTN                        StringLen;
528   LEGACY_BM_BOOT_OPTION_BBS_DATA  *OptionalData;
529   BBS_BBS_DEVICE_PATH          *BbsNode;
530 
531   if ((BootOption == NULL) || (BbsEntry == NULL)) {
532     return EFI_INVALID_PARAMETER;
533   }
534 
535   LegacyBmBuildLegacyDevNameString (BbsEntry, BbsIndex, sizeof (Description), Description);
536 
537   //
538   // Create the BBS device path with description string
539   //
540   UnicodeStrToAsciiStrS (Description, HelpString, sizeof (HelpString));
541   StringLen = AsciiStrLen (HelpString);
542   DevicePath = AllocatePool (sizeof (BBS_BBS_DEVICE_PATH) + StringLen + END_DEVICE_PATH_LENGTH);
543   ASSERT (DevicePath != NULL);
544 
545   BbsNode = (BBS_BBS_DEVICE_PATH *) DevicePath;
546   SetDevicePathNodeLength (BbsNode, sizeof (BBS_BBS_DEVICE_PATH) + StringLen);
547   BbsNode->Header.Type    = BBS_DEVICE_PATH;
548   BbsNode->Header.SubType = BBS_BBS_DP;
549   BbsNode->DeviceType     = BbsEntry->DeviceType;
550   CopyMem (&BbsNode->StatusFlag, &BbsEntry->StatusFlags, sizeof (BBS_STATUS_FLAGS));
551   CopyMem (BbsNode->String, HelpString, StringLen + 1);
552 
553   SetDevicePathEndNode (NextDevicePathNode (BbsNode));
554 
555   //
556   // Create the OptionalData
557   //
558   OptionalData = AllocatePool (sizeof (LEGACY_BM_BOOT_OPTION_BBS_DATA));
559   ASSERT (OptionalData != NULL);
560   OptionalData->BbsIndex = BbsIndex;
561 
562   //
563   // Create the BootOption
564   //
565   Status = EfiBootManagerInitializeLoadOption (
566              BootOption,
567              LoadOptionNumberUnassigned,
568              LoadOptionTypeBoot,
569              LOAD_OPTION_ACTIVE,
570              Description,
571              DevicePath,
572              (UINT8 *) OptionalData,
573              sizeof (LEGACY_BM_BOOT_OPTION_BBS_DATA)
574              );
575   FreePool (DevicePath);
576   FreePool (OptionalData);
577 
578   return Status;
579 }
580 
581 /**
582   Fill the device order buffer.
583 
584   @param BbsTable        The BBS table.
585   @param BbsType         The BBS Type.
586   @param BbsCount        The BBS Count.
587   @param Buf             device order buffer.
588 
589   @return The device order buffer.
590 
591 **/
592 UINT16 *
LegacyBmFillDevOrderBuf(IN BBS_TABLE * BbsTable,IN BBS_TYPE BbsType,IN UINTN BbsCount,OUT UINT16 * Buf)593 LegacyBmFillDevOrderBuf (
594   IN BBS_TABLE                    *BbsTable,
595   IN BBS_TYPE                     BbsType,
596   IN UINTN                        BbsCount,
597   OUT UINT16                      *Buf
598   )
599 {
600   UINTN Index;
601 
602   for (Index = 0; Index < BbsCount; Index++) {
603     if (!LegacyBmValidBbsEntry (&BbsTable[Index])) {
604       continue;
605     }
606 
607     if (BbsTable[Index].DeviceType != BbsType) {
608       continue;
609     }
610 
611     *Buf = (UINT16) (Index & 0xFF);
612     Buf++;
613   }
614 
615   return Buf;
616 }
617 
618 /**
619   Create the device order buffer.
620 
621   @param BbsTable        The BBS table.
622   @param BbsCount        The BBS Count.
623 
624   @retval EFI_SUCCES             The buffer is created and the EFI variable named
625                                  VAR_LEGACY_DEV_ORDER and EfiLegacyDevOrderGuid is
626                                  set correctly.
627   @retval EFI_OUT_OF_RESOURCES   Memmory or storage is not enough.
628   @retval EFI_DEVICE_ERROR       Fail to add the device order into EFI variable fail
629                                  because of hardware error.
630 **/
631 EFI_STATUS
LegacyBmCreateDevOrder(IN BBS_TABLE * BbsTable,IN UINT16 BbsCount)632 LegacyBmCreateDevOrder (
633   IN BBS_TABLE                  *BbsTable,
634   IN UINT16                     BbsCount
635   )
636 {
637   UINTN                       Index;
638   UINTN                       FDCount;
639   UINTN                       HDCount;
640   UINTN                       CDCount;
641   UINTN                       NETCount;
642   UINTN                       BEVCount;
643   UINTN                       TotalSize;
644   UINTN                       HeaderSize;
645   LEGACY_DEV_ORDER_ENTRY      *DevOrder;
646   LEGACY_DEV_ORDER_ENTRY      *DevOrderPtr;
647   EFI_STATUS                  Status;
648 
649   FDCount     = 0;
650   HDCount     = 0;
651   CDCount     = 0;
652   NETCount    = 0;
653   BEVCount    = 0;
654   TotalSize   = 0;
655   HeaderSize  = sizeof (BBS_TYPE) + sizeof (UINT16);
656   DevOrder    = NULL;
657   Status      = EFI_SUCCESS;
658 
659   //
660   // Count all boot devices
661   //
662   for (Index = 0; Index < BbsCount; Index++) {
663     if (!LegacyBmValidBbsEntry (&BbsTable[Index])) {
664       continue;
665     }
666 
667     switch (BbsTable[Index].DeviceType) {
668     case BBS_FLOPPY:
669       FDCount++;
670       break;
671 
672     case BBS_HARDDISK:
673       HDCount++;
674       break;
675 
676     case BBS_CDROM:
677       CDCount++;
678       break;
679 
680     case BBS_EMBED_NETWORK:
681       NETCount++;
682       break;
683 
684     case BBS_BEV_DEVICE:
685       BEVCount++;
686       break;
687 
688     default:
689       break;
690     }
691   }
692 
693   TotalSize += (HeaderSize + sizeof (UINT16) * FDCount);
694   TotalSize += (HeaderSize + sizeof (UINT16) * HDCount);
695   TotalSize += (HeaderSize + sizeof (UINT16) * CDCount);
696   TotalSize += (HeaderSize + sizeof (UINT16) * NETCount);
697   TotalSize += (HeaderSize + sizeof (UINT16) * BEVCount);
698 
699   //
700   // Create buffer to hold all boot device order
701   //
702   DevOrder = AllocateZeroPool (TotalSize);
703   if (NULL == DevOrder) {
704     return EFI_OUT_OF_RESOURCES;
705   }
706   DevOrderPtr          = DevOrder;
707 
708   DevOrderPtr->BbsType = BBS_FLOPPY;
709   DevOrderPtr->Length  = (UINT16) (sizeof (DevOrderPtr->Length) + FDCount * sizeof (UINT16));
710   DevOrderPtr          = (LEGACY_DEV_ORDER_ENTRY *) LegacyBmFillDevOrderBuf (BbsTable, BBS_FLOPPY, BbsCount, DevOrderPtr->Data);
711 
712   DevOrderPtr->BbsType = BBS_HARDDISK;
713   DevOrderPtr->Length  = (UINT16) (sizeof (UINT16) + HDCount * sizeof (UINT16));
714   DevOrderPtr          = (LEGACY_DEV_ORDER_ENTRY *) LegacyBmFillDevOrderBuf (BbsTable, BBS_HARDDISK, BbsCount, DevOrderPtr->Data);
715 
716   DevOrderPtr->BbsType = BBS_CDROM;
717   DevOrderPtr->Length  = (UINT16) (sizeof (UINT16) + CDCount * sizeof (UINT16));
718   DevOrderPtr          = (LEGACY_DEV_ORDER_ENTRY *) LegacyBmFillDevOrderBuf (BbsTable, BBS_CDROM, BbsCount, DevOrderPtr->Data);
719 
720   DevOrderPtr->BbsType = BBS_EMBED_NETWORK;
721   DevOrderPtr->Length  = (UINT16) (sizeof (UINT16) + NETCount * sizeof (UINT16));
722   DevOrderPtr          = (LEGACY_DEV_ORDER_ENTRY *) LegacyBmFillDevOrderBuf (BbsTable, BBS_EMBED_NETWORK, BbsCount, DevOrderPtr->Data);
723 
724   DevOrderPtr->BbsType = BBS_BEV_DEVICE;
725   DevOrderPtr->Length  = (UINT16) (sizeof (UINT16) + BEVCount * sizeof (UINT16));
726   DevOrderPtr          = (LEGACY_DEV_ORDER_ENTRY *) LegacyBmFillDevOrderBuf (BbsTable, BBS_BEV_DEVICE, BbsCount, DevOrderPtr->Data);
727 
728   ASSERT (TotalSize == (UINTN) ((UINT8 *) DevOrderPtr - (UINT8 *) DevOrder));
729 
730   //
731   // Save device order for legacy boot device to variable.
732   //
733   Status = gRT->SetVariable (
734                   VAR_LEGACY_DEV_ORDER,
735                   &gEfiLegacyDevOrderVariableGuid,
736                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
737                   TotalSize,
738                   DevOrder
739                   );
740   FreePool (DevOrder);
741 
742   return Status;
743 }
744 
745 /**
746   Add the legacy boot devices from BBS table into
747   the legacy device boot order.
748 
749   @retval EFI_SUCCESS           The boot devices are added successfully.
750   @retval EFI_NOT_FOUND         The legacy boot devices are not found.
751   @retval EFI_OUT_OF_RESOURCES  Memmory or storage is not enough.
752   @retval EFI_DEVICE_ERROR      Fail to add the legacy device boot order into EFI variable
753                                 because of hardware error.
754 **/
755 EFI_STATUS
LegacyBmUpdateDevOrder(VOID)756 LegacyBmUpdateDevOrder (
757   VOID
758   )
759 {
760   LEGACY_DEV_ORDER_ENTRY      *DevOrder;
761   LEGACY_DEV_ORDER_ENTRY      *NewDevOrder;
762   LEGACY_DEV_ORDER_ENTRY      *Ptr;
763   LEGACY_DEV_ORDER_ENTRY      *NewPtr;
764   EFI_LEGACY_BIOS_PROTOCOL    *LegacyBios;
765   EFI_STATUS                  Status;
766   UINT16                      HddCount;
767   UINT16                      BbsCount;
768   HDD_INFO                    *LocalHddInfo;
769   BBS_TABLE                   *LocalBbsTable;
770   UINTN                       Index;
771   UINTN                       Index2;
772   UINTN                       *Idx;
773   UINTN                       FDCount;
774   UINTN                       HDCount;
775   UINTN                       CDCount;
776   UINTN                       NETCount;
777   UINTN                       BEVCount;
778   UINTN                       TotalSize;
779   UINTN                       HeaderSize;
780   UINT16                      *NewFDPtr;
781   UINT16                      *NewHDPtr;
782   UINT16                      *NewCDPtr;
783   UINT16                      *NewNETPtr;
784   UINT16                      *NewBEVPtr;
785   UINT16                      *NewDevPtr;
786   UINTN                       FDIndex;
787   UINTN                       HDIndex;
788   UINTN                       CDIndex;
789   UINTN                       NETIndex;
790   UINTN                       BEVIndex;
791 
792   Idx           = NULL;
793   FDCount       = 0;
794   HDCount       = 0;
795   CDCount       = 0;
796   NETCount      = 0;
797   BEVCount      = 0;
798   TotalSize     = 0;
799   HeaderSize    = sizeof (BBS_TYPE) + sizeof (UINT16);
800   FDIndex       = 0;
801   HDIndex       = 0;
802   CDIndex       = 0;
803   NETIndex      = 0;
804   BEVIndex      = 0;
805   NewDevPtr     = NULL;
806 
807   Status        = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
808   if (EFI_ERROR (Status)) {
809     return Status;
810   }
811 
812   Status = LegacyBios->GetBbsInfo (
813                          LegacyBios,
814                          &HddCount,
815                          &LocalHddInfo,
816                          &BbsCount,
817                          &LocalBbsTable
818                          );
819   if (EFI_ERROR (Status)) {
820     return Status;
821   }
822 
823   GetVariable2 (VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, (VOID **) &DevOrder, NULL);
824   if (NULL == DevOrder) {
825     return LegacyBmCreateDevOrder (LocalBbsTable, BbsCount);
826   }
827   //
828   // First we figure out how many boot devices with same device type respectively
829   //
830   for (Index = 0; Index < BbsCount; Index++) {
831     if (!LegacyBmValidBbsEntry (&LocalBbsTable[Index])) {
832       continue;
833     }
834 
835     switch (LocalBbsTable[Index].DeviceType) {
836     case BBS_FLOPPY:
837       FDCount++;
838       break;
839 
840     case BBS_HARDDISK:
841       HDCount++;
842       break;
843 
844     case BBS_CDROM:
845       CDCount++;
846       break;
847 
848     case BBS_EMBED_NETWORK:
849       NETCount++;
850       break;
851 
852     case BBS_BEV_DEVICE:
853       BEVCount++;
854       break;
855 
856     default:
857       break;
858     }
859   }
860 
861   TotalSize += (HeaderSize + FDCount * sizeof (UINT16));
862   TotalSize += (HeaderSize + HDCount * sizeof (UINT16));
863   TotalSize += (HeaderSize + CDCount * sizeof (UINT16));
864   TotalSize += (HeaderSize + NETCount * sizeof (UINT16));
865   TotalSize += (HeaderSize + BEVCount * sizeof (UINT16));
866 
867   NewDevOrder = AllocateZeroPool (TotalSize);
868   if (NULL == NewDevOrder) {
869     return EFI_OUT_OF_RESOURCES;
870   }
871 
872   //
873   // copy FD
874   //
875   Ptr             = DevOrder;
876   NewPtr          = NewDevOrder;
877   NewPtr->BbsType = Ptr->BbsType;
878   NewPtr->Length  = (UINT16) (sizeof (UINT16) + FDCount * sizeof (UINT16));
879   for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
880     if (!LegacyBmValidBbsEntry (&LocalBbsTable[Ptr->Data[Index] & 0xFF]) ||
881         LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_FLOPPY
882         ) {
883       continue;
884     }
885 
886     NewPtr->Data[FDIndex] = Ptr->Data[Index];
887     FDIndex++;
888   }
889   NewFDPtr = NewPtr->Data;
890 
891   //
892   // copy HD
893   //
894   Ptr             = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
895   NewPtr          = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
896   NewPtr->BbsType = Ptr->BbsType;
897   NewPtr->Length  = (UINT16) (sizeof (UINT16) + HDCount * sizeof (UINT16));
898   for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
899     if (!LegacyBmValidBbsEntry (&LocalBbsTable[Ptr->Data[Index] & 0xFF]) ||
900         LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_HARDDISK
901         ) {
902       continue;
903     }
904 
905     NewPtr->Data[HDIndex] = Ptr->Data[Index];
906     HDIndex++;
907   }
908   NewHDPtr = NewPtr->Data;
909 
910   //
911   // copy CD
912   //
913   Ptr    = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
914   NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
915   NewPtr->BbsType = Ptr->BbsType;
916   NewPtr->Length  = (UINT16) (sizeof (UINT16) + CDCount * sizeof (UINT16));
917   for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
918     if (!LegacyBmValidBbsEntry (&LocalBbsTable[Ptr->Data[Index] & 0xFF]) ||
919         LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_CDROM
920         ) {
921       continue;
922     }
923 
924     NewPtr->Data[CDIndex] = Ptr->Data[Index];
925     CDIndex++;
926   }
927   NewCDPtr = NewPtr->Data;
928 
929   //
930   // copy NET
931   //
932   Ptr    = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
933   NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
934   NewPtr->BbsType = Ptr->BbsType;
935   NewPtr->Length  = (UINT16) (sizeof (UINT16) + NETCount * sizeof (UINT16));
936   for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
937     if (!LegacyBmValidBbsEntry (&LocalBbsTable[Ptr->Data[Index] & 0xFF]) ||
938         LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_EMBED_NETWORK
939         ) {
940       continue;
941     }
942 
943     NewPtr->Data[NETIndex] = Ptr->Data[Index];
944     NETIndex++;
945   }
946   NewNETPtr = NewPtr->Data;
947 
948   //
949   // copy BEV
950   //
951   Ptr    = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
952   NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
953   NewPtr->BbsType = Ptr->BbsType;
954   NewPtr->Length  = (UINT16) (sizeof (UINT16) + BEVCount * sizeof (UINT16));
955   for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
956     if (!LegacyBmValidBbsEntry (&LocalBbsTable[Ptr->Data[Index] & 0xFF]) ||
957         LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_BEV_DEVICE
958         ) {
959       continue;
960     }
961 
962     NewPtr->Data[BEVIndex] = Ptr->Data[Index];
963     BEVIndex++;
964   }
965   NewBEVPtr = NewPtr->Data;
966 
967   for (Index = 0; Index < BbsCount; Index++) {
968     if (!LegacyBmValidBbsEntry (&LocalBbsTable[Index])) {
969       continue;
970     }
971 
972     switch (LocalBbsTable[Index].DeviceType) {
973     case BBS_FLOPPY:
974       Idx       = &FDIndex;
975       NewDevPtr = NewFDPtr;
976       break;
977 
978     case BBS_HARDDISK:
979       Idx       = &HDIndex;
980       NewDevPtr = NewHDPtr;
981       break;
982 
983     case BBS_CDROM:
984       Idx       = &CDIndex;
985       NewDevPtr = NewCDPtr;
986       break;
987 
988     case BBS_EMBED_NETWORK:
989       Idx       = &NETIndex;
990       NewDevPtr = NewNETPtr;
991       break;
992 
993     case BBS_BEV_DEVICE:
994       Idx       = &BEVIndex;
995       NewDevPtr = NewBEVPtr;
996       break;
997 
998     default:
999       Idx = NULL;
1000       break;
1001     }
1002     //
1003     // at this point we have copied those valid indexes to new buffer
1004     // and we should check if there is any new appeared boot device
1005     //
1006     if (Idx != NULL) {
1007       for (Index2 = 0; Index2 < *Idx; Index2++) {
1008         if ((NewDevPtr[Index2] & 0xFF) == (UINT16) Index) {
1009           break;
1010         }
1011       }
1012 
1013       if (Index2 == *Idx) {
1014         //
1015         // Index2 == *Idx means we didn't find Index
1016         // so Index is a new appeared device's index in BBS table
1017         // insert it before disabled indexes.
1018         //
1019         for (Index2 = 0; Index2 < *Idx; Index2++) {
1020           if ((NewDevPtr[Index2] & 0xFF00) == 0xFF00) {
1021             break;
1022           }
1023         }
1024         CopyMem (&NewDevPtr[Index2 + 1], &NewDevPtr[Index2], (*Idx - Index2) * sizeof (UINT16));
1025         NewDevPtr[Index2] = (UINT16) (Index & 0xFF);
1026         (*Idx)++;
1027       }
1028     }
1029   }
1030 
1031   FreePool (DevOrder);
1032 
1033   Status = gRT->SetVariable (
1034                   VAR_LEGACY_DEV_ORDER,
1035                   &gEfiLegacyDevOrderVariableGuid,
1036                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
1037                   TotalSize,
1038                   NewDevOrder
1039                   );
1040   FreePool (NewDevOrder);
1041 
1042   return Status;
1043 }
1044 
1045 /**
1046   Set Boot Priority for specified device type.
1047 
1048   @param DeviceType      The device type.
1049   @param BbsIndex        The BBS index to set the highest priority. Ignore when -1.
1050   @param LocalBbsTable   The BBS table.
1051   @param Priority        The prority table.
1052 
1053   @retval EFI_SUCCESS           The function completes successfully.
1054   @retval EFI_NOT_FOUND         Failed to find device.
1055   @retval EFI_OUT_OF_RESOURCES  Failed to get the efi variable of device order.
1056 
1057 **/
1058 EFI_STATUS
LegacyBmSetPriorityForSameTypeDev(IN UINT16 DeviceType,IN UINTN BbsIndex,IN OUT BBS_TABLE * LocalBbsTable,IN OUT UINT16 * Priority)1059 LegacyBmSetPriorityForSameTypeDev (
1060   IN UINT16                                              DeviceType,
1061   IN UINTN                                               BbsIndex,
1062   IN OUT BBS_TABLE                                       *LocalBbsTable,
1063   IN OUT UINT16                                          *Priority
1064   )
1065 {
1066   LEGACY_DEV_ORDER_ENTRY      *DevOrder;
1067   LEGACY_DEV_ORDER_ENTRY      *DevOrderPtr;
1068   UINTN                       DevOrderSize;
1069   UINTN                       Index;
1070 
1071   GetVariable2 (VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, (VOID **) &DevOrder, &DevOrderSize);
1072   if (NULL == DevOrder) {
1073     return EFI_OUT_OF_RESOURCES;
1074   }
1075 
1076   DevOrderPtr = DevOrder;
1077   while ((UINT8 *) DevOrderPtr < (UINT8 *) DevOrder + DevOrderSize) {
1078     if (DevOrderPtr->BbsType == DeviceType) {
1079       break;
1080     }
1081 
1082     DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) ((UINTN) DevOrderPtr + sizeof (BBS_TYPE) + DevOrderPtr->Length);
1083   }
1084 
1085   if ((UINT8 *) DevOrderPtr >= (UINT8 *) DevOrder + DevOrderSize) {
1086     FreePool (DevOrder);
1087     return EFI_NOT_FOUND;
1088   }
1089 
1090   if (BbsIndex != (UINTN) -1) {
1091     //
1092     // In case the BBS entry isn't valid because devices were plugged or removed.
1093     //
1094     if (!LegacyBmValidBbsEntry (&LocalBbsTable[BbsIndex]) || (LocalBbsTable[BbsIndex].DeviceType != DeviceType)) {
1095       FreePool (DevOrder);
1096       return EFI_NOT_FOUND;
1097     }
1098     LocalBbsTable[BbsIndex].BootPriority = *Priority;
1099     (*Priority)++;
1100   }
1101   //
1102   // If the high byte of the DevIndex is 0xFF, it indicates that this device has been disabled.
1103   //
1104   for (Index = 0; Index < DevOrderPtr->Length / sizeof (UINT16) - 1; Index++) {
1105     if ((DevOrderPtr->Data[Index] & 0xFF00) == 0xFF00) {
1106       //
1107       // LocalBbsTable[DevIndex[Index] & 0xFF].BootPriority = BBS_DISABLED_ENTRY;
1108       //
1109     } else if (DevOrderPtr->Data[Index] != BbsIndex) {
1110       LocalBbsTable[DevOrderPtr->Data[Index]].BootPriority = *Priority;
1111       (*Priority)++;
1112     }
1113   }
1114 
1115   FreePool (DevOrder);
1116   return EFI_SUCCESS;
1117 }
1118 
1119 /**
1120   Print the BBS Table.
1121 
1122   @param LocalBbsTable   The BBS table.
1123   @param BbsCount        The count of entry in BBS table.
1124 **/
1125 VOID
LegacyBmPrintBbsTable(IN BBS_TABLE * LocalBbsTable,IN UINT16 BbsCount)1126 LegacyBmPrintBbsTable (
1127   IN BBS_TABLE  *LocalBbsTable,
1128   IN UINT16     BbsCount
1129   )
1130 {
1131   UINT16  Index;
1132 
1133   DEBUG ((DEBUG_INFO, "\n"));
1134   DEBUG ((DEBUG_INFO, " NO  Prio bb/dd/ff cl/sc Type Stat segm:offs\n"));
1135   DEBUG ((DEBUG_INFO, "=============================================\n"));
1136   for (Index = 0; Index < BbsCount; Index++) {
1137     if (!LegacyBmValidBbsEntry (&LocalBbsTable[Index])) {
1138       continue;
1139     }
1140 
1141     DEBUG (
1142       (DEBUG_INFO,
1143       " %02x: %04x %02x/%02x/%02x %02x/%02x %04x %04x %04x:%04x\n",
1144       (UINTN) Index,
1145       (UINTN) LocalBbsTable[Index].BootPriority,
1146       (UINTN) LocalBbsTable[Index].Bus,
1147       (UINTN) LocalBbsTable[Index].Device,
1148       (UINTN) LocalBbsTable[Index].Function,
1149       (UINTN) LocalBbsTable[Index].Class,
1150       (UINTN) LocalBbsTable[Index].SubClass,
1151       (UINTN) LocalBbsTable[Index].DeviceType,
1152       (UINTN) * (UINT16 *) &LocalBbsTable[Index].StatusFlags,
1153       (UINTN) LocalBbsTable[Index].BootHandlerSegment,
1154       (UINTN) LocalBbsTable[Index].BootHandlerOffset,
1155       (UINTN) ((LocalBbsTable[Index].MfgStringSegment << 4) + LocalBbsTable[Index].MfgStringOffset),
1156       (UINTN) ((LocalBbsTable[Index].DescStringSegment << 4) + LocalBbsTable[Index].DescStringOffset))
1157       );
1158   }
1159 
1160   DEBUG ((DEBUG_INFO, "\n"));
1161 }
1162 
1163 /**
1164   Set the boot priority for BBS entries based on boot option entry and boot order.
1165 
1166   @param  BootOption            The boot option is to be checked for refresh BBS table.
1167 
1168   @retval EFI_SUCCESS           The boot priority for BBS entries is refreshed successfully.
1169   @retval EFI_NOT_FOUND         BBS entries can't be found.
1170   @retval EFI_OUT_OF_RESOURCES  Failed to get the legacy device boot order.
1171 **/
1172 EFI_STATUS
LegacyBmRefreshBbsTableForBoot(IN EFI_BOOT_MANAGER_LOAD_OPTION * BootOption)1173 LegacyBmRefreshBbsTableForBoot (
1174   IN EFI_BOOT_MANAGER_LOAD_OPTION        *BootOption
1175   )
1176 {
1177   EFI_STATUS                    Status;
1178   UINT16                        BbsIndex;
1179   UINT16                        HddCount;
1180   UINT16                        BbsCount;
1181   HDD_INFO                      *LocalHddInfo;
1182   BBS_TABLE                     *LocalBbsTable;
1183   UINT16                        DevType;
1184   EFI_LEGACY_BIOS_PROTOCOL      *LegacyBios;
1185   UINTN                         Index;
1186   UINT16                        Priority;
1187   UINT16                        *DeviceType;
1188   UINTN                         DeviceTypeCount;
1189   UINTN                         DeviceTypeIndex;
1190   EFI_BOOT_MANAGER_LOAD_OPTION  *Option;
1191   UINTN                         OptionCount;
1192 
1193   HddCount      = 0;
1194   BbsCount      = 0;
1195   LocalHddInfo  = NULL;
1196   LocalBbsTable = NULL;
1197   DevType       = BBS_UNKNOWN;
1198 
1199   Status        = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
1200   if (EFI_ERROR (Status)) {
1201     return Status;
1202   }
1203 
1204   Status = LegacyBios->GetBbsInfo (
1205                          LegacyBios,
1206                          &HddCount,
1207                          &LocalHddInfo,
1208                          &BbsCount,
1209                          &LocalBbsTable
1210                          );
1211   if (EFI_ERROR (Status)) {
1212     return Status;
1213   }
1214 
1215   //
1216   // First, set all the present devices' boot priority to BBS_UNPRIORITIZED_ENTRY
1217   // We will set them according to the settings setup by user
1218   //
1219   for (Index = 0; Index < BbsCount; Index++) {
1220     if (LegacyBmValidBbsEntry (&LocalBbsTable[Index])) {
1221       LocalBbsTable[Index].BootPriority = BBS_UNPRIORITIZED_ENTRY;
1222     }
1223   }
1224   //
1225   // boot priority always starts at 0
1226   //
1227   Priority = 0;
1228   if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) &&
1229       (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) {
1230     //
1231     // If BootOption stands for a legacy boot option, we prioritize the devices with the same type first.
1232     //
1233     DevType  = LegacyBmDeviceType (BootOption->FilePath);
1234     BbsIndex = ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption->OptionalData)->BbsIndex;
1235     Status = LegacyBmSetPriorityForSameTypeDev (
1236                DevType,
1237                BbsIndex,
1238                LocalBbsTable,
1239                &Priority
1240                );
1241     if (EFI_ERROR (Status)) {
1242       return Status;
1243     }
1244   }
1245   //
1246   // we have to set the boot priority for other BBS entries with different device types
1247   //
1248   Option          = EfiBootManagerGetLoadOptions (&OptionCount, LoadOptionTypeBoot);
1249   DeviceType      = AllocatePool (sizeof (UINT16) * OptionCount);
1250   ASSERT (DeviceType != NULL);
1251   DeviceType[0]   = DevType;
1252   DeviceTypeCount = 1;
1253   for (Index = 0; Index < OptionCount; Index++) {
1254     if ((DevicePathType (Option[Index].FilePath) != BBS_DEVICE_PATH) ||
1255         (DevicePathSubType (Option[Index].FilePath) != BBS_BBS_DP)) {
1256       continue;
1257     }
1258 
1259     DevType = LegacyBmDeviceType (Option[Index].FilePath);
1260     for (DeviceTypeIndex = 0; DeviceTypeIndex < DeviceTypeCount; DeviceTypeIndex++) {
1261       if (DeviceType[DeviceTypeIndex] == DevType) {
1262         break;
1263       }
1264     }
1265     if (DeviceTypeIndex < DeviceTypeCount) {
1266       //
1267       // We don't want to process twice for a device type
1268       //
1269       continue;
1270     }
1271 
1272     DeviceType[DeviceTypeCount] = DevType;
1273     DeviceTypeCount++;
1274 
1275     Status = LegacyBmSetPriorityForSameTypeDev (
1276                DevType,
1277                (UINTN) -1,
1278                LocalBbsTable,
1279                &Priority
1280                );
1281   }
1282   EfiBootManagerFreeLoadOptions (Option, OptionCount);
1283 
1284   DEBUG_CODE_BEGIN();
1285     LegacyBmPrintBbsTable (LocalBbsTable, BbsCount);
1286   DEBUG_CODE_END();
1287 
1288   return Status;
1289 }
1290 
1291 
1292 /**
1293   Boot the legacy system with the boot option.
1294 
1295   @param  BootOption The legacy boot option which have BBS device path
1296                      On return, BootOption->Status contains the boot status.
1297                      EFI_UNSUPPORTED    There is no legacybios protocol, do not support
1298                                         legacy boot.
1299                      EFI_STATUS         The status of LegacyBios->LegacyBoot ().
1300 **/
1301 VOID
1302 EFIAPI
LegacyBmBoot(IN EFI_BOOT_MANAGER_LOAD_OPTION * BootOption)1303 LegacyBmBoot (
1304   IN  EFI_BOOT_MANAGER_LOAD_OPTION           *BootOption
1305   )
1306 {
1307   EFI_STATUS                Status;
1308   EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
1309 
1310   Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
1311   if (EFI_ERROR (Status)) {
1312     //
1313     // If no LegacyBios protocol we do not support legacy boot
1314     //
1315     BootOption->Status = EFI_UNSUPPORTED;
1316     return;
1317   }
1318   //
1319   // Notes: if we separate the int 19, then we don't need to refresh BBS
1320   //
1321   Status = LegacyBmRefreshBbsTableForBoot (BootOption);
1322   if (EFI_ERROR (Status)) {
1323     BootOption->Status = Status;
1324     return;
1325   }
1326 
1327   BootOption->Status = LegacyBios->LegacyBoot (
1328                                      LegacyBios,
1329                                      (BBS_BBS_DEVICE_PATH *) BootOption->FilePath,
1330                                      BootOption->OptionalDataSize,
1331                                      BootOption->OptionalData
1332                                      );
1333 }
1334 
1335 /**
1336   This function enumerates all the legacy boot options.
1337 
1338   @param BootOptionCount   Return the legacy boot option count.
1339 
1340   @retval    Pointer to the legacy boot option buffer.
1341 **/
1342 EFI_BOOT_MANAGER_LOAD_OPTION *
LegacyBmEnumerateAllBootOptions(UINTN * BootOptionCount)1343 LegacyBmEnumerateAllBootOptions (
1344   UINTN                         *BootOptionCount
1345   )
1346 {
1347   EFI_STATUS                    Status;
1348   UINT16                        HddCount;
1349   UINT16                        BbsCount;
1350   HDD_INFO                      *HddInfo;
1351   BBS_TABLE                     *BbsTable;
1352   EFI_LEGACY_BIOS_PROTOCOL      *LegacyBios;
1353   UINT16                        Index;
1354   EFI_BOOT_MANAGER_LOAD_OPTION  *BootOptions;
1355 
1356   ASSERT (BootOptionCount != NULL);
1357 
1358   BootOptions      = NULL;
1359   *BootOptionCount = 0;
1360   BbsCount         = 0;
1361 
1362   Status        = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
1363   if (EFI_ERROR (Status)) {
1364     return NULL;
1365   }
1366 
1367   Status = LegacyBios->GetBbsInfo (
1368                          LegacyBios,
1369                          &HddCount,
1370                          &HddInfo,
1371                          &BbsCount,
1372                          &BbsTable
1373                          );
1374   if (EFI_ERROR (Status)) {
1375     return NULL;
1376   }
1377 
1378   for (Index = 0; Index < BbsCount; Index++) {
1379     if (!LegacyBmValidBbsEntry (&BbsTable[Index])) {
1380       continue;
1381     }
1382 
1383     BootOptions = ReallocatePool (
1384                     sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),
1385                     sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),
1386                     BootOptions
1387                     );
1388     ASSERT (BootOptions != NULL);
1389 
1390     Status = LegacyBmCreateLegacyBootOption (&BootOptions[(*BootOptionCount)++], &BbsTable[Index], Index);
1391     ASSERT_EFI_ERROR (Status);
1392   }
1393 
1394   return BootOptions;
1395 }
1396 
1397 /**
1398   Return the index of the boot option in the boot option array.
1399 
1400   The function compares the Description, FilePath, OptionalData.
1401 
1402   @param Key         The input boot option which is compared with.
1403   @param Array       The input boot option array.
1404   @param Count       The count of the input boot options.
1405 
1406   @retval  The index of the input boot option in the array.
1407 
1408 **/
1409 INTN
LegacyBmFindBootOption(IN CONST EFI_BOOT_MANAGER_LOAD_OPTION * Key,IN CONST EFI_BOOT_MANAGER_LOAD_OPTION * Array,IN UINTN Count)1410 LegacyBmFindBootOption (
1411   IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Key,
1412   IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Array,
1413   IN UINTN                              Count
1414   )
1415 {
1416   UINTN                             Index;
1417 
1418   for (Index = 0; Index < Count; Index++) {
1419     if ((StrCmp (Key->Description, Array[Index].Description) == 0) &&
1420         (CompareMem (Key->FilePath, Array[Index].FilePath, GetDevicePathSize (Key->FilePath)) == 0) &&
1421         (Key->OptionalDataSize == Array[Index].OptionalDataSize) &&
1422         (CompareMem (Key->OptionalData, Array[Index].OptionalData, Key->OptionalDataSize) == 0)) {
1423       return (INTN) Index;
1424     }
1425   }
1426 
1427   return -1;
1428 }
1429 
1430 /**
1431   Refresh all legacy boot options.
1432 
1433 **/
1434 VOID
1435 EFIAPI
LegacyBmRefreshAllBootOption(VOID)1436 LegacyBmRefreshAllBootOption (
1437   VOID
1438   )
1439 {
1440   EFI_STATUS                                 Status;
1441   EFI_LEGACY_BIOS_PROTOCOL                   *LegacyBios;
1442   UINTN                                      RootBridgeHandleCount;
1443   EFI_HANDLE                                 *RootBridgeHandleBuffer;
1444   UINTN                                      HandleCount;
1445   EFI_HANDLE                                 *HandleBuffer;
1446   UINTN                                      RootBridgeIndex;
1447   UINTN                                      Index;
1448   UINTN                                      Flags;
1449   EFI_BOOT_MANAGER_LOAD_OPTION               *BootOptions;
1450   UINTN                                      BootOptionCount;
1451   EFI_BOOT_MANAGER_LOAD_OPTION               *ExistingBootOptions;
1452   UINTN                                      ExistingBootOptionCount;
1453 
1454   Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
1455   if (EFI_ERROR (Status)) {
1456     LegacyBmDeleteAllBootOptions ();
1457     return;
1458   }
1459   PERF_START (NULL, "LegacyBootOptionEnum", "BDS", 0);
1460 
1461   //
1462   // Before enumerating the legacy boot option, we need to dispatch all the legacy option roms
1463   // to ensure the GetBbsInfo() counts all the legacy devices.
1464   //
1465   gBS->LocateHandleBuffer (
1466          ByProtocol,
1467          &gEfiPciRootBridgeIoProtocolGuid,
1468          NULL,
1469          &RootBridgeHandleCount,
1470          &RootBridgeHandleBuffer
1471          );
1472   for (RootBridgeIndex = 0; RootBridgeIndex < RootBridgeHandleCount; RootBridgeIndex++) {
1473     gBS->ConnectController (RootBridgeHandleBuffer[RootBridgeIndex], NULL, NULL, FALSE);
1474     gBS->LocateHandleBuffer (
1475            ByProtocol,
1476            &gEfiPciIoProtocolGuid,
1477            NULL,
1478            &HandleCount,
1479            &HandleBuffer
1480            );
1481     for (Index = 0; Index < HandleCount; Index++) {
1482       //
1483       // Start the thunk driver so that the legacy option rom gets dispatched.
1484       // Note: We don't directly call InstallPciRom because some thunk drivers
1485       // (e.g. BlockIo thunk driver) depend on the immediate result after dispatching
1486       //
1487       Status = LegacyBios->CheckPciRom (
1488                              LegacyBios,
1489                              HandleBuffer[Index],
1490                              NULL,
1491                              NULL,
1492                              &Flags
1493                              );
1494       if (!EFI_ERROR (Status)) {
1495         gBS->ConnectController (HandleBuffer[Index], NULL, NULL, FALSE);
1496       }
1497     }
1498   }
1499 
1500   //
1501   // Same algorithm pattern as the EfiBootManagerRefreshAllBootOption
1502   // Firstly delete the invalid legacy boot options,
1503   // then enumreate and save the newly appeared legacy boot options
1504   // the last step is legacy boot option special action to refresh the LegacyDevOrder variable
1505   //
1506   LegacyBmDeleteAllInvalidBootOptions ();
1507 
1508   ExistingBootOptions = EfiBootManagerGetLoadOptions (&ExistingBootOptionCount, LoadOptionTypeBoot);
1509   BootOptions         = LegacyBmEnumerateAllBootOptions   (&BootOptionCount);
1510 
1511   for (Index = 0; Index < BootOptionCount; Index++) {
1512     if (LegacyBmFindBootOption (&BootOptions[Index], ExistingBootOptions, ExistingBootOptionCount) == -1) {
1513       Status = EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN) -1);
1514       DEBUG ((
1515         EFI_D_INFO, "[LegacyBds] New Boot Option: Boot%04x Bbs0x%04x %s %r\n",
1516         (UINTN) BootOptions[Index].OptionNumber,
1517         (UINTN) ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOptions[Index].OptionalData)->BbsIndex,
1518         BootOptions[Index].Description,
1519         Status
1520         ));
1521       //
1522       // Continue upon failure to add boot option.
1523       //
1524     }
1525   }
1526 
1527   EfiBootManagerFreeLoadOptions (ExistingBootOptions, ExistingBootOptionCount);
1528   EfiBootManagerFreeLoadOptions (BootOptions,         BootOptionCount);
1529 
1530   //
1531   // Failure to create LegacyDevOrder variable only impacts the boot order.
1532   //
1533   LegacyBmUpdateDevOrder ();
1534 
1535   PERF_END   (NULL, "LegacyBootOptionEnum", "BDS", 0);
1536 }
1537