3 * Copyright (c) 2011, ARM Limited. All rights reserved.
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
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.
15 #include "BdsInternal.h"
17 extern EFI_HANDLE mImageHandle
;
21 IN BDS_LOAD_OPTION
*BootOption
25 EFI_DEVICE_PATH
* FdtDevicePath
;
26 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL
* EfiDevicePathFromTextProtocol
;
28 ARM_BDS_LOADER_OPTIONAL_DATA
* OptionalData
;
29 ARM_BDS_LINUX_ARGUMENTS
* LinuxArguments
;
30 EFI_DEVICE_PATH_PROTOCOL
* FdtDevicePath
;
31 EFI_DEVICE_PATH_PROTOCOL
* DefaultFdtDevicePath
;
32 UINTN FdtDevicePathSize
;
35 EFI_DEVICE_PATH
* Initrd
;
37 Status
= EFI_UNSUPPORTED
;
38 OptionalData
= BootOption
->OptionalData
;
39 LoaderType
= ReadUnaligned32 ((CONST UINT32
*)&OptionalData
->Header
.LoaderType
);
41 if (LoaderType
== BDS_LOADER_EFI_APPLICATION
) {
42 // Need to connect every drivers to ensure no dependencies are missing for the application
43 BdsConnectAllDrivers();
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
);
52 Initrd
= GetAlignedDevicePath ((EFI_DEVICE_PATH
*)((UINTN
)(LinuxArguments
+ 1) + CmdLineSize
));
57 Status
= BdsBootLinux (BootOption
->FilePathList
,
59 (CHAR8
*)(LinuxArguments
+ 1), // CmdLine
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
);
67 Initrd
= GetAlignedDevicePath ((EFI_DEVICE_PATH
*)((UINTN
)(LinuxArguments
+ 1) + CmdLineSize
));
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
));
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
);
82 Status
= BdsBootLinux (BootOption
->FilePathList
,
84 (CHAR8
*)(LinuxArguments
+ 1),
91 BootOptionParseLoadOption (
92 IN EFI_LOAD_OPTION EfiLoadOption
,
93 IN UINTN EfiLoadOptionSize
,
94 OUT BDS_LOAD_OPTION
**BdsLoadOption
97 BDS_LOAD_OPTION
*LoadOption
;
98 UINTN FilePathListLength
;
99 UINTN DescriptionLength
;
101 if (EfiLoadOption
== NULL
) {
102 return EFI_INVALID_PARAMETER
;
105 if (EfiLoadOptionSize
< sizeof(UINT32
) + sizeof(UINT16
) + sizeof(CHAR16
) + sizeof(EFI_DEVICE_PATH_PROTOCOL
)) {
106 return EFI_BAD_BUFFER_SIZE
;
109 LoadOption
= (BDS_LOAD_OPTION
*)AllocatePool(sizeof(BDS_LOAD_OPTION
));
110 if (LoadOption
== NULL
) {
111 return EFI_OUT_OF_RESOURCES
;
114 LoadOption
->LoadOption
= EfiLoadOption
;
115 LoadOption
->LoadOptionSize
= EfiLoadOptionSize
;
117 LoadOption
->Attributes
= *(UINT32
*)EfiLoadOption
;
118 FilePathListLength
= *(UINT16
*)(EfiLoadOption
+ sizeof(UINT32
));
119 LoadOption
->Description
= (CHAR16
*)(EfiLoadOption
+ sizeof(UINT32
) + sizeof(UINT16
));
120 DescriptionLength
= StrSize (LoadOption
->Description
);
121 LoadOption
->FilePathList
= (EFI_DEVICE_PATH_PROTOCOL
*)(EfiLoadOption
+ sizeof(UINT32
) + sizeof(UINT16
) + DescriptionLength
);
123 if ((UINTN
)((UINT8
*)LoadOption
->FilePathList
+ FilePathListLength
- EfiLoadOption
) == EfiLoadOptionSize
) {
124 LoadOption
->OptionalData
= NULL
;
126 LoadOption
->OptionalData
= (BDS_LOADER_OPTIONAL_DATA
*)((UINT8
*)LoadOption
->FilePathList
+ FilePathListLength
);
129 *BdsLoadOption
= LoadOption
;
134 BootOptionFromLoadOptionVariable (
135 IN UINT16 LoadOptionIndex
,
136 OUT BDS_LOAD_OPTION
**BdsLoadOption
140 CHAR16 BootVariableName
[9];
141 EFI_LOAD_OPTION EfiLoadOption
;
142 UINTN EfiLoadOptionSize
;
144 UnicodeSPrint (BootVariableName
, 9 * sizeof(CHAR16
), L
"Boot%04X", LoadOptionIndex
);
146 Status
= GetEnvironmentVariable (BootVariableName
, NULL
, &EfiLoadOptionSize
, (VOID
**)&EfiLoadOption
);
147 if (!EFI_ERROR(Status
)) {
148 Status
= BootOptionParseLoadOption (EfiLoadOption
,EfiLoadOptionSize
,BdsLoadOption
);
149 if (!EFI_ERROR(Status
)) {
150 (*BdsLoadOption
)->LoadOptionIndex
= LoadOptionIndex
;
159 IN OUT LIST_ENTRY
*BootOptionList
166 BDS_LOAD_OPTION
*BdsLoadOption
;
168 InitializeListHead (BootOptionList
);
170 // Get the Boot Option Order from the environment variable
171 Status
= GetEnvironmentVariable (L
"BootOrder", NULL
, &BootOrderSize
, (VOID
**)&BootOrder
);
172 if (EFI_ERROR(Status
)) {
176 for (Index
= 0; Index
< BootOrderSize
/ sizeof (UINT16
); Index
++) {
177 Status
= BootOptionFromLoadOptionVariable (BootOrder
[Index
],&BdsLoadOption
);
178 if (!EFI_ERROR(Status
)) {
179 InsertTailList (BootOptionList
,&BdsLoadOption
->Link
);
187 BootOptionAllocateBootIndex (
198 // Get the Boot Option Order from the environment variable
199 Status
= GetEnvironmentVariable (L
"BootOrder", NULL
, &BootOrderSize
, (VOID
**)&BootOrder
);
200 if (!EFI_ERROR(Status
)) {
201 for (BootIndex
= 0; BootIndex
<= 0xFFFF; BootIndex
++) {
203 for (Index
= 0; Index
< BootOrderSize
/ sizeof (UINT16
); Index
++) {
204 if (BootOrder
[Index
] == BootIndex
) {
214 // Return the first index
220 BootOptionSetFields (
221 IN BDS_LOAD_OPTION
* BootOption
,
222 IN UINT32 Attributes
,
223 IN CHAR16
* BootDescription
,
224 IN EFI_DEVICE_PATH_PROTOCOL
* DevicePath
,
225 IN ARM_BDS_LOADER_TYPE BootType
,
226 IN ARM_BDS_LOADER_ARGUMENTS
* BootArguments
229 EFI_LOAD_OPTION EfiLoadOption
;
230 UINTN EfiLoadOptionSize
;
231 UINTN BootDescriptionSize
;
232 UINTN BootOptionalDataSize
;
233 UINT16 FilePathListLength
;
234 UINT8
* EfiLoadOptionPtr
;
235 UINT8
* InitrdPathListPtr
;
236 UINTN OptionalDataSize
;
237 ARM_BDS_LINUX_ARGUMENTS
* DestLinuxArguments
;
238 ARM_BDS_LINUX_ARGUMENTS
* SrcLinuxArguments
;
240 // If we are overwriting an existent Boot Option then we have to free previously allocated memory
241 if (BootOption
->LoadOption
) {
242 FreePool(BootOption
->LoadOption
);
245 BootDescriptionSize
= StrSize (BootDescription
);
246 BootOptionalDataSize
= sizeof(ARM_BDS_LOADER_OPTIONAL_DATA_HEADER
);
247 if ((BootType
== BDS_LOADER_KERNEL_LINUX_ATAG
) || (BootType
== BDS_LOADER_KERNEL_LINUX_FDT
)) {
248 BootOptionalDataSize
+= sizeof(ARM_BDS_LINUX_ARGUMENTS
) + BootArguments
->LinuxArguments
.CmdLineSize
+ BootArguments
->LinuxArguments
.InitrdSize
;
251 // Compute the size of the FilePath list
252 FilePathListLength
= GetUnalignedDevicePathSize (DevicePath
);
254 // Allocate the memory for the EFI Load Option
255 EfiLoadOptionSize
= sizeof(UINT32
) + sizeof(UINT16
) + BootDescriptionSize
+ FilePathListLength
+ BootOptionalDataSize
;
256 EfiLoadOption
= (EFI_LOAD_OPTION
)AllocatePool(EfiLoadOptionSize
);
257 EfiLoadOptionPtr
= EfiLoadOption
;
260 // Populate the EFI Load Option and BDS Boot Option structures
264 BootOption
->Attributes
= Attributes
;
265 *(UINT32
*)EfiLoadOptionPtr
= Attributes
;
266 EfiLoadOptionPtr
+= sizeof(UINT32
);
268 // FilePath List fields
269 BootOption
->FilePathListLength
= FilePathListLength
;
270 *(UINT16
*)EfiLoadOptionPtr
= FilePathListLength
;
271 EfiLoadOptionPtr
+= sizeof(UINT16
);
273 // Boot description fields
274 BootOption
->Description
= (CHAR16
*)EfiLoadOptionPtr
;
275 CopyMem (EfiLoadOptionPtr
, BootDescription
, BootDescriptionSize
);
276 EfiLoadOptionPtr
+= BootDescriptionSize
;
279 BootOption
->FilePathList
= (EFI_DEVICE_PATH_PROTOCOL
*)EfiLoadOptionPtr
;
280 CopyMem (EfiLoadOptionPtr
, DevicePath
, FilePathListLength
);
281 EfiLoadOptionPtr
+= FilePathListLength
;
283 // Optional Data fields, Do unaligned writes
284 BootOption
->OptionalData
= EfiLoadOptionPtr
;
285 WriteUnaligned32 ((UINT32
*)EfiLoadOptionPtr
, ARM_BDS_OPTIONAL_DATA_SIGNATURE
);
286 WriteUnaligned32 ((UINT32
*)(EfiLoadOptionPtr
+ 4), BootType
);
288 OptionalDataSize
= sizeof(ARM_BDS_LOADER_OPTIONAL_DATA_HEADER
);
290 if ((BootType
== BDS_LOADER_KERNEL_LINUX_ATAG
) || (BootType
== BDS_LOADER_KERNEL_LINUX_FDT
)) {
291 SrcLinuxArguments
= &(BootArguments
->LinuxArguments
);
292 DestLinuxArguments
= &((ARM_BDS_LOADER_OPTIONAL_DATA
*)EfiLoadOptionPtr
)->Arguments
.LinuxArguments
;
294 WriteUnaligned16 ((UINT16
*)&(DestLinuxArguments
->CmdLineSize
), SrcLinuxArguments
->CmdLineSize
);
295 WriteUnaligned16 ((UINT16
*)&(DestLinuxArguments
->InitrdSize
), SrcLinuxArguments
->InitrdSize
);
296 OptionalDataSize
+= sizeof (ARM_BDS_LINUX_ARGUMENTS
);
298 if (SrcLinuxArguments
->CmdLineSize
> 0) {
299 CopyMem ((VOID
*)(DestLinuxArguments
+ 1), (VOID
*)(SrcLinuxArguments
+ 1), SrcLinuxArguments
->CmdLineSize
);
300 OptionalDataSize
+= SrcLinuxArguments
->CmdLineSize
;
303 if (SrcLinuxArguments
->InitrdSize
> 0) {
304 InitrdPathListPtr
= (UINT8
*)((UINTN
)(DestLinuxArguments
+ 1) + SrcLinuxArguments
->CmdLineSize
);
305 CopyMem (InitrdPathListPtr
, (VOID
*)((UINTN
)(SrcLinuxArguments
+ 1) + SrcLinuxArguments
->CmdLineSize
), SrcLinuxArguments
->InitrdSize
);
308 BootOption
->OptionalDataSize
= OptionalDataSize
;
310 // If this function is called at the creation of the Boot Device entry (not at the update) the
311 // BootOption->LoadOptionSize must be zero then we get a new BootIndex for this entry
312 if (BootOption
->LoadOptionSize
== 0) {
313 BootOption
->LoadOptionIndex
= BootOptionAllocateBootIndex ();
316 // Fill the EFI Load option fields
317 BootOption
->LoadOption
= EfiLoadOption
;
318 BootOption
->LoadOptionSize
= EfiLoadOptionSize
;
325 IN UINT32 Attributes
,
326 IN CHAR16
* BootDescription
,
327 IN EFI_DEVICE_PATH_PROTOCOL
* DevicePath
,
328 IN ARM_BDS_LOADER_TYPE BootType
,
329 IN ARM_BDS_LOADER_ARGUMENTS
* BootArguments
,
330 OUT BDS_LOAD_OPTION
**BdsLoadOption
334 BDS_LOAD_OPTION
*BootOption
;
335 CHAR16 BootVariableName
[9];
340 // Allocate and fill the memory for the BDS Load Option structure
342 BootOption
= (BDS_LOAD_OPTION
*)AllocateZeroPool(sizeof(BDS_LOAD_OPTION
));
344 InitializeListHead (&BootOption
->Link
);
345 BootOptionSetFields (BootOption
, Attributes
, BootDescription
, DevicePath
, BootType
, BootArguments
);
348 // Set the related environment variables
351 // Create Boot#### environment variable
352 UnicodeSPrint (BootVariableName
, 9 * sizeof(CHAR16
), L
"Boot%04X", BootOption
->LoadOptionIndex
);
353 Status
= gRT
->SetVariable (
355 &gEfiGlobalVariableGuid
,
356 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
,
357 BootOption
->LoadOptionSize
,
358 BootOption
->LoadOption
361 // Add the new Boot Index to the list
362 Status
= GetEnvironmentVariable (L
"BootOrder", NULL
, &BootOrderSize
, (VOID
**)&BootOrder
);
363 if (!EFI_ERROR(Status
)) {
364 BootOrder
= ReallocatePool (BootOrderSize
, BootOrderSize
+ sizeof(UINT16
), BootOrder
);
365 // Add the new index at the end
366 BootOrder
[BootOrderSize
/ sizeof(UINT16
)] = BootOption
->LoadOptionIndex
;
367 BootOrderSize
+= sizeof(UINT16
);
369 // BootOrder does not exist. Create it
370 BootOrderSize
= sizeof(UINT16
);
371 BootOrder
= &(BootOption
->LoadOptionIndex
);
374 // Update (or Create) the BootOrder environment variable
375 Status
= gRT
->SetVariable (
377 &gEfiGlobalVariableGuid
,
378 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
,
383 *BdsLoadOption
= BootOption
;
389 IN BDS_LOAD_OPTION
* BdsLoadOption
,
390 IN UINT32 Attributes
,
391 IN CHAR16
* BootDescription
,
392 IN EFI_DEVICE_PATH_PROTOCOL
* DevicePath
,
393 IN ARM_BDS_LOADER_TYPE BootType
,
394 IN ARM_BDS_LOADER_ARGUMENTS
* BootArguments
398 CHAR16 BootVariableName
[9];
400 // Update the BDS Load Option structure
401 BootOptionSetFields (BdsLoadOption
, Attributes
, BootDescription
, DevicePath
, BootType
, BootArguments
);
403 // Update the related environment variables
404 UnicodeSPrint (BootVariableName
, 9 * sizeof(CHAR16
), L
"Boot%04X", BdsLoadOption
->LoadOptionIndex
);
406 Status
= gRT
->SetVariable (
408 &gEfiGlobalVariableGuid
,
409 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
,
410 BdsLoadOption
->LoadOptionSize
,
411 BdsLoadOption
->LoadOption
419 IN BDS_LOAD_OPTION
*BootOption
425 UINTN BootOrderCount
;
428 // If the Boot Optiono was attached to a list remove it
429 if (!IsListEmpty (&BootOption
->Link
)) {
430 // Remove the entry from the list
431 RemoveEntryList (&BootOption
->Link
);
434 // Remove the entry from the BootOrder environment variable
435 Status
= GetEnvironmentVariable (L
"BootOrder", NULL
, &BootOrderSize
, (VOID
**)&BootOrder
);
436 if (!EFI_ERROR(Status
)) {
437 BootOrderCount
= BootOrderSize
/ sizeof(UINT16
);
439 // Find the index of the removed entry
440 for (Index
= 0; Index
< BootOrderCount
; Index
++) {
441 if (BootOrder
[Index
] == BootOption
->LoadOptionIndex
) {
442 // If it the last entry we do not need to rearrange the BootOrder list
443 if (Index
+ 1 != BootOrderCount
) {
444 CopyMem (&BootOrder
[Index
],&BootOrder
[Index
+1], BootOrderCount
- (Index
+ 1));
450 // Update the BootOrder environment variable
451 Status
= gRT
->SetVariable (
453 &gEfiGlobalVariableGuid
,
454 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
,
455 BootOrderSize
- sizeof(UINT16
),