]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Bds/BootMenu.c
ArmPlatformPkg/Bds: Fix setting kernel command line
[mirror_edk2.git] / ArmPlatformPkg / Bds / BootMenu.c
1 /** @file
2 *
3 * Copyright (c) 2011 - 2014, 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 #include <Guid/ArmGlobalVariableHob.h>
18
19 extern EFI_HANDLE mImageHandle;
20 extern BDS_LOAD_OPTION_SUPPORT *BdsLoadOptionSupportList;
21
22
23 EFI_STATUS
24 SelectBootDevice (
25 OUT BDS_SUPPORTED_DEVICE** SupportedBootDevice
26 )
27 {
28 EFI_STATUS Status;
29 LIST_ENTRY SupportedDeviceList;
30 UINTN SupportedDeviceCount;
31 LIST_ENTRY* Entry;
32 UINTN SupportedDeviceSelected;
33 UINTN Index;
34
35 //
36 // List the Boot Devices supported
37 //
38
39 // Start all the drivers first
40 BdsConnectAllDrivers ();
41
42 // List the supported devices
43 Status = BootDeviceListSupportedInit (&SupportedDeviceList);
44 ASSERT_EFI_ERROR(Status);
45
46 SupportedDeviceCount = 0;
47 for (Entry = GetFirstNode (&SupportedDeviceList);
48 !IsNull (&SupportedDeviceList,Entry);
49 Entry = GetNextNode (&SupportedDeviceList,Entry)
50 )
51 {
52 *SupportedBootDevice = SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry);
53 Print(L"[%d] %s\n",SupportedDeviceCount+1,(*SupportedBootDevice)->Description);
54
55 DEBUG_CODE_BEGIN();
56 CHAR16* DevicePathTxt;
57 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
58
59 Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
60 ASSERT_EFI_ERROR(Status);
61 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText ((*SupportedBootDevice)->DevicePathProtocol,TRUE,TRUE);
62
63 Print(L"\t- %s\n",DevicePathTxt);
64
65 FreePool(DevicePathTxt);
66 DEBUG_CODE_END();
67
68 SupportedDeviceCount++;
69 }
70
71 if (SupportedDeviceCount == 0) {
72 Print(L"There is no supported device.\n");
73 Status = EFI_ABORTED;
74 goto EXIT;
75 }
76
77 //
78 // Select the Boot Device
79 //
80 SupportedDeviceSelected = 0;
81 while (SupportedDeviceSelected == 0) {
82 Print(L"Select the Boot Device: ");
83 Status = GetHIInputInteger (&SupportedDeviceSelected);
84 if (EFI_ERROR(Status)) {
85 Status = EFI_ABORTED;
86 goto EXIT;
87 } else if ((SupportedDeviceSelected == 0) || (SupportedDeviceSelected > SupportedDeviceCount)) {
88 Print(L"Invalid input (max %d)\n",SupportedDeviceCount);
89 SupportedDeviceSelected = 0;
90 }
91 }
92
93 //
94 // Get the Device Path for the selected boot device
95 //
96 Index = 1;
97 for (Entry = GetFirstNode (&SupportedDeviceList);
98 !IsNull (&SupportedDeviceList,Entry);
99 Entry = GetNextNode (&SupportedDeviceList,Entry)
100 )
101 {
102 if (Index == SupportedDeviceSelected) {
103 *SupportedBootDevice = SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry);
104 break;
105 }
106 Index++;
107 }
108
109 EXIT:
110 BootDeviceListSupportedFree (&SupportedDeviceList, *SupportedBootDevice);
111 return Status;
112 }
113
114 EFI_STATUS
115 BootMenuAddBootOption (
116 IN LIST_ENTRY *BootOptionsList
117 )
118 {
119 EFI_STATUS Status;
120 BDS_SUPPORTED_DEVICE* SupportedBootDevice;
121 ARM_BDS_LOADER_ARGUMENTS* BootArguments;
122 CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX];
123 CHAR8 AsciiCmdLine[BOOT_DEVICE_OPTION_MAX];
124 CHAR16 CmdLine[BOOT_DEVICE_OPTION_MAX];
125 UINT32 Attributes;
126 ARM_BDS_LOADER_TYPE BootType;
127 BDS_LOAD_OPTION_ENTRY *BdsLoadOptionEntry;
128 EFI_DEVICE_PATH *DevicePath;
129 EFI_DEVICE_PATH_PROTOCOL *DevicePathNodes;
130 EFI_DEVICE_PATH_PROTOCOL *InitrdPathNodes;
131 EFI_DEVICE_PATH_PROTOCOL *InitrdPath;
132 UINTN CmdLineSize;
133 BOOLEAN InitrdSupport;
134 UINTN InitrdSize;
135 UINT8* OptionalData;
136 UINTN OptionalDataSize;
137 BOOLEAN RequestBootType;
138
139 Attributes = 0;
140 SupportedBootDevice = NULL;
141
142 // List the Boot Devices supported
143 Status = SelectBootDevice (&SupportedBootDevice);
144 if (EFI_ERROR(Status)) {
145 Status = EFI_ABORTED;
146 goto EXIT;
147 }
148
149 // Create the specific device path node
150 RequestBootType = TRUE;
151 Status = SupportedBootDevice->Support->CreateDevicePathNode (L"EFI Application or the kernel", &DevicePathNodes, &RequestBootType);
152 if (EFI_ERROR(Status)) {
153 Status = EFI_ABORTED;
154 goto EXIT;
155 }
156 // Append the Device Path to the selected device path
157 DevicePath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)DevicePathNodes);
158 if (DevicePath == NULL) {
159 Status = EFI_OUT_OF_RESOURCES;
160 goto EXIT;
161 }
162
163 if (RequestBootType) {
164 Status = BootDeviceGetType (DevicePath, &BootType, &Attributes);
165 if (EFI_ERROR(Status)) {
166 Status = EFI_ABORTED;
167 goto EXIT;
168 }
169 } else {
170 BootType = BDS_LOADER_EFI_APPLICATION;
171 }
172
173 if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) {
174 Print(L"Add an initrd: ");
175 Status = GetHIInputBoolean (&InitrdSupport);
176 if (EFI_ERROR(Status)) {
177 Status = EFI_ABORTED;
178 goto EXIT;
179 }
180
181 if (InitrdSupport) {
182 // Create the specific device path node
183 Status = SupportedBootDevice->Support->CreateDevicePathNode (L"initrd", &InitrdPathNodes, NULL);
184 if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) { // EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd
185 Status = EFI_ABORTED;
186 goto EXIT;
187 }
188
189 if (InitrdPathNodes != NULL) {
190 // Append the Device Path to the selected device path
191 InitrdPath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)InitrdPathNodes);
192 if (InitrdPath == NULL) {
193 Status = EFI_OUT_OF_RESOURCES;
194 goto EXIT;
195 }
196 } else {
197 InitrdPath = NULL;
198 }
199 } else {
200 InitrdPath = NULL;
201 }
202
203 Print(L"Arguments to pass to the binary: ");
204 Status = GetHIInputAscii (AsciiCmdLine, BOOT_DEVICE_OPTION_MAX);
205 if (EFI_ERROR(Status)) {
206 Status = EFI_ABORTED;
207 goto FREE_DEVICE_PATH;
208 }
209
210 CmdLineSize = AsciiStrSize (AsciiCmdLine);
211 InitrdSize = GetDevicePathSize (InitrdPath);
212
213 OptionalDataSize = sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize;
214 BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool (OptionalDataSize);
215
216 BootArguments->LinuxArguments.CmdLineSize = CmdLineSize;
217 BootArguments->LinuxArguments.InitrdSize = InitrdSize;
218 CopyMem ((VOID*)(&BootArguments->LinuxArguments + 1), AsciiCmdLine, CmdLineSize);
219 CopyMem ((VOID*)((UINTN)(&BootArguments->LinuxArguments + 1) + CmdLineSize), InitrdPath, InitrdSize);
220
221 OptionalData = (UINT8*)BootArguments;
222 } else {
223 Print (L"Arguments to pass to the EFI Application: ");
224 Status = GetHIInputStr (CmdLine, BOOT_DEVICE_OPTION_MAX);
225 if (EFI_ERROR (Status)) {
226 Status = EFI_ABORTED;
227 goto EXIT;
228 }
229
230 OptionalData = (UINT8*)CmdLine;
231 OptionalDataSize = StrSize (CmdLine);
232 }
233
234 Print(L"Description for this new Entry: ");
235 Status = GetHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX);
236 if (EFI_ERROR(Status)) {
237 Status = EFI_ABORTED;
238 goto FREE_DEVICE_PATH;
239 }
240
241 // Create new entry
242 BdsLoadOptionEntry = (BDS_LOAD_OPTION_ENTRY*)AllocatePool (sizeof(BDS_LOAD_OPTION_ENTRY));
243 Status = BootOptionCreate (Attributes, BootDescription, DevicePath, BootType, OptionalData, OptionalDataSize, &BdsLoadOptionEntry->BdsLoadOption);
244 if (!EFI_ERROR(Status)) {
245 InsertTailList (BootOptionsList, &BdsLoadOptionEntry->Link);
246 }
247
248 FREE_DEVICE_PATH:
249 FreePool (DevicePath);
250
251 EXIT:
252 if (Status == EFI_ABORTED) {
253 Print(L"\n");
254 }
255 FreePool(SupportedBootDevice);
256 return Status;
257 }
258
259 STATIC
260 EFI_STATUS
261 BootMenuSelectBootOption (
262 IN LIST_ENTRY* BootOptionsList,
263 IN CONST CHAR16* InputStatement,
264 OUT BDS_LOAD_OPTION_ENTRY** BdsLoadOptionEntry
265 )
266 {
267 EFI_STATUS Status;
268 LIST_ENTRY* Entry;
269 BDS_LOAD_OPTION* BdsLoadOption;
270 UINTN BootOptionSelected;
271 UINTN BootOptionCount;
272 UINTN Index;
273 BOOLEAN IsUnicode;
274
275 // Display the list of supported boot devices
276 BootOptionCount = 0;
277 for (Entry = GetFirstNode (BootOptionsList);
278 !IsNull (BootOptionsList,Entry);
279 Entry = GetNextNode (BootOptionsList, Entry)
280 )
281 {
282 BdsLoadOption = LOAD_OPTION_FROM_LINK(Entry);
283
284 Print (L"[%d] %s\n", (BootOptionCount + 1), BdsLoadOption->Description);
285
286 DEBUG_CODE_BEGIN();
287 CHAR16* DevicePathTxt;
288 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
289 ARM_BDS_LOADER_TYPE LoaderType;
290 ARM_BDS_LOADER_OPTIONAL_DATA* OptionalData;
291
292 Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
293 ASSERT_EFI_ERROR(Status);
294 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText(BdsLoadOption->FilePathList,TRUE,TRUE);
295
296 Print(L"\t- %s\n",DevicePathTxt);
297 OptionalData = BdsLoadOption->OptionalData;
298 if (IS_ARM_BDS_BOOTENTRY (BdsLoadOption)) {
299 LoaderType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((CONST UINT32*)&OptionalData->Header.LoaderType);
300 if ((LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) || (LoaderType == BDS_LOADER_KERNEL_LINUX_FDT)) {
301 Print (L"\t- Arguments: %a\n",&OptionalData->Arguments.LinuxArguments + 1);
302 }
303 } else if (OptionalData != NULL) {
304 if (IsPrintableString (OptionalData, &IsUnicode)) {
305 if (IsUnicode) {
306 Print (L"\t- Arguments: %s\n", OptionalData);
307 } else {
308 AsciiPrint ("\t- Arguments: %a\n", OptionalData);
309 }
310 }
311 }
312
313 FreePool(DevicePathTxt);
314 DEBUG_CODE_END();
315
316 BootOptionCount++;
317 }
318
319 // Check if a valid boot option(s) is found
320 if (BootOptionCount == 0) {
321 if (StrCmp (InputStatement, DELETE_BOOT_ENTRY) == 0) {
322 Print (L"Nothing to remove!\n");
323 } else if (StrCmp (InputStatement, UPDATE_BOOT_ENTRY) == 0) {
324 Print (L"Couldn't find valid boot entries\n");
325 } else{
326 Print (L"No supported Boot Entry.\n");
327 }
328
329 return EFI_NOT_FOUND;
330 }
331
332 // Get the index of the boot device to delete
333 BootOptionSelected = 0;
334 while (BootOptionSelected == 0) {
335 Print(InputStatement);
336 Status = GetHIInputInteger (&BootOptionSelected);
337 if (EFI_ERROR(Status)) {
338 return Status;
339 } else if ((BootOptionSelected == 0) || (BootOptionSelected > BootOptionCount)) {
340 Print(L"Invalid input (max %d)\n",BootOptionCount);
341 BootOptionSelected = 0;
342 }
343 }
344
345 // Get the structure of the Boot device to delete
346 Index = 1;
347 for (Entry = GetFirstNode (BootOptionsList);
348 !IsNull (BootOptionsList, Entry);
349 Entry = GetNextNode (BootOptionsList,Entry)
350 )
351 {
352 if (Index == BootOptionSelected) {
353 *BdsLoadOptionEntry = LOAD_OPTION_ENTRY_FROM_LINK(Entry);
354 break;
355 }
356 Index++;
357 }
358
359 return EFI_SUCCESS;
360 }
361
362 EFI_STATUS
363 BootMenuRemoveBootOption (
364 IN LIST_ENTRY *BootOptionsList
365 )
366 {
367 EFI_STATUS Status;
368 BDS_LOAD_OPTION_ENTRY* BootOptionEntry;
369
370 Status = BootMenuSelectBootOption (BootOptionsList, DELETE_BOOT_ENTRY, &BootOptionEntry);
371 if (EFI_ERROR(Status)) {
372 return Status;
373 }
374
375 // If the Boot Option was attached to a list remove it
376 if (!IsListEmpty (&BootOptionEntry->Link)) {
377 // Remove the entry from the list
378 RemoveEntryList (&BootOptionEntry->Link);
379 }
380
381 // Delete the BDS Load option structures
382 BootOptionDelete (BootOptionEntry->BdsLoadOption);
383
384 return EFI_SUCCESS;
385 }
386
387 EFI_STATUS
388 BootMenuUpdateBootOption (
389 IN LIST_ENTRY *BootOptionsList
390 )
391 {
392 EFI_STATUS Status;
393 BDS_LOAD_OPTION_ENTRY *BootOptionEntry;
394 BDS_LOAD_OPTION *BootOption;
395 BDS_LOAD_OPTION_SUPPORT* DeviceSupport;
396 ARM_BDS_LOADER_ARGUMENTS* BootArguments;
397 CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX];
398 CHAR8 CmdLine[BOOT_DEVICE_OPTION_MAX];
399 CHAR16 UnicodeCmdLine[BOOT_DEVICE_OPTION_MAX];
400 EFI_DEVICE_PATH *DevicePath;
401 EFI_DEVICE_PATH *TempInitrdPath;
402 ARM_BDS_LOADER_TYPE BootType;
403 ARM_BDS_LOADER_OPTIONAL_DATA* LoaderOptionalData;
404 ARM_BDS_LINUX_ARGUMENTS* LinuxArguments;
405 EFI_DEVICE_PATH *InitrdPathNodes;
406 EFI_DEVICE_PATH *InitrdPath;
407 UINTN InitrdSize;
408 UINTN CmdLineSize;
409 BOOLEAN InitrdSupport;
410 UINT8* OptionalData;
411 UINTN OptionalDataSize;
412 BOOLEAN RequestBootType;
413 BOOLEAN IsPrintable;
414 BOOLEAN IsUnicode;
415
416 Status = BootMenuSelectBootOption (BootOptionsList, UPDATE_BOOT_ENTRY, &BootOptionEntry);
417 if (EFI_ERROR(Status)) {
418 return Status;
419 }
420 BootOption = BootOptionEntry->BdsLoadOption;
421
422 // Get the device support for this Boot Option
423 Status = BootDeviceGetDeviceSupport (BootOption->FilePathList, &DeviceSupport);
424 if (EFI_ERROR(Status)) {
425 Print(L"Not possible to retrieve the supported device for the update\n");
426 return EFI_UNSUPPORTED;
427 }
428
429 RequestBootType = TRUE;
430 Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, L"EFI Application or the kernel", &DevicePath, &RequestBootType);
431 if (EFI_ERROR(Status)) {
432 Status = EFI_ABORTED;
433 goto EXIT;
434 }
435
436 if (RequestBootType) {
437 Status = BootDeviceGetType (DevicePath, &BootType, &BootOption->Attributes);
438 if (EFI_ERROR(Status)) {
439 Status = EFI_ABORTED;
440 goto EXIT;
441 }
442 }
443
444 LoaderOptionalData = BootOption->OptionalData;
445 if (LoaderOptionalData != NULL) {
446 BootType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((UINT32 *)(&LoaderOptionalData->Header.LoaderType));
447 } else {
448 BootType = BDS_LOADER_EFI_APPLICATION;
449 }
450
451 if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) {
452 LinuxArguments = &LoaderOptionalData->Arguments.LinuxArguments;
453
454 CmdLineSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->CmdLineSize);
455
456 InitrdSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->InitrdSize);
457 if (InitrdSize > 0) {
458 Print(L"Keep the initrd: ");
459 } else {
460 Print(L"Add an initrd: ");
461 }
462 Status = GetHIInputBoolean (&InitrdSupport);
463 if (EFI_ERROR(Status)) {
464 Status = EFI_ABORTED;
465 goto EXIT;
466 }
467
468 if (InitrdSupport) {
469 if (InitrdSize > 0) {
470 // Case we update the initrd device path
471 Status = DeviceSupport->UpdateDevicePathNode ((EFI_DEVICE_PATH*)((UINTN)(LinuxArguments + 1) + CmdLineSize), L"initrd", &InitrdPath, NULL);
472 if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) {// EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd
473 Status = EFI_ABORTED;
474 goto EXIT;
475 }
476 InitrdSize = GetDevicePathSize (InitrdPath);
477 } else {
478 // Case we create the initrd device path
479
480 Status = DeviceSupport->CreateDevicePathNode (L"initrd", &InitrdPathNodes, NULL);
481 if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) { // EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd
482 Status = EFI_ABORTED;
483 goto EXIT;
484 }
485
486 if (InitrdPathNodes != NULL) {
487 // Duplicate Linux kernel Device Path
488 TempInitrdPath = DuplicateDevicePath (BootOption->FilePathList);
489 // Replace Linux kernel Node by EndNode
490 SetDevicePathEndNode (GetLastDevicePathNode (TempInitrdPath));
491 // Append the Device Path to the selected device path
492 InitrdPath = AppendDevicePath (TempInitrdPath, (CONST EFI_DEVICE_PATH_PROTOCOL *)InitrdPathNodes);
493 FreePool (TempInitrdPath);
494 if (InitrdPath == NULL) {
495 Status = EFI_OUT_OF_RESOURCES;
496 goto EXIT;
497 }
498 InitrdSize = GetDevicePathSize (InitrdPath);
499 } else {
500 InitrdPath = NULL;
501 }
502 }
503 } else {
504 InitrdSize = 0;
505 }
506
507 Print(L"Arguments to pass to the binary: ");
508 if (CmdLineSize > 0) {
509 AsciiStrnCpy(CmdLine, (CONST CHAR8*)(LinuxArguments + 1), CmdLineSize);
510 } else {
511 CmdLine[0] = '\0';
512 }
513 Status = EditHIInputAscii (CmdLine, BOOT_DEVICE_OPTION_MAX);
514 if (EFI_ERROR(Status)) {
515 Status = EFI_ABORTED;
516 goto FREE_DEVICE_PATH;
517 }
518
519 CmdLineSize = AsciiStrSize (CmdLine);
520
521 OptionalDataSize = sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize;
522 BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool (OptionalDataSize);
523 BootArguments->LinuxArguments.CmdLineSize = CmdLineSize;
524 BootArguments->LinuxArguments.InitrdSize = InitrdSize;
525 CopyMem (&BootArguments->LinuxArguments + 1, CmdLine, CmdLineSize);
526 CopyMem ((VOID*)((UINTN)(&BootArguments->LinuxArguments + 1) + CmdLineSize), InitrdPath, InitrdSize);
527
528 OptionalData = (UINT8*)BootArguments;
529 } else {
530 Print (L"Arguments to pass to the EFI Application: ");
531
532 if (BootOption->OptionalDataSize > 0) {
533 IsPrintable = IsPrintableString (BootOption->OptionalData, &IsUnicode);
534 if (IsPrintable) {
535 if (IsUnicode) {
536 StrnCpy (UnicodeCmdLine, BootOption->OptionalData, BootOption->OptionalDataSize / 2);
537 } else {
538 AsciiStrnCpy (CmdLine, BootOption->OptionalData, BootOption->OptionalDataSize);
539 }
540 }
541 } else {
542 UnicodeCmdLine[0] = L'\0';
543 IsPrintable = TRUE;
544 IsUnicode = TRUE;
545 }
546
547 // We do not request arguments for OptionalData that cannot be printed
548 if (IsPrintable) {
549 if (IsUnicode) {
550 Status = EditHIInputStr (UnicodeCmdLine, BOOT_DEVICE_OPTION_MAX);
551 if (EFI_ERROR (Status)) {
552 Status = EFI_ABORTED;
553 goto FREE_DEVICE_PATH;
554 }
555
556 OptionalData = (UINT8*)UnicodeCmdLine;
557 OptionalDataSize = StrSize (UnicodeCmdLine);
558 } else {
559 Status = EditHIInputAscii (CmdLine, BOOT_DEVICE_OPTION_MAX);
560 if (EFI_ERROR (Status)) {
561 Status = EFI_ABORTED;
562 goto FREE_DEVICE_PATH;
563 }
564
565 OptionalData = (UINT8*)CmdLine;
566 OptionalDataSize = AsciiStrSize (CmdLine);
567 }
568 } else {
569 // We keep the former OptionalData
570 OptionalData = BootOption->OptionalData;
571 OptionalDataSize = BootOption->OptionalDataSize;
572 }
573 }
574
575 Print(L"Description for this new Entry: ");
576 StrnCpy (BootDescription, BootOption->Description, BOOT_DEVICE_DESCRIPTION_MAX);
577 Status = EditHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX);
578 if (EFI_ERROR(Status)) {
579 Status = EFI_ABORTED;
580 goto FREE_DEVICE_PATH;
581 }
582
583 // Update the entry
584 Status = BootOptionUpdate (BootOption, BootOption->Attributes, BootDescription, DevicePath, BootType, OptionalData, OptionalDataSize);
585
586 FREE_DEVICE_PATH:
587 FreePool (DevicePath);
588
589 EXIT:
590 if (Status == EFI_ABORTED) {
591 Print(L"\n");
592 }
593 return Status;
594 }
595
596 EFI_STATUS
597 UpdateFdtPath (
598 IN LIST_ENTRY *BootOptionsList
599 )
600 {
601 EFI_STATUS Status;
602 UINTN FdtDevicePathSize;
603 BDS_SUPPORTED_DEVICE *SupportedBootDevice;
604 EFI_DEVICE_PATH_PROTOCOL *FdtDevicePathNodes;
605 EFI_DEVICE_PATH_PROTOCOL *FdtDevicePath;
606
607 Status = SelectBootDevice (&SupportedBootDevice);
608 if (EFI_ERROR(Status)) {
609 Status = EFI_ABORTED;
610 goto EXIT;
611 }
612
613 // Create the specific device path node
614 Status = SupportedBootDevice->Support->CreateDevicePathNode (L"FDT blob", &FdtDevicePathNodes, NULL);
615 if (EFI_ERROR(Status)) {
616 Status = EFI_ABORTED;
617 goto EXIT;
618 }
619
620 if (FdtDevicePathNodes != NULL) {
621 // Append the Device Path node to the select device path
622 FdtDevicePath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, FdtDevicePathNodes);
623 FdtDevicePathSize = GetDevicePathSize (FdtDevicePath);
624 Status = gRT->SetVariable (
625 (CHAR16*)L"Fdt",
626 &gArmGlobalVariableGuid,
627 EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
628 FdtDevicePathSize,
629 FdtDevicePath
630 );
631 ASSERT_EFI_ERROR(Status);
632 } else {
633 gRT->SetVariable (
634 (CHAR16*)L"Fdt",
635 &gArmGlobalVariableGuid,
636 EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
637 0,
638 NULL
639 );
640 ASSERT_EFI_ERROR(Status);
641 }
642
643 EXIT:
644 if (Status == EFI_ABORTED) {
645 Print(L"\n");
646 }
647 FreePool(SupportedBootDevice);
648 return Status;
649 }
650
651 struct BOOT_MANAGER_ENTRY {
652 CONST CHAR16* Description;
653 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);
654 } BootManagerEntries[] = {
655 { L"Add Boot Device Entry", BootMenuAddBootOption },
656 { L"Update Boot Device Entry", BootMenuUpdateBootOption },
657 { L"Remove Boot Device Entry", BootMenuRemoveBootOption },
658 { L"Update FDT path", UpdateFdtPath },
659 };
660
661 EFI_STATUS
662 BootMenuManager (
663 IN LIST_ENTRY *BootOptionsList
664 )
665 {
666 UINTN Index;
667 UINTN OptionSelected;
668 UINTN BootManagerEntryCount;
669 EFI_STATUS Status;
670
671 BootManagerEntryCount = sizeof(BootManagerEntries) / sizeof(struct BOOT_MANAGER_ENTRY);
672
673 while (TRUE) {
674 // Display Boot Manager menu
675 for (Index = 0; Index < BootManagerEntryCount; Index++) {
676 Print(L"[%d] %s\n",Index+1,BootManagerEntries[Index]);
677 }
678 Print(L"[%d] Return to main menu\n",Index+1);
679
680 // Select which entry to call
681 Print(L"Choice: ");
682 Status = GetHIInputInteger (&OptionSelected);
683 if (EFI_ERROR(Status) || (OptionSelected == (BootManagerEntryCount+1))) {
684 if (EFI_ERROR(Status)) {
685 Print(L"\n");
686 }
687 return EFI_SUCCESS;
688 } else if ((OptionSelected > 0) && (OptionSelected <= BootManagerEntryCount)) {
689 BootManagerEntries[OptionSelected-1].Callback (BootOptionsList);
690 }
691 }
692 // Should never go here
693 }
694
695 EFI_STATUS
696 BootShell (
697 IN LIST_ENTRY *BootOptionsList
698 )
699 {
700 EFI_STATUS Status;
701
702 // Start EFI Shell
703 Status = BdsLoadApplication (mImageHandle, L"Shell", 0, NULL);
704 if (Status == EFI_NOT_FOUND) {
705 Print (L"Error: EFI Application not found.\n");
706 } else if (EFI_ERROR(Status)) {
707 Print (L"Error: Status Code: 0x%X\n",(UINT32)Status);
708 }
709
710 return Status;
711 }
712
713 struct BOOT_MAIN_ENTRY {
714 CONST CHAR16* Description;
715 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);
716 } BootMainEntries[] = {
717 { L"Shell", BootShell },
718 { L"Boot Manager", BootMenuManager },
719 };
720
721
722 EFI_STATUS
723 BootMenuMain (
724 VOID
725 )
726 {
727 LIST_ENTRY BootOptionsList;
728 UINTN OptionCount;
729 UINTN BootOptionCount;
730 EFI_STATUS Status;
731 LIST_ENTRY* Entry;
732 BDS_LOAD_OPTION* BootOption;
733 UINTN BootOptionSelected;
734 UINTN Index;
735 UINTN BootMainEntryCount;
736 BOOLEAN IsUnicode;
737
738 BootOption = NULL;
739 BootMainEntryCount = sizeof(BootMainEntries) / sizeof(struct BOOT_MAIN_ENTRY);
740
741 while (TRUE) {
742 // Get Boot#### list
743 BootOptionList (&BootOptionsList);
744
745 OptionCount = 1;
746
747 // Display the Boot options
748 for (Entry = GetFirstNode (&BootOptionsList);
749 !IsNull (&BootOptionsList,Entry);
750 Entry = GetNextNode (&BootOptionsList,Entry)
751 )
752 {
753 BootOption = LOAD_OPTION_FROM_LINK(Entry);
754
755 Print(L"[%d] %s\n", OptionCount, BootOption->Description);
756
757 DEBUG_CODE_BEGIN();
758 CHAR16* DevicePathTxt;
759 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
760 ARM_BDS_LOADER_OPTIONAL_DATA* OptionalData;
761 UINTN CmdLineSize;
762 ARM_BDS_LOADER_TYPE LoaderType;
763
764 Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
765 if (EFI_ERROR(Status)) {
766 // You must provide an implementation of DevicePathToTextProtocol in your firmware (eg: DevicePathDxe)
767 DEBUG((EFI_D_ERROR,"Error: Bds requires DevicePathToTextProtocol\n"));
768 return Status;
769 }
770 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (BootOption->FilePathList, TRUE, TRUE);
771
772 Print(L"\t- %s\n",DevicePathTxt);
773
774 // If it is a supported BootEntry then print its details
775 if (IS_ARM_BDS_BOOTENTRY (BootOption)) {
776 OptionalData = BootOption->OptionalData;
777 LoaderType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((CONST UINT32*)&OptionalData->Header.LoaderType);
778 if ((LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) || (LoaderType == BDS_LOADER_KERNEL_LINUX_FDT)) {
779 if (ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.InitrdSize) > 0) {
780 CmdLineSize = ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.CmdLineSize);
781 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (
782 GetAlignedDevicePath ((EFI_DEVICE_PATH*)((UINTN)(&OptionalData->Arguments.LinuxArguments + 1) + CmdLineSize)), TRUE, TRUE);
783 Print(L"\t- Initrd: %s\n", DevicePathTxt);
784 }
785 if (ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.CmdLineSize) > 0) {
786 Print(L"\t- Arguments: %a\n", (&OptionalData->Arguments.LinuxArguments + 1));
787 }
788 }
789
790 switch (LoaderType) {
791 case BDS_LOADER_EFI_APPLICATION:
792 Print(L"\t- LoaderType: EFI Application\n");
793 break;
794
795 case BDS_LOADER_KERNEL_LINUX_ATAG:
796 Print(L"\t- LoaderType: Linux kernel with ATAG support\n");
797 break;
798
799 case BDS_LOADER_KERNEL_LINUX_FDT:
800 Print(L"\t- LoaderType: Linux kernel with FDT support\n");
801 break;
802
803 default:
804 Print(L"\t- LoaderType: Not recognized (%d)\n", LoaderType);
805 }
806 } else if (BootOption->OptionalData != NULL) {
807 if (IsPrintableString (BootOption->OptionalData, &IsUnicode)) {
808 if (IsUnicode) {
809 Print (L"\t- Arguments: %s\n", BootOption->OptionalData);
810 } else {
811 AsciiPrint ("\t- Arguments: %a\n", BootOption->OptionalData);
812 }
813 }
814 }
815 FreePool(DevicePathTxt);
816 DEBUG_CODE_END();
817
818 OptionCount++;
819 }
820 BootOptionCount = OptionCount-1;
821
822 // Display the hardcoded Boot entries
823 for (Index = 0; Index < BootMainEntryCount; Index++) {
824 Print(L"[%d] %s\n",OptionCount,BootMainEntries[Index]);
825 OptionCount++;
826 }
827
828 // Request the boot entry from the user
829 BootOptionSelected = 0;
830 while (BootOptionSelected == 0) {
831 Print(L"Start: ");
832 Status = GetHIInputInteger (&BootOptionSelected);
833 if (EFI_ERROR(Status) || (BootOptionSelected == 0) || (BootOptionSelected > OptionCount)) {
834 Print(L"Invalid input (max %d)\n",(OptionCount-1));
835 BootOptionSelected = 0;
836 }
837 }
838
839 // Start the selected entry
840 if (BootOptionSelected > BootOptionCount) {
841 // Start the hardcoded entry
842 Status = BootMainEntries[BootOptionSelected - BootOptionCount - 1].Callback (&BootOptionsList);
843 } else {
844 // Find the selected entry from the Boot#### list
845 Index = 1;
846 for (Entry = GetFirstNode (&BootOptionsList);
847 !IsNull (&BootOptionsList,Entry);
848 Entry = GetNextNode (&BootOptionsList,Entry)
849 )
850 {
851 if (Index == BootOptionSelected) {
852 BootOption = LOAD_OPTION_FROM_LINK(Entry);
853 break;
854 }
855 Index++;
856 }
857
858 Status = BootOptionStart (BootOption);
859 }
860 }
861 // Should never go here
862 }