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
;
18 extern BDS_LOAD_OPTION_SUPPORT
*BdsLoadOptionSupportList
;
22 OUT BDS_SUPPORTED_DEVICE
** SupportedBootDevice
26 LIST_ENTRY SupportedDeviceList
;
27 UINTN SupportedDeviceCount
;
29 UINTN SupportedDeviceSelected
;
33 // List the Boot Devices supported
36 // Start all the drivers first
37 BdsConnectAllDrivers ();
39 // List the supported devices
40 Status
= BootDeviceListSupportedInit (&SupportedDeviceList
);
41 ASSERT_EFI_ERROR(Status
);
43 SupportedDeviceCount
= 0;
44 for (Entry
= GetFirstNode (&SupportedDeviceList
);
45 !IsNull (&SupportedDeviceList
,Entry
);
46 Entry
= GetNextNode (&SupportedDeviceList
,Entry
)
49 *SupportedBootDevice
= SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry
);
50 Print(L
"[%d] %s\n",SupportedDeviceCount
+1,(*SupportedBootDevice
)->Description
);
53 CHAR16
* DevicePathTxt
;
54 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
* DevicePathToTextProtocol
;
56 Status
= gBS
->LocateProtocol(&gEfiDevicePathToTextProtocolGuid
, NULL
, (VOID
**)&DevicePathToTextProtocol
);
57 ASSERT_EFI_ERROR(Status
);
58 DevicePathTxt
= DevicePathToTextProtocol
->ConvertDevicePathToText((*SupportedBootDevice
)->DevicePathProtocol
,TRUE
,TRUE
);
60 Print(L
"\t- %s\n",DevicePathTxt
);
62 FreePool(DevicePathTxt
);
65 SupportedDeviceCount
++;
68 if (SupportedDeviceCount
== 0) {
69 Print(L
"There is no supported device.\n");
75 // Select the Boot Device
77 SupportedDeviceSelected
= 0;
78 while (SupportedDeviceSelected
== 0) {
79 Print(L
"Select the Boot Device: ");
80 Status
= GetHIInputInteger (&SupportedDeviceSelected
);
81 if (EFI_ERROR(Status
)) {
84 } else if ((SupportedDeviceSelected
== 0) || (SupportedDeviceSelected
> SupportedDeviceCount
)) {
85 Print(L
"Invalid input (max %d)\n",SupportedDeviceSelected
);
86 SupportedDeviceSelected
= 0;
91 // Get the Device Path for the selected boot device
94 for (Entry
= GetFirstNode (&SupportedDeviceList
);
95 !IsNull (&SupportedDeviceList
,Entry
);
96 Entry
= GetNextNode (&SupportedDeviceList
,Entry
)
99 if (Index
== SupportedDeviceSelected
) {
100 *SupportedBootDevice
= SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry
);
107 BootDeviceListSupportedFree (&SupportedDeviceList
, *SupportedBootDevice
);
112 BootMenuAddBootOption (
113 IN LIST_ENTRY
*BootOptionsList
117 BDS_SUPPORTED_DEVICE
* SupportedBootDevice
;
118 ARM_BDS_LOADER_ARGUMENTS
* BootArguments
;
119 CHAR16 BootDescription
[BOOT_DEVICE_DESCRIPTION_MAX
];
120 CHAR8 CmdLine
[BOOT_DEVICE_OPTION_MAX
];
122 ARM_BDS_LOADER_TYPE BootType
;
123 BDS_LOAD_OPTION
*BdsLoadOption
;
124 EFI_DEVICE_PATH
*DevicePath
;
125 EFI_DEVICE_PATH_PROTOCOL
*DevicePathNode
;
126 EFI_DEVICE_PATH_PROTOCOL
*InitrdPathNode
;
127 EFI_DEVICE_PATH_PROTOCOL
*InitrdPath
;
132 SupportedBootDevice
= NULL
;
134 // List the Boot Devices supported
135 Status
= SelectBootDevice (&SupportedBootDevice
);
136 if (EFI_ERROR(Status
)) {
137 Status
= EFI_ABORTED
;
141 // Create the specific device path node
142 Print(L
"File path of the EFI Application or the kernel: ");
143 Status
= SupportedBootDevice
->Support
->CreateDevicePathNode (SupportedBootDevice
, &DevicePathNode
, &BootType
, &Attributes
);
144 if (EFI_ERROR(Status
)) {
145 Status
= EFI_ABORTED
;
148 // Append the Device Path node to the select device path
149 DevicePath
= AppendDevicePathNode (SupportedBootDevice
->DevicePathProtocol
, (CONST EFI_DEVICE_PATH_PROTOCOL
*)DevicePathNode
);
151 if ((BootType
== BDS_LOADER_KERNEL_LINUX_ATAG
) || (BootType
== BDS_LOADER_KERNEL_LINUX_FDT
)) {
152 // Create the specific device path node
153 Print(L
"File path of the initrd: ");
154 Status
= SupportedBootDevice
->Support
->CreateDevicePathNode (SupportedBootDevice
, &InitrdPathNode
, NULL
, NULL
);
155 if (EFI_ERROR(Status
) && Status
!= EFI_NOT_FOUND
) { // EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd
156 Status
= EFI_ABORTED
;
160 if (InitrdPathNode
!= NULL
) {
161 // Append the Device Path node to the select device path
162 InitrdPath
= AppendDevicePathNode (SupportedBootDevice
->DevicePathProtocol
, (CONST EFI_DEVICE_PATH_PROTOCOL
*)InitrdPathNode
);
167 Print(L
"Arguments to pass to the binary: ");
168 Status
= GetHIInputAscii (CmdLine
,BOOT_DEVICE_OPTION_MAX
);
169 if (EFI_ERROR(Status
)) {
170 Status
= EFI_ABORTED
;
171 goto FREE_DEVICE_PATH
;
174 CmdLineSize
= AsciiStrSize (CmdLine
);
175 InitrdSize
= GetDevicePathSize (InitrdPath
);
177 BootArguments
= (ARM_BDS_LOADER_ARGUMENTS
*)AllocatePool (sizeof(ARM_BDS_LOADER_ARGUMENTS
) + CmdLineSize
+ InitrdSize
);
179 BootArguments
->LinuxArguments
.CmdLineSize
= CmdLineSize
;
180 BootArguments
->LinuxArguments
.InitrdSize
= InitrdSize
;
181 CopyMem ((VOID
*)(&BootArguments
->LinuxArguments
+ 1), CmdLine
, CmdLineSize
);
182 CopyMem ((VOID
*)((UINTN
)(&BootArguments
->LinuxArguments
+ 1) + CmdLineSize
), InitrdPath
, InitrdSize
);
184 BootArguments
= NULL
;
187 Print(L
"Description for this new Entry: ");
188 Status
= GetHIInputStr (BootDescription
, BOOT_DEVICE_DESCRIPTION_MAX
);
189 if (EFI_ERROR(Status
)) {
190 Status
= EFI_ABORTED
;
191 goto FREE_DEVICE_PATH
;
195 Status
= BootOptionCreate (Attributes
, BootDescription
, DevicePath
, BootType
, &BootArguments
, &BdsLoadOption
);
196 if (!EFI_ERROR(Status
)) {
197 InsertTailList (BootOptionsList
,&BdsLoadOption
->Link
);
201 FreePool (DevicePath
);
205 if (Status
== EFI_ABORTED
) {
208 FreePool(SupportedBootDevice
);
214 BootMenuSelectBootOption (
215 IN LIST_ENTRY
*BootOptionsList
,
216 IN CONST CHAR16
* InputStatement
,
217 OUT BDS_LOAD_OPTION
**BdsLoadOption
222 BDS_LOAD_OPTION
*BootOption
;
223 UINTN BootOptionSelected
;
224 UINTN BootOptionCount
;
227 // Display the list of supported boot devices
229 for (Entry
= GetFirstNode (BootOptionsList
);
230 !IsNull (BootOptionsList
,Entry
);
231 Entry
= GetNextNode (BootOptionsList
,Entry
)
234 BootOption
= LOAD_OPTION_FROM_LINK(Entry
);
235 Print(L
"[%d] %s\n",BootOptionCount
,BootOption
->Description
);
238 CHAR16
* DevicePathTxt
;
239 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
* DevicePathToTextProtocol
;
240 ARM_BDS_LOADER_TYPE LoaderType
;
242 Status
= gBS
->LocateProtocol(&gEfiDevicePathToTextProtocolGuid
, NULL
, (VOID
**)&DevicePathToTextProtocol
);
243 ASSERT_EFI_ERROR(Status
);
244 DevicePathTxt
= DevicePathToTextProtocol
->ConvertDevicePathToText(BootOption
->FilePathList
,TRUE
,TRUE
);
246 Print(L
"\t- %s\n",DevicePathTxt
);
247 OptionalData
= BdsLoadOption
->OptionalData
;
248 LoaderType
= (ARM_BDS_LOADER_TYPE
)ReadUnaligned32 ((CONST UINT32
*)&OptionalData
->Header
.LoaderType
);
249 if ((LoaderType
== BDS_LOADER_KERNEL_LINUX_ATAG
) || (LoaderType
== BDS_LOADER_KERNEL_LINUX_FDT
)) {
250 Print (L
"\t- Arguments: %a\n",&OptionalData
->Arguments
.LinuxArguments
+ 1);
253 FreePool(DevicePathTxt
);
259 // Get the index of the boot device to delete
260 BootOptionSelected
= 0;
261 while (BootOptionSelected
== 0) {
262 Print(InputStatement
);
263 Status
= GetHIInputInteger (&BootOptionSelected
);
264 if (EFI_ERROR(Status
)) {
266 } else if ((BootOptionSelected
== 0) || (BootOptionSelected
>= BootOptionCount
)) {
267 Print(L
"Invalid input (max %d)\n",BootOptionCount
-1);
268 BootOptionSelected
= 0;
272 // Get the structure of the Boot device to delete
274 for (Entry
= GetFirstNode (BootOptionsList
);
275 !IsNull (BootOptionsList
,Entry
);
276 Entry
= GetNextNode (BootOptionsList
,Entry
)
279 if (Index
== BootOptionSelected
) {
280 *BdsLoadOption
= LOAD_OPTION_FROM_LINK(Entry
);
290 BootMenuRemoveBootOption (
291 IN LIST_ENTRY
*BootOptionsList
295 BDS_LOAD_OPTION
*BootOption
;
297 Status
= BootMenuSelectBootOption (BootOptionsList
,L
"Delete entry: ",&BootOption
);
298 if (EFI_ERROR(Status
)) {
302 // Delete the BDS Load option structures
303 BootOptionDelete (BootOption
);
309 BootMenuUpdateBootOption (
310 IN LIST_ENTRY
*BootOptionsList
314 BDS_LOAD_OPTION
*BootOption
;
315 BDS_LOAD_OPTION_SUPPORT
* DeviceSupport
;
316 ARM_BDS_LOADER_ARGUMENTS
* BootArguments
;
317 CHAR16 BootDescription
[BOOT_DEVICE_DESCRIPTION_MAX
];
318 CHAR8 CmdLine
[BOOT_DEVICE_OPTION_MAX
];
319 EFI_DEVICE_PATH
* DevicePath
;
320 ARM_BDS_LOADER_TYPE BootType
;
321 ARM_BDS_LOADER_OPTIONAL_DATA
* OptionalData
;
322 ARM_BDS_LINUX_ARGUMENTS
* LinuxArguments
;
323 EFI_DEVICE_PATH
* InitrdPathList
;
327 Status
= BootMenuSelectBootOption (BootOptionsList
,L
"Update entry: ",&BootOption
);
328 if (EFI_ERROR(Status
)) {
332 // Get the device support for this Boot Option
333 Status
= BootDeviceGetDeviceSupport (BootOption
,&DeviceSupport
);
334 if (EFI_ERROR(Status
)) {
335 Print(L
"Impossible to retrieve the supported device for the update\n");
336 return EFI_UNSUPPORTED
;
339 Print(L
"File path of the EFI Application or the kernel: ");
340 Status
= DeviceSupport
->UpdateDevicePathNode (BootOption
->FilePathList
, &DevicePath
, NULL
, NULL
);
341 if (EFI_ERROR(Status
)) {
342 Status
= EFI_ABORTED
;
346 OptionalData
= BootOption
->OptionalData
;
347 BootType
= (ARM_BDS_LOADER_TYPE
)ReadUnaligned32 ((UINT32
*)(&OptionalData
->Header
.LoaderType
));
349 // TODO: Allow adding an initrd to a boot entry without one
350 if ((BootType
== BDS_LOADER_KERNEL_LINUX_ATAG
) || (BootType
== BDS_LOADER_KERNEL_LINUX_FDT
)) {
351 LinuxArguments
= &OptionalData
->Arguments
.LinuxArguments
;
353 CmdLineSize
= ReadUnaligned16 ((CONST UINT16
*)&LinuxArguments
->CmdLineSize
);
354 InitrdSize
= GetUnalignedDevicePathSize ((EFI_DEVICE_PATH
*)((LinuxArguments
+ 1) + CmdLineSize
));
356 Print(L
"File path of the initrd: ");
357 Status
= DeviceSupport
->UpdateDevicePathNode (
358 (EFI_DEVICE_PATH_PROTOCOL
*)((UINTN
)(LinuxArguments
+ 1) + CmdLineSize
), &InitrdPathList
, NULL
, NULL
);
359 if (EFI_ERROR(Status
) && Status
!= EFI_NOT_FOUND
) {// EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd
360 Status
= EFI_ABORTED
;
364 Print(L
"Arguments to pass to the binary: ");
365 if (CmdLineSize
> 0) {
366 AsciiStrnCpy(CmdLine
, (CONST CHAR8
*)(LinuxArguments
+ 1), CmdLineSize
);
370 Status
= EditHIInputAscii (CmdLine
, BOOT_DEVICE_OPTION_MAX
);
371 if (EFI_ERROR(Status
)) {
372 Status
= EFI_ABORTED
;
373 goto FREE_DEVICE_PATH
;
376 CmdLineSize
= AsciiStrSize (CmdLine
);
377 InitrdSize
= GetDevicePathSize (InitrdPathList
);
379 BootArguments
= (ARM_BDS_LOADER_ARGUMENTS
*)AllocatePool(sizeof(ARM_BDS_LOADER_ARGUMENTS
) + CmdLineSize
+ InitrdSize
);
380 BootArguments
->LinuxArguments
.CmdLineSize
= CmdLineSize
;
381 BootArguments
->LinuxArguments
.InitrdSize
= InitrdSize
;
382 CopyMem (&BootArguments
->LinuxArguments
+ 1, CmdLine
, CmdLineSize
);
383 CopyMem ((UINTN
)(&BootArguments
->LinuxArguments
+ 1) + CmdLine
, InitrdPathList
, InitrdSize
);
385 BootArguments
= NULL
;
388 Print(L
"Description for this new Entry: ");
389 Status
= EditHIInputStr (BootDescription
, BOOT_DEVICE_DESCRIPTION_MAX
);
390 if (EFI_ERROR(Status
)) {
391 Status
= EFI_ABORTED
;
392 goto FREE_DEVICE_PATH
;
396 Status
= BootOptionUpdate (BootOption
, BootOption
->Attributes
, BootDescription
, DevicePath
, BootType
, BootArguments
);
399 FreePool (DevicePath
);
402 if (Status
== EFI_ABORTED
) {
408 struct BOOT_MANAGER_ENTRY
{
409 CONST CHAR16
* Description
;
410 EFI_STATUS (*Callback
) (IN LIST_ENTRY
*BootOptionsList
);
411 } BootManagerEntries
[] = {
412 { L
"Add Boot Device Entry", BootMenuAddBootOption
},
413 { L
"Update Boot Device Entry", BootMenuUpdateBootOption
},
414 { L
"Remove Boot Device Entry", BootMenuRemoveBootOption
},
419 IN LIST_ENTRY
*BootOptionsList
423 UINTN OptionSelected
;
424 UINTN BootManagerEntryCount
;
427 BootManagerEntryCount
= sizeof(BootManagerEntries
) / sizeof(struct BOOT_MANAGER_ENTRY
);
430 // Display Boot Manager menu
431 for (Index
= 0; Index
< BootManagerEntryCount
; Index
++) {
432 Print(L
"[%d] %s\n",Index
+1,BootManagerEntries
[Index
]);
434 Print(L
"[%d] Return to main menu\n",Index
+1);
436 // Select which entry to call
438 Status
= GetHIInputInteger (&OptionSelected
);
439 if (EFI_ERROR(Status
) || (OptionSelected
== (BootManagerEntryCount
+1))) {
440 if (EFI_ERROR(Status
)) {
444 } else if ((OptionSelected
> 0) && (OptionSelected
<= BootManagerEntryCount
)) {
445 Status
= BootManagerEntries
[OptionSelected
-1].Callback (BootOptionsList
);
454 IN LIST_ENTRY
*BootOptionsList
460 Status
= BdsLoadApplication(mImageHandle
, L
"Ebl");
461 if (Status
== EFI_NOT_FOUND
) {
462 Print (L
"Error: EFI Application not found.\n");
463 } else if (EFI_ERROR(Status
)) {
464 Print (L
"Error: Status Code: 0x%X\n",(UINT32
)Status
);
470 struct BOOT_MAIN_ENTRY
{
471 CONST CHAR16
* Description
;
472 EFI_STATUS (*Callback
) (IN LIST_ENTRY
*BootOptionsList
);
473 } BootMainEntries
[] = {
475 { L
"Boot Manager", BootMenuManager
},
484 LIST_ENTRY BootOptionsList
;
486 UINTN BootOptionCount
;
489 BDS_LOAD_OPTION
* BootOption
;
490 UINTN BootOptionSelected
;
492 UINTN BootMainEntryCount
;
495 BootMainEntryCount
= sizeof(BootMainEntries
) / sizeof(struct BOOT_MAIN_ENTRY
);
498 BootOptionList (&BootOptionsList
);
503 // Display the Boot options
504 for (Entry
= GetFirstNode (&BootOptionsList
);
505 !IsNull (&BootOptionsList
,Entry
);
506 Entry
= GetNextNode (&BootOptionsList
,Entry
)
509 BootOption
= LOAD_OPTION_FROM_LINK(Entry
);
511 Print(L
"[%d] %s\n",OptionCount
,BootOption
->Description
);
514 CHAR16
* DevicePathTxt
;
515 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
* DevicePathToTextProtocol
;
516 ARM_BDS_LOADER_OPTIONAL_DATA
* OptionalData
;
518 ARM_BDS_LOADER_TYPE LoaderType
;
520 Status
= gBS
->LocateProtocol(&gEfiDevicePathToTextProtocolGuid
, NULL
, (VOID
**)&DevicePathToTextProtocol
);
521 if (EFI_ERROR(Status
)) {
522 // You must provide an implementation of DevicePathToTextProtocol in your firmware (eg: DevicePathDxe)
523 DEBUG((EFI_D_ERROR
,"Error: Bds requires DevicePathToTextProtocol\n"));
526 DevicePathTxt
= DevicePathToTextProtocol
->ConvertDevicePathToText(BootOption
->FilePathList
,TRUE
,TRUE
);
528 Print(L
"\t- %s\n",DevicePathTxt
);
530 // If it is a supported BootEntry then print its details
531 if (IS_ARM_BDS_BOOTENTRY (BootOption
)) {
532 OptionalData
= BootOption
->OptionalData
;
533 LoaderType
= (ARM_BDS_LOADER_TYPE
)ReadUnaligned32 ((CONST UINT32
*)&OptionalData
->Header
.LoaderType
);
534 if ((LoaderType
== BDS_LOADER_KERNEL_LINUX_ATAG
) || (LoaderType
== BDS_LOADER_KERNEL_LINUX_FDT
)) {
535 if (ReadUnaligned16 (&OptionalData
->Arguments
.LinuxArguments
.InitrdSize
) > 0) {
536 CmdLineSize
= ReadUnaligned16 (&OptionalData
->Arguments
.LinuxArguments
.CmdLineSize
);
537 DevicePathTxt
= DevicePathToTextProtocol
->ConvertDevicePathToText (
538 GetAlignedDevicePath ((EFI_DEVICE_PATH
*)((UINTN
)(&OptionalData
->Arguments
.LinuxArguments
+ 1) + CmdLineSize
)), TRUE
, TRUE
);
539 Print(L
"\t- Initrd: %s\n", DevicePathTxt
);
541 Print(L
"\t- Arguments: %a\n", (&OptionalData
->Arguments
.LinuxArguments
+ 1));
543 Print(L
"\t- LoaderType: %d\n", LoaderType
);
545 FreePool(DevicePathTxt
);
550 BootOptionCount
= OptionCount
-1;
552 // Display the hardcoded Boot entries
553 for (Index
= 0; Index
< BootMainEntryCount
; Index
++) {
554 Print(L
"[%d] %s\n",OptionCount
,BootMainEntries
[Index
]);
558 // Request the boot entry from the user
559 BootOptionSelected
= 0;
560 while (BootOptionSelected
== 0) {
562 Status
= GetHIInputInteger (&BootOptionSelected
);
563 if (EFI_ERROR(Status
) || (BootOptionSelected
== 0) || (BootOptionSelected
> OptionCount
)) {
564 Print(L
"Invalid input (max %d)\n",(OptionCount
-1));
565 BootOptionSelected
= 0;
569 // Start the selected entry
570 if (BootOptionSelected
> BootOptionCount
) {
571 // Start the hardcoded entry
572 Status
= BootMainEntries
[BootOptionSelected
- BootOptionCount
- 1].Callback (&BootOptionsList
);
574 // Find the selected entry from the Boot#### list
576 for (Entry
= GetFirstNode (&BootOptionsList
);
577 !IsNull (&BootOptionsList
,Entry
);
578 Entry
= GetNextNode (&BootOptionsList
,Entry
)
581 if (Index
== BootOptionSelected
) {
582 BootOption
= LOAD_OPTION_FROM_LINK(Entry
);
588 Status
= BootOptionStart (BootOption
);