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