]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Bds/BootOption.c
ArmPlatformPkg/Bds: Upgrade the BDS to be more conformed to the UEFI Specification
[mirror_edk2.git] / ArmPlatformPkg / Bds / BootOption.c
1 /** @file
2 *
3 * Copyright (c) 2011, 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 extern EFI_HANDLE mImageHandle;
18
19 EFI_STATUS
20 BootOptionStart (
21 IN BDS_LOAD_OPTION *BootOption
22 )
23 {
24 EFI_STATUS Status;
25 EFI_DEVICE_PATH* FdtDevicePath;
26 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol;
27
28 Status = EFI_UNSUPPORTED;
29
30 if (BootOption->OptionalData->LoaderType == BDS_LOADER_EFI_APPLICATION) {
31 // Need to connect every drivers to ensure no dependencies are missing for the application
32 BdsConnectAllDrivers();
33
34 Status = BdsStartEfiApplication (mImageHandle, BootOption->FilePathList);
35 } else if (BootOption->OptionalData->LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) {
36 Status = BdsBootLinux (BootOption->FilePathList, BootOption->OptionalData->Arguments, NULL);
37 } else if (BootOption->OptionalData->LoaderType == BDS_LOADER_KERNEL_LINUX_FDT) {
38 // Convert the FDT path into a Device Path
39 Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
40 ASSERT_EFI_ERROR(Status);
41 FdtDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr(PcdFdtDevicePath));
42
43 Status = BdsBootLinux (BootOption->FilePathList, BootOption->OptionalData->Arguments, FdtDevicePath);
44 FreePool(FdtDevicePath);
45 }
46
47 return Status;
48 }
49
50 EFI_STATUS
51 BootOptionParseLoadOption (
52 IN EFI_LOAD_OPTION EfiLoadOption,
53 IN UINTN EfiLoadOptionSize,
54 OUT BDS_LOAD_OPTION **BdsLoadOption
55 )
56 {
57 BDS_LOAD_OPTION *LoadOption;
58 UINTN FilePathListLength;
59 UINTN DescriptionLength;
60
61 if (EfiLoadOption == NULL) {
62 return EFI_INVALID_PARAMETER;
63 }
64
65 if (EfiLoadOptionSize < sizeof(UINT32) + sizeof(UINT16) + sizeof(CHAR16) + sizeof(EFI_DEVICE_PATH_PROTOCOL)) {
66 return EFI_BAD_BUFFER_SIZE;
67 }
68
69 LoadOption = (BDS_LOAD_OPTION*)AllocatePool(sizeof(BDS_LOAD_OPTION));
70 if (LoadOption == NULL) {
71 return EFI_OUT_OF_RESOURCES;
72 }
73
74 LoadOption->LoadOption = EfiLoadOption;
75 LoadOption->LoadOptionSize = EfiLoadOptionSize;
76
77 LoadOption->Attributes = *(UINT32*)EfiLoadOption;
78 FilePathListLength = *(UINT16*)(EfiLoadOption + sizeof(UINT32));
79 LoadOption->Description = (CHAR16*)(EfiLoadOption + sizeof(UINT32) + sizeof(UINT16));
80 DescriptionLength = StrSize (LoadOption->Description);
81 LoadOption->FilePathList = (EFI_DEVICE_PATH_PROTOCOL*)(EfiLoadOption + sizeof(UINT32) + sizeof(UINT16) + DescriptionLength);
82
83 if ((UINTN)((UINT8*)LoadOption->FilePathList + FilePathListLength - EfiLoadOption) == EfiLoadOptionSize) {
84 LoadOption->OptionalData = NULL;
85 } else {
86 LoadOption->OptionalData = (BDS_LOADER_OPTIONAL_DATA *)((UINT8*)LoadOption->FilePathList + FilePathListLength);
87 }
88
89 *BdsLoadOption = LoadOption;
90 return EFI_SUCCESS;
91 }
92
93 EFI_STATUS
94 BootOptionFromLoadOptionVariable (
95 IN UINT16 LoadOptionIndex,
96 OUT BDS_LOAD_OPTION **BdsLoadOption
97 )
98 {
99 EFI_STATUS Status;
100 CHAR16 BootVariableName[9];
101 EFI_LOAD_OPTION EfiLoadOption;
102 UINTN EfiLoadOptionSize;
103
104 UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", LoadOptionIndex);
105
106 Status = GetEnvironmentVariable (BootVariableName, NULL, &EfiLoadOptionSize, (VOID**)&EfiLoadOption);
107 if (!EFI_ERROR(Status)) {
108 Status = BootOptionParseLoadOption (EfiLoadOption,EfiLoadOptionSize,BdsLoadOption);
109 if (!EFI_ERROR(Status)) {
110 (*BdsLoadOption)->LoadOptionIndex = LoadOptionIndex;
111 }
112 }
113
114 return Status;
115 }
116
117 EFI_STATUS
118 BootOptionList (
119 IN OUT LIST_ENTRY *BootOptionList
120 )
121 {
122 EFI_STATUS Status;
123 UINTN Index;
124 UINT16 *BootOrder;
125 UINTN BootOrderSize;
126 BDS_LOAD_OPTION *BdsLoadOption;
127
128 InitializeListHead (BootOptionList);
129
130 // Get the Boot Option Order from the environment variable
131 Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
132 if (EFI_ERROR(Status)) {
133 return Status;
134 }
135
136 for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
137 Status = BootOptionFromLoadOptionVariable (BootOrder[Index],&BdsLoadOption);
138 if (!EFI_ERROR(Status)) {
139 InsertTailList (BootOptionList,&BdsLoadOption->Link);
140 }
141 }
142
143 return EFI_SUCCESS;
144 }
145
146 UINT16
147 BootOptionAllocateBootIndex (
148 VOID
149 )
150 {
151 EFI_STATUS Status;
152 UINTN Index;
153 UINT32 BootIndex;
154 UINT16 *BootOrder;
155 UINTN BootOrderSize;
156 BOOLEAN Found;
157
158 // Get the Boot Option Order from the environment variable
159 Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
160 if (!EFI_ERROR(Status)) {
161 for (BootIndex = 0; BootIndex <= 0xFFFF; BootIndex++) {
162 Found = FALSE;
163 for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
164 if (BootOrder[Index] == BootIndex) {
165 Found = TRUE;
166 break;
167 }
168 }
169 if (!Found) {
170 return BootIndex;
171 }
172 }
173 }
174 // Return the first index
175 return 0;
176 }
177
178 STATIC
179 EFI_STATUS
180 BootOptionSetFields (
181 IN BDS_LOAD_OPTION *BootOption,
182 IN UINT32 Attributes,
183 IN CHAR16* BootDescription,
184 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,
185 IN BDS_LOADER_TYPE BootType,
186 IN CHAR8* BootArguments
187 )
188 {
189 EFI_LOAD_OPTION EfiLoadOption;
190 UINTN EfiLoadOptionSize;
191 UINTN BootDescriptionSize;
192 UINTN BootOptionalDataSize;
193 UINT16 FilePathListLength;
194 EFI_DEVICE_PATH_PROTOCOL* DevicePathNode;
195 UINTN NodeLength;
196 UINT8* EfiLoadOptionPtr;
197
198 // If we are overwriting an existent Boot Option then we have to free previously allocated memory
199 if (BootOption->LoadOption) {
200 FreePool(BootOption->LoadOption);
201 }
202
203 BootDescriptionSize = StrSize(BootDescription);
204 BootOptionalDataSize = sizeof(BDS_LOADER_OPTIONAL_DATA) +
205 (BootArguments == NULL ? 0 : AsciiStrSize(BootArguments));
206
207 // Compute the size of the FilePath list
208 FilePathListLength = 0;
209 DevicePathNode = DevicePath;
210 while (!IsDevicePathEndType (DevicePathNode)) {
211 FilePathListLength += DevicePathNodeLength (DevicePathNode);
212 DevicePathNode = NextDevicePathNode (DevicePathNode);
213 }
214 // Add the length of the DevicePath EndType
215 FilePathListLength += DevicePathNodeLength (DevicePathNode);
216
217 // Allocate the memory for the EFI Load Option
218 EfiLoadOptionSize = sizeof(UINT32) + sizeof(UINT16) + BootDescriptionSize + FilePathListLength + BootOptionalDataSize;
219 EfiLoadOption = (EFI_LOAD_OPTION)AllocatePool(EfiLoadOptionSize);
220 EfiLoadOptionPtr = EfiLoadOption;
221
222 //
223 // Populate the EFI Load Option and BDS Boot Option structures
224 //
225
226 // Attributes fields
227 BootOption->Attributes = Attributes;
228 *(UINT32*)EfiLoadOptionPtr = Attributes;
229 EfiLoadOptionPtr += sizeof(UINT32);
230
231 // FilePath List fields
232 BootOption->FilePathListLength = FilePathListLength;
233 *(UINT16*)EfiLoadOptionPtr = FilePathListLength;
234 EfiLoadOptionPtr += sizeof(UINT16);
235
236 // Boot description fields
237 BootOption->Description = (CHAR16*)EfiLoadOptionPtr;
238 CopyMem (EfiLoadOptionPtr, BootDescription, BootDescriptionSize);
239 EfiLoadOptionPtr += BootDescriptionSize;
240
241 // File path fields
242 BootOption->FilePathList = (EFI_DEVICE_PATH_PROTOCOL*)EfiLoadOptionPtr;
243 DevicePathNode = DevicePath;
244 while (!IsDevicePathEndType (DevicePathNode)) {
245 NodeLength = DevicePathNodeLength(DevicePathNode);
246 CopyMem (EfiLoadOptionPtr, DevicePathNode, NodeLength);
247 EfiLoadOptionPtr += NodeLength;
248 DevicePathNode = NextDevicePathNode (DevicePathNode);
249 }
250
251 // Set the End Device Path Type
252 SetDevicePathEndNode (EfiLoadOptionPtr);
253 EfiLoadOptionPtr = (UINT8 *)EfiLoadOptionPtr + sizeof(EFI_DEVICE_PATH);
254
255 // Optional Data fields, Do unaligned writes
256 WriteUnaligned32 ((UINT32 *)EfiLoadOptionPtr, BootType);
257
258 CopyMem (&((BDS_LOADER_OPTIONAL_DATA*)EfiLoadOptionPtr)->Arguments, BootArguments, AsciiStrSize(BootArguments));
259 BootOption->OptionalData = (BDS_LOADER_OPTIONAL_DATA *)EfiLoadOptionPtr;
260
261 // Fill the EFI Load option fields
262 BootOption->LoadOptionIndex = BootOptionAllocateBootIndex();
263 BootOption->LoadOption = EfiLoadOption;
264 BootOption->LoadOptionSize = EfiLoadOptionSize;
265
266 return EFI_SUCCESS;
267 }
268
269 EFI_STATUS
270 BootOptionCreate (
271 IN UINT32 Attributes,
272 IN CHAR16* BootDescription,
273 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,
274 IN BDS_LOADER_TYPE BootType,
275 IN CHAR8* BootArguments,
276 OUT BDS_LOAD_OPTION **BdsLoadOption
277 )
278 {
279 EFI_STATUS Status;
280 BDS_LOAD_OPTION *BootOption;
281 CHAR16 BootVariableName[9];
282 UINT16 *BootOrder;
283 UINTN BootOrderSize;
284
285 //
286 // Allocate and fill the memory for the BDS Load Option structure
287 //
288 BootOption = (BDS_LOAD_OPTION*)AllocateZeroPool(sizeof(BDS_LOAD_OPTION));
289
290 InitializeListHead (&BootOption->Link);
291 BootOptionSetFields (BootOption, Attributes, BootDescription, DevicePath, BootType, BootArguments);
292
293 //
294 // Set the related environment variables
295 //
296
297 // Create Boot#### environment variable
298 UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BootOption->LoadOptionIndex);
299 Status = gRT->SetVariable (
300 BootVariableName,
301 &gEfiGlobalVariableGuid,
302 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
303 BootOption->LoadOptionSize,
304 BootOption->LoadOption
305 );
306
307 // Add the new Boot Index to the list
308 Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
309 if (!EFI_ERROR(Status)) {
310 BootOrder = ReallocatePool (BootOrderSize, BootOrderSize + sizeof(UINT16), BootOrder);
311 // Add the new index at the end
312 BootOrder[BootOrderSize / sizeof(UINT16)] = BootOption->LoadOptionIndex;
313 BootOrderSize += sizeof(UINT16);
314 } else {
315 // BootOrder does not exist. Create it
316 BootOrderSize = sizeof(UINT16);
317 BootOrder = &(BootOption->LoadOptionIndex);
318 }
319
320 // Update (or Create) the BootOrder environment variable
321 Status = gRT->SetVariable (
322 L"BootOrder",
323 &gEfiGlobalVariableGuid,
324 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
325 BootOrderSize,
326 BootOrder
327 );
328
329 *BdsLoadOption = BootOption;
330 return Status;
331 }
332
333 EFI_STATUS
334 BootOptionUpdate (
335 IN BDS_LOAD_OPTION *BdsLoadOption,
336 IN UINT32 Attributes,
337 IN CHAR16* BootDescription,
338 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,
339 IN BDS_LOADER_TYPE BootType,
340 IN CHAR8* BootArguments
341 )
342 {
343 EFI_STATUS Status;
344 BDS_LOAD_OPTION *BootOption;
345 CHAR16 BootVariableName[9];
346
347 // Update the BDS Load Option structure
348 BootOptionSetFields (BdsLoadOption, Attributes, BootDescription, DevicePath, BootType, BootArguments);
349
350 // Update the related environment variables
351 UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BootOption->LoadOptionIndex);
352 Status = gRT->SetVariable (
353 BootVariableName,
354 &gEfiGlobalVariableGuid,
355 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
356 BootOption->LoadOptionSize,
357 BootOption->LoadOption
358 );
359
360 return Status;
361 }
362
363 EFI_STATUS
364 BootOptionDelete (
365 IN BDS_LOAD_OPTION *BootOption
366 )
367 {
368 UINTN Index;
369 UINTN BootOrderSize;
370 UINT16* BootOrder;
371 UINTN BootOrderCount;
372 EFI_STATUS Status;
373
374 // If the Boot Optiono was attached to a list remove it
375 if (!IsListEmpty (&BootOption->Link)) {
376 // Remove the entry from the list
377 RemoveEntryList (&BootOption->Link);
378 }
379
380 // Remove the entry from the BootOrder environment variable
381 Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
382 if (!EFI_ERROR(Status)) {
383 BootOrderCount = BootOrderSize / sizeof(UINT16);
384
385 // Find the index of the removed entry
386 for (Index = 0; Index < BootOrderCount; Index++) {
387 if (BootOrder[Index] == BootOption->LoadOptionIndex) {
388 // If it the last entry we do not need to rearrange the BootOrder list
389 if (Index + 1 != BootOrderCount) {
390 CopyMem (&BootOrder[Index],&BootOrder[Index+1], BootOrderCount - (Index + 1));
391 }
392 break;
393 }
394 }
395
396 // Update the BootOrder environment variable
397 Status = gRT->SetVariable (
398 L"BootOrder",
399 &gEfiGlobalVariableGuid,
400 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
401 BootOrderSize - sizeof(UINT16),
402 BootOrder
403 );
404 }
405
406 return EFI_SUCCESS;
407 }