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
6Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>
7This program and the accompanying materials
8are licensed and made available under the terms and conditions of the BSD License
9which accompanies this distribution.  The full text of the license may be found at
10http://opensource.org/licenses/bsd-license.php
11
12THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13WITHOUT 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**/
31EFI_STATUS
32EFIAPI
33LegacyBootManagerLibConstructor (
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**/
52UINT16
53LegacyBmDeviceType (
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**/
70BOOLEAN
71LegacyBmValidBbsEntry (
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**/
94VOID
95LegacyBmBuildLegacyDevNameString (
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**/
213UINT16
214LegacyBmFuzzyMatch (
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**/
296VOID
297LegacyBmUpdateBbsIndex (
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**/
346EFI_STATUS
347LegacyBmDeleteAllBootOptions (
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**/
391EFI_STATUS
392LegacyBmDeleteAllInvalidBootOptions (
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**/
516EFI_STATUS
517LegacyBmCreateLegacyBootOption (
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**/
592UINT16 *
593LegacyBmFillDevOrderBuf (
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**/
631EFI_STATUS
632LegacyBmCreateDevOrder (
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**/
755EFI_STATUS
756LegacyBmUpdateDevOrder (
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**/
1058EFI_STATUS
1059LegacyBmSetPriorityForSameTypeDev (
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**/
1125VOID
1126LegacyBmPrintBbsTable (
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**/
1172EFI_STATUS
1173LegacyBmRefreshBbsTableForBoot (
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**/
1301VOID
1302EFIAPI
1303LegacyBmBoot (
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**/
1342EFI_BOOT_MANAGER_LOAD_OPTION *
1343LegacyBmEnumerateAllBootOptions (
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**/
1409INTN
1410LegacyBmFindBootOption (
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**/
1434VOID
1435EFIAPI
1436LegacyBmRefreshAllBootOption (
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