1 /** @file
2 *
3 *  Copyright (c) 2011-2015, ARM Limited. All rights reserved.
4 *
5 *  This program and the accompanying materials
6 *  are licensed and made available under the terms and conditions of the BSD License
7 *  which accompanies this distribution.  The full text of the license may be found at
8 *  http://opensource.org/licenses/bsd-license.php
9 *
10 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 *
13 **/
14 
15 #include "BdsInternal.h"
16 
17 EFI_STATUS
BootOptionStart(IN BDS_LOAD_OPTION * BootOption)18 BootOptionStart (
19   IN BDS_LOAD_OPTION *BootOption
20   )
21 {
22   EFI_STATUS Status;
23   UINT16     LoadOptionIndexSize;
24 
25   // Connect all the drivers if the EFI Application is not a EFI OS Loader
26   if ((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_APP) {
27     BdsConnectAllDrivers ();
28   }
29 
30   // Set BootCurrent variable
31   LoadOptionIndexSize = sizeof (UINT16);
32   gRT->SetVariable (L"BootCurrent", &gEfiGlobalVariableGuid,
33             EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
34             LoadOptionIndexSize, &(BootOption->LoadOptionIndex));
35 
36   Status = BdsStartEfiApplication (gImageHandle, BootOption->FilePathList, BootOption->OptionalDataSize, BootOption->OptionalData);
37 
38   // Clear BootCurrent variable
39   LoadOptionIndexSize = sizeof (UINT16);
40   gRT->SetVariable (L"BootCurrent", &gEfiGlobalVariableGuid,
41             EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
42             0, NULL);
43 
44   return Status;
45 }
46 
47 EFI_STATUS
BootOptionList(IN OUT LIST_ENTRY * BootOptionList)48 BootOptionList (
49   IN OUT LIST_ENTRY *BootOptionList
50   )
51 {
52   EFI_STATUS                    Status;
53   UINTN                         Index;
54   UINT16*                       BootOrder;
55   UINTN                         BootOrderSize;
56   BDS_LOAD_OPTION*              BdsLoadOption;
57   BDS_LOAD_OPTION_ENTRY*        BdsLoadOptionEntry;
58 
59   InitializeListHead (BootOptionList);
60 
61   // Get the Boot Option Order from the environment variable
62   Status = GetGlobalEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
63   if (EFI_ERROR(Status)) {
64     return Status;
65   }
66 
67   for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
68     Status = BootOptionFromLoadOptionIndex (BootOrder[Index], &BdsLoadOption);
69     if (!EFI_ERROR(Status)) {
70       BdsLoadOptionEntry = (BDS_LOAD_OPTION_ENTRY*)AllocatePool(sizeof(BDS_LOAD_OPTION_ENTRY));
71       BdsLoadOptionEntry->BdsLoadOption = BdsLoadOption;
72       InsertTailList (BootOptionList,&BdsLoadOptionEntry->Link);
73     }
74   }
75 
76   FreePool (BootOrder);
77 
78   return EFI_SUCCESS;
79 }
80 
81 STATIC
82 EFI_STATUS
BootOptionSetFields(IN BDS_LOAD_OPTION * BootOption,IN UINT32 Attributes,IN CHAR16 * BootDescription,IN EFI_DEVICE_PATH_PROTOCOL * DevicePath,IN UINT8 * OptionalData,IN UINTN OptionalDataSize)83 BootOptionSetFields (
84   IN BDS_LOAD_OPTION*           BootOption,
85   IN UINT32                     Attributes,
86   IN CHAR16*                    BootDescription,
87   IN EFI_DEVICE_PATH_PROTOCOL*  DevicePath,
88   IN UINT8*                     OptionalData,
89   IN UINTN                      OptionalDataSize
90   )
91 {
92   EFI_LOAD_OPTION               *EfiLoadOption;
93   UINTN                         EfiLoadOptionSize;
94   UINTN                         BootDescriptionSize;
95   UINT16                        FilePathListLength;
96   UINT8*                        EfiLoadOptionPtr;
97 
98   // If we are overwriting an existent Boot Option then we have to free previously allocated memory
99   if (BootOption->LoadOption) {
100     FreePool (BootOption->LoadOption);
101   }
102 
103   BootDescriptionSize = StrSize (BootDescription);
104 
105   // Compute the size of the FilePath list
106   FilePathListLength = GetUnalignedDevicePathSize (DevicePath);
107 
108   // Allocate the memory for the EFI Load Option
109   EfiLoadOptionSize = sizeof(UINT32) + sizeof(UINT16) + BootDescriptionSize + FilePathListLength + OptionalDataSize;
110   EfiLoadOption = (EFI_LOAD_OPTION *)AllocatePool(EfiLoadOptionSize);
111   EfiLoadOptionPtr = (UINT8 *)EfiLoadOption;
112 
113   //
114   // Populate the EFI Load Option and BDS Boot Option structures
115   //
116 
117   // Attributes fields
118   BootOption->Attributes = Attributes;
119   *(UINT32*)EfiLoadOptionPtr = Attributes;
120   EfiLoadOptionPtr += sizeof(UINT32);
121 
122   // FilePath List fields
123   BootOption->FilePathListLength = FilePathListLength;
124   *(UINT16*)EfiLoadOptionPtr = FilePathListLength;
125   EfiLoadOptionPtr += sizeof(UINT16);
126 
127   // Boot description fields
128   BootOption->Description = (CHAR16*)EfiLoadOptionPtr;
129   CopyMem (EfiLoadOptionPtr, BootDescription, BootDescriptionSize);
130   EfiLoadOptionPtr += BootDescriptionSize;
131 
132   // File path fields
133   BootOption->FilePathList = (EFI_DEVICE_PATH_PROTOCOL*)EfiLoadOptionPtr;
134   CopyMem (EfiLoadOptionPtr, DevicePath, FilePathListLength);
135   EfiLoadOptionPtr += FilePathListLength;
136 
137   // Optional Data fields, Do unaligned writes
138   BootOption->OptionalData = EfiLoadOptionPtr;
139 
140   if (OptionalData != NULL) {
141     CopyMem (BootOption->OptionalData, OptionalData, OptionalDataSize);
142   }
143 
144   BootOption->OptionalDataSize = OptionalDataSize;
145 
146   // If this function is called at the creation of the Boot Device entry (not at the update) the
147   // BootOption->LoadOptionSize must be zero then we get a new BootIndex for this entry
148   if (BootOption->LoadOptionSize == 0) {
149     BootOption->LoadOptionIndex = BootOptionAllocateBootIndex ();
150   }
151 
152   // Fill the EFI Load option fields
153   BootOption->LoadOption = EfiLoadOption;
154   BootOption->LoadOptionSize = EfiLoadOptionSize;
155 
156   return EFI_SUCCESS;
157 }
158 
159 EFI_STATUS
BootOptionCreate(IN UINT32 Attributes,IN CHAR16 * BootDescription,IN EFI_DEVICE_PATH_PROTOCOL * DevicePath,IN UINT8 * OptionalData,IN UINTN OptionalDataSize,OUT BDS_LOAD_OPTION ** BdsLoadOption)160 BootOptionCreate (
161   IN  UINT32                    Attributes,
162   IN  CHAR16*                   BootDescription,
163   IN  EFI_DEVICE_PATH_PROTOCOL* DevicePath,
164   IN  UINT8*                    OptionalData,
165   IN  UINTN                     OptionalDataSize,
166   OUT BDS_LOAD_OPTION**         BdsLoadOption
167   )
168 {
169   EFI_STATUS                    Status;
170   BDS_LOAD_OPTION_ENTRY*        BootOptionEntry;
171   BDS_LOAD_OPTION*              BootOption;
172   CHAR16                        BootVariableName[9];
173   UINT16*                       BootOrder;
174   UINTN                         BootOrderSize;
175 
176   //
177   // Allocate and fill the memory for the BDS Load Option structure
178   //
179   BootOptionEntry = (BDS_LOAD_OPTION_ENTRY*)AllocatePool (sizeof (BDS_LOAD_OPTION_ENTRY));
180   InitializeListHead (&BootOptionEntry->Link);
181   BootOptionEntry->BdsLoadOption = (BDS_LOAD_OPTION*)AllocateZeroPool (sizeof(BDS_LOAD_OPTION));
182 
183   BootOption = BootOptionEntry->BdsLoadOption;
184   BootOptionSetFields (BootOption, Attributes, BootDescription, DevicePath, OptionalData, OptionalDataSize);
185 
186   //
187   // Set the related environment variables
188   //
189 
190   // Create Boot#### environment variable
191   UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BootOption->LoadOptionIndex);
192   Status = gRT->SetVariable (
193       BootVariableName,
194       &gEfiGlobalVariableGuid,
195       EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
196       BootOption->LoadOptionSize,
197       BootOption->LoadOption
198       );
199 
200   // Add the new Boot Index to the list
201   Status = GetGlobalEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
202   if (!EFI_ERROR(Status)) {
203     BootOrder = ReallocatePool (BootOrderSize, BootOrderSize + sizeof(UINT16), BootOrder);
204     // Add the new index at the end
205     BootOrder[BootOrderSize / sizeof(UINT16)] = BootOption->LoadOptionIndex;
206     BootOrderSize += sizeof(UINT16);
207   } else {
208     // BootOrder does not exist. Create it
209     BootOrderSize = sizeof(UINT16);
210     BootOrder = &(BootOption->LoadOptionIndex);
211   }
212 
213   // Update (or Create) the BootOrder environment variable
214   Status = gRT->SetVariable (
215       L"BootOrder",
216       &gEfiGlobalVariableGuid,
217       EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
218       BootOrderSize,
219       BootOrder
220       );
221 
222   // We only free it if the UEFI Variable 'BootOrder' was already existing
223   if (BootOrderSize > sizeof(UINT16)) {
224     FreePool (BootOrder);
225   }
226 
227   *BdsLoadOption = BootOption;
228   return Status;
229 }
230 
231 EFI_STATUS
BootOptionUpdate(IN BDS_LOAD_OPTION * BdsLoadOption,IN UINT32 Attributes,IN CHAR16 * BootDescription,IN EFI_DEVICE_PATH_PROTOCOL * DevicePath,IN UINT8 * OptionalData,IN UINTN OptionalDataSize)232 BootOptionUpdate (
233   IN  BDS_LOAD_OPTION*          BdsLoadOption,
234   IN  UINT32                    Attributes,
235   IN  CHAR16*                   BootDescription,
236   IN  EFI_DEVICE_PATH_PROTOCOL* DevicePath,
237   IN UINT8*                     OptionalData,
238   IN UINTN                      OptionalDataSize
239   )
240 {
241   EFI_STATUS      Status;
242   CHAR16          BootVariableName[9];
243 
244   // Update the BDS Load Option structure
245   BootOptionSetFields (BdsLoadOption, Attributes, BootDescription, DevicePath, OptionalData, OptionalDataSize);
246 
247   // Update the related environment variables
248   UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BdsLoadOption->LoadOptionIndex);
249 
250   Status = gRT->SetVariable (
251       BootVariableName,
252       &gEfiGlobalVariableGuid,
253       EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
254       BdsLoadOption->LoadOptionSize,
255       BdsLoadOption->LoadOption
256       );
257 
258   return Status;
259 }
260 
261 EFI_STATUS
BootOptionDelete(IN BDS_LOAD_OPTION * BootOption)262 BootOptionDelete (
263   IN  BDS_LOAD_OPTION *BootOption
264   )
265 {
266   UINTN         Index;
267   UINTN         BootOrderSize;
268   UINT16*       BootOrder;
269   UINTN         BootOrderCount;
270   CHAR16        BootVariableName[9];
271   EFI_STATUS    Status;
272 
273   // Remove the entry from the BootOrder environment variable
274   Status = GetGlobalEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
275   if (!EFI_ERROR(Status)) {
276     BootOrderCount = BootOrderSize / sizeof(UINT16);
277 
278     // Find the index of the removed entry
279     for (Index = 0; Index < BootOrderCount; Index++) {
280       if (BootOrder[Index] == BootOption->LoadOptionIndex) {
281         // If it the last entry we do not need to rearrange the BootOrder list
282         if (Index + 1 != BootOrderCount) {
283           CopyMem (
284             &BootOrder[Index],
285             &BootOrder[Index + 1],
286             (BootOrderCount - (Index + 1)) * sizeof(UINT16)
287             );
288         }
289         break;
290       }
291     }
292 
293     // Update the BootOrder environment variable
294     Status = gRT->SetVariable (
295         L"BootOrder",
296         &gEfiGlobalVariableGuid,
297         EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
298         BootOrderSize - sizeof(UINT16),
299         BootOrder
300         );
301   }
302 
303   // Delete Boot#### environment variable
304   UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BootOption->LoadOptionIndex);
305   Status = gRT->SetVariable (
306       BootVariableName,
307       &gEfiGlobalVariableGuid,
308       EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
309       0,
310       NULL
311       );
312 
313   FreePool (BootOrder);
314 
315   return Status;
316 }
317