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) 2004 - 2015, 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 "BBSsupport.h"
18 
19 #pragma pack(1)
20 typedef struct {
21   BBS_TABLE BbsEntry;
22   UINT16    BbsIndex;
23 } LEGACY_BOOT_OPTION_BBS_DATA;
24 #pragma pack()
25 
26 /**
27   Re-order the Boot Option according to the DevOrder.
28 
29   The routine re-orders the Boot Option in BootOption array according to
30   the order specified by DevOrder.
31 
32   @param DevOrder           Pointer to buffer containing the BBS Index,
33                             high 8-bit value 0xFF indicating a disabled boot option
34   @param DevOrderCount      Count of the BBS Index
35   @param EnBootOption       Callee allocated buffer containing the enabled Boot Option Numbers
36   @param EnBootOptionCount  Count of the enabled Boot Option Numbers
37   @param DisBootOption      Callee allocated buffer containing the disabled Boot Option Numbers
38   @param DisBootOptionCount Count of the disabled Boot Option Numbers
39 **/
40 VOID
OrderLegacyBootOption4SameType(UINT16 * DevOrder,UINTN DevOrderCount,UINT16 ** EnBootOption,UINTN * EnBootOptionCount,UINT16 ** DisBootOption,UINTN * DisBootOptionCount)41 OrderLegacyBootOption4SameType (
42   UINT16                   *DevOrder,
43   UINTN                    DevOrderCount,
44   UINT16                   **EnBootOption,
45   UINTN                    *EnBootOptionCount,
46   UINT16                   **DisBootOption,
47   UINTN                    *DisBootOptionCount
48   )
49 {
50   EFI_STATUS               Status;
51   UINT16                   *NewBootOption;
52   UINT16                   *BootOrder;
53   UINTN                    BootOrderSize;
54   UINTN                    Index;
55   UINTN                    StartPosition;
56 
57   BDS_COMMON_OPTION        *BootOption;
58 
59   CHAR16                   OptionName[sizeof ("Boot####")];
60   UINT16                   *BbsIndexArray;
61   UINT16                   *DeviceTypeArray;
62   LIST_ENTRY               List;
63 
64   BootOrder = BdsLibGetVariableAndSize (
65                 L"BootOrder",
66                 &gEfiGlobalVariableGuid,
67                 &BootOrderSize
68                 );
69   ASSERT (BootOrder != NULL);
70 
71   BbsIndexArray       = AllocatePool (BootOrderSize);
72   DeviceTypeArray     = AllocatePool (BootOrderSize);
73   *EnBootOption       = AllocatePool (BootOrderSize);
74   *DisBootOption      = AllocatePool (BootOrderSize);
75   *DisBootOptionCount = 0;
76   *EnBootOptionCount  = 0;
77   Index               = 0;
78 
79   ASSERT (BbsIndexArray != NULL);
80   ASSERT (DeviceTypeArray != NULL);
81   ASSERT (*EnBootOption != NULL);
82   ASSERT (*DisBootOption != NULL);
83 
84   for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
85 
86     UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", BootOrder[Index]);
87     InitializeListHead (&List);
88     BootOption = BdsLibVariableToOption (&List, OptionName);
89     ASSERT (BootOption != NULL);
90 
91     if ((DevicePathType (BootOption->DevicePath) == BBS_DEVICE_PATH) &&
92         (DevicePathSubType (BootOption->DevicePath) == BBS_BBS_DP)) {
93       //
94       // Legacy Boot Option
95       //
96       ASSERT (BootOption->LoadOptionsSize == sizeof (LEGACY_BOOT_OPTION_BBS_DATA));
97 
98       DeviceTypeArray[Index] = ((BBS_BBS_DEVICE_PATH *) BootOption->DevicePath)->DeviceType;
99       BbsIndexArray  [Index] = ((LEGACY_BOOT_OPTION_BBS_DATA *) BootOption->LoadOptions)->BbsIndex;
100     } else {
101       DeviceTypeArray[Index] = BBS_TYPE_UNKNOWN;
102       BbsIndexArray  [Index] = 0xFFFF;
103     }
104     FreePool (BootOption->DevicePath);
105     FreePool (BootOption->Description);
106     FreePool (BootOption->LoadOptions);
107     FreePool (BootOption);
108   }
109 
110   //
111   // Record the corresponding Boot Option Numbers according to the DevOrder
112   // Record the EnBootOption and DisBootOption according to the DevOrder
113   //
114   StartPosition = BootOrderSize / sizeof (UINT16);
115   NewBootOption = AllocatePool (DevOrderCount * sizeof (UINT16));
116   ASSERT (NewBootOption != NULL);
117   while (DevOrderCount-- != 0) {
118     for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
119       if (BbsIndexArray[Index] == (DevOrder[DevOrderCount] & 0xFF)) {
120         StartPosition = MIN (StartPosition, Index);
121         NewBootOption[DevOrderCount] = BootOrder[Index];
122 
123         if ((DevOrder[DevOrderCount] & 0xFF00) == 0xFF00) {
124           (*DisBootOption)[*DisBootOptionCount] = BootOrder[Index];
125           (*DisBootOptionCount)++;
126         } else {
127           (*EnBootOption)[*EnBootOptionCount] = BootOrder[Index];
128           (*EnBootOptionCount)++;
129         }
130         break;
131       }
132     }
133   }
134 
135   //
136   // Overwrite the old BootOption
137   //
138   CopyMem (&BootOrder[StartPosition], NewBootOption, (*DisBootOptionCount + *EnBootOptionCount) * sizeof (UINT16));
139   Status = gRT->SetVariable (
140                   L"BootOrder",
141                   &gEfiGlobalVariableGuid,
142                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
143                   BootOrderSize,
144                   BootOrder
145                   );
146   //
147   // Changing content without increasing its size with current variable implementation shouldn't fail.
148   //
149   ASSERT_EFI_ERROR (Status);
150 
151   FreePool (NewBootOption);
152   FreePool (DeviceTypeArray);
153   FreePool (BbsIndexArray);
154   FreePool (BootOrder);
155 }
156 
157 /**
158   Group the legacy boot options in the BootOption.
159 
160   The routine assumes the boot options in the beginning that covers all the device
161   types are ordered properly and re-position the following boot options just after
162   the corresponding boot options with the same device type.
163   For example:
164   1. Input  = [Harddisk1 CdRom2 Efi1 Harddisk0 CdRom0 CdRom1 Harddisk2 Efi0]
165      Assuming [Harddisk1 CdRom2 Efi1] is ordered properly
166      Output = [Harddisk1 Harddisk0 Harddisk2 CdRom2 CdRom0 CdRom1 Efi1 Efi0]
167 
168   2. Input  = [Efi1 Efi0 CdRom1 Harddisk0 Harddisk1 Harddisk2 CdRom0 CdRom2]
169      Assuming [Efi1 Efi0 CdRom1 Harddisk0] is ordered properly
170      Output = [Efi1 Efi0 CdRom1 CdRom0 CdRom2 Harddisk0 Harddisk1 Harddisk2]
171 
172 **/
173 VOID
GroupMultipleLegacyBootOption4SameType(VOID)174 GroupMultipleLegacyBootOption4SameType (
175   VOID
176   )
177 {
178   EFI_STATUS                   Status;
179   UINTN                        Index;
180   UINTN                        DeviceIndex;
181   UINTN                        DeviceTypeIndex[7];
182   UINTN                        *NextIndex;
183   UINT16                       OptionNumber;
184   UINT16                       *BootOrder;
185   UINTN                        BootOrderSize;
186   CHAR16                       OptionName[sizeof ("Boot####")];
187   BDS_COMMON_OPTION            *BootOption;
188   LIST_ENTRY                   List;
189 
190   SetMem (DeviceTypeIndex, sizeof (DeviceTypeIndex), 0xff);
191 
192   BootOrder = BdsLibGetVariableAndSize (
193                 L"BootOrder",
194                 &gEfiGlobalVariableGuid,
195                 &BootOrderSize
196                 );
197 
198   for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
199     UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", BootOrder[Index]);
200     InitializeListHead (&List);
201     BootOption = BdsLibVariableToOption (&List, OptionName);
202     ASSERT (BootOption != NULL);
203 
204     if ((DevicePathType (BootOption->DevicePath) == BBS_DEVICE_PATH) &&
205         (DevicePathSubType (BootOption->DevicePath) == BBS_BBS_DP)) {
206       //
207       // Legacy Boot Option
208       //
209       ASSERT ((((BBS_BBS_DEVICE_PATH *) BootOption->DevicePath)->DeviceType & 0xF) < ARRAY_SIZE (DeviceTypeIndex));
210       NextIndex = &DeviceTypeIndex[((BBS_BBS_DEVICE_PATH *) BootOption->DevicePath)->DeviceType & 0xF];
211 
212       if (*NextIndex == (UINTN) -1) {
213         //
214         // *NextIndex is the Index in BootOrder to put the next Option Number for the same type
215         //
216         *NextIndex = Index + 1;
217       } else {
218         //
219         // insert the current boot option before *NextIndex, causing [*Next .. Index] shift right one position
220         //
221         OptionNumber = BootOrder[Index];
222         CopyMem (&BootOrder[*NextIndex + 1], &BootOrder[*NextIndex], (Index - *NextIndex) * sizeof (UINT16));
223         BootOrder[*NextIndex] = OptionNumber;
224 
225         //
226         // Update the DeviceTypeIndex array to reflect the right shift operation
227         //
228         for (DeviceIndex = 0; DeviceIndex < ARRAY_SIZE (DeviceTypeIndex); DeviceIndex++) {
229           if (DeviceTypeIndex[DeviceIndex] != (UINTN) -1 && DeviceTypeIndex[DeviceIndex] >= *NextIndex) {
230             DeviceTypeIndex[DeviceIndex]++;
231           }
232         }
233       }
234     }
235     FreePool (BootOption->DevicePath);
236     FreePool (BootOption->Description);
237     FreePool (BootOption->LoadOptions);
238     FreePool (BootOption);
239   }
240 
241   Status = gRT->SetVariable (
242                   L"BootOrder",
243                   &gEfiGlobalVariableGuid,
244                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
245                   BootOrderSize,
246                   BootOrder
247                   );
248   //
249   // Changing content without increasing its size with current variable implementation shouldn't fail.
250   //
251   ASSERT_EFI_ERROR (Status);
252   FreePool (BootOrder);
253 }
254 
255