]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Bds/BootMenu.c
ArmPlatformPkg/Bds: Get User inputs in Unicode
[mirror_edk2.git] / ArmPlatformPkg / Bds / BootMenu.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 extern BDS_LOAD_OPTION_SUPPORT *BdsLoadOptionSupportList;
19
20 EFI_STATUS
21 SelectBootDevice (
22 OUT BDS_SUPPORTED_DEVICE** SupportedBootDevice
23 )
24 {
25 EFI_STATUS Status;
26 LIST_ENTRY SupportedDeviceList;
27 UINTN SupportedDeviceCount;
28 LIST_ENTRY* Entry;
29 UINTN SupportedDeviceSelected;
30 UINTN Index;
31
32 //
33 // List the Boot Devices supported
34 //
35
36 // Start all the drivers first
37 BdsConnectAllDrivers ();
38
39 // List the supported devices
40 Status = BootDeviceListSupportedInit (&SupportedDeviceList);
41 ASSERT_EFI_ERROR(Status);
42
43 SupportedDeviceCount = 0;
44 for (Entry = GetFirstNode (&SupportedDeviceList);
45 !IsNull (&SupportedDeviceList,Entry);
46 Entry = GetNextNode (&SupportedDeviceList,Entry)
47 )
48 {
49 *SupportedBootDevice = SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry);
50 Print(L"[%d] %s\n",SupportedDeviceCount+1,(*SupportedBootDevice)->Description);
51
52 DEBUG_CODE_BEGIN();
53 CHAR16* DevicePathTxt;
54 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
55
56 Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
57 ASSERT_EFI_ERROR(Status);
58 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText((*SupportedBootDevice)->DevicePathProtocol,TRUE,TRUE);
59
60 Print(L"\t- %s\n",DevicePathTxt);
61
62 FreePool(DevicePathTxt);
63 DEBUG_CODE_END();
64
65 SupportedDeviceCount++;
66 }
67
68 if (SupportedDeviceCount == 0) {
69 Print(L"There is no supported device.\n");
70 Status = EFI_ABORTED;
71 goto EXIT;
72 }
73
74 //
75 // Select the Boot Device
76 //
77 SupportedDeviceSelected = 0;
78 while (SupportedDeviceSelected == 0) {
79 Print(L"Select the Boot Device: ");
80 Status = GetHIInputInteger (&SupportedDeviceSelected);
81 if (EFI_ERROR(Status)) {
82 Status = EFI_ABORTED;
83 goto EXIT;
84 } else if ((SupportedDeviceSelected == 0) || (SupportedDeviceSelected > SupportedDeviceCount)) {
85 Print(L"Invalid input (max %d)\n",SupportedDeviceSelected);
86 SupportedDeviceSelected = 0;
87 }
88 }
89
90 //
91 // Get the Device Path for the selected boot device
92 //
93 Index = 1;
94 for (Entry = GetFirstNode (&SupportedDeviceList);
95 !IsNull (&SupportedDeviceList,Entry);
96 Entry = GetNextNode (&SupportedDeviceList,Entry)
97 )
98 {
99 if (Index == SupportedDeviceSelected) {
100 *SupportedBootDevice = SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry);
101 break;
102 }
103 Index++;
104 }
105
106 EXIT:
107 BootDeviceListSupportedFree (&SupportedDeviceList, *SupportedBootDevice);
108 return Status;
109 }
110
111 EFI_STATUS
112 BootMenuAddBootOption (
113 IN LIST_ENTRY *BootOptionsList
114 )
115 {
116 EFI_STATUS Status;
117 BDS_SUPPORTED_DEVICE* SupportedBootDevice;
118 BDS_LOADER_ARGUMENTS BootArguments;
119 CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX];
120 UINT32 Attributes;
121 BDS_LOADER_TYPE BootType;
122 BDS_LOAD_OPTION *BdsLoadOption;
123 EFI_DEVICE_PATH *DevicePath;
124 EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;
125 EFI_DEVICE_PATH_PROTOCOL *InitrdPathNode;
126
127 Attributes = 0;
128 SupportedBootDevice = NULL;
129
130 // List the Boot Devices supported
131 Status = SelectBootDevice(&SupportedBootDevice);
132 if (EFI_ERROR(Status)) {
133 Status = EFI_ABORTED;
134 goto EXIT;
135 }
136
137 // Create the specific device path node
138 Print(L"File path of the EFI Application or the kernel: ");
139 Status = SupportedBootDevice->Support->CreateDevicePathNode (SupportedBootDevice, &DevicePathNode, &BootType, &Attributes);
140 if (EFI_ERROR(Status)) {
141 Status = EFI_ABORTED;
142 goto EXIT;
143 }
144 // Append the Device Path node to the select device path
145 DevicePath = AppendDevicePathNode (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)DevicePathNode);
146
147 if (BootType == BDS_LOADER_KERNEL_LINUX_ATAG) {
148 // Create the specific device path node
149 Print(L"File path of the initrd: ");
150 Status = SupportedBootDevice->Support->CreateDevicePathNode (SupportedBootDevice, &InitrdPathNode, NULL, NULL);
151 if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) { // EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd
152 Status = EFI_ABORTED;
153 goto EXIT;
154 }
155
156 if (InitrdPathNode != NULL) {
157 // Append the Device Path node to the select device path
158 BootArguments.LinuxAtagArguments.InitrdPathList = AppendDevicePathNode (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)InitrdPathNode);
159 } else {
160 BootArguments.LinuxAtagArguments.InitrdPathList = NULL;
161 }
162
163 Print(L"Arguments to pass to the binary: ");
164 Status = GetHIInputAscii (BootArguments.LinuxAtagArguments.CmdLine,BOOT_DEVICE_OPTION_MAX);
165 if (EFI_ERROR(Status)) {
166 Status = EFI_ABORTED;
167 goto FREE_DEVICE_PATH;
168 }
169 BootArguments.LinuxAtagArguments.CmdLine[BOOT_DEVICE_OPTION_MAX]= '\0';
170 }
171
172 Print(L"Description for this new Entry: ");
173 Status = GetHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX);
174 if (EFI_ERROR(Status)) {
175 Status = EFI_ABORTED;
176 goto FREE_DEVICE_PATH;
177 }
178
179 // Create new entry
180 Status = BootOptionCreate (Attributes, BootDescription, DevicePath, BootType, &BootArguments, &BdsLoadOption);
181 if (!EFI_ERROR(Status)) {
182 InsertTailList (BootOptionsList,&BdsLoadOption->Link);
183 }
184
185 FREE_DEVICE_PATH:
186 FreePool (DevicePath);
187
188
189 EXIT:
190 if (Status == EFI_ABORTED) {
191 Print(L"\n");
192 }
193 FreePool(SupportedBootDevice);
194 return Status;
195 }
196
197 STATIC
198 EFI_STATUS
199 BootMenuSelectBootOption (
200 IN LIST_ENTRY *BootOptionsList,
201 IN CONST CHAR16* InputStatement,
202 OUT BDS_LOAD_OPTION **BdsLoadOption
203 )
204 {
205 EFI_STATUS Status;
206 LIST_ENTRY* Entry;
207 BDS_LOAD_OPTION *BootOption;
208 UINTN BootOptionSelected;
209 UINTN BootOptionCount;
210 UINTN Index;
211
212 // Display the list of supported boot devices
213 BootOptionCount = 1;
214 for (Entry = GetFirstNode (BootOptionsList);
215 !IsNull (BootOptionsList,Entry);
216 Entry = GetNextNode (BootOptionsList,Entry)
217 )
218 {
219 BootOption = LOAD_OPTION_FROM_LINK(Entry);
220 Print(L"[%d] %s\n",BootOptionCount,BootOption->Description);
221
222 DEBUG_CODE_BEGIN();
223 CHAR16* DevicePathTxt;
224 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
225
226 Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
227 ASSERT_EFI_ERROR(Status);
228 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText(BootOption->FilePathList,TRUE,TRUE);
229
230 Print(L"\t- %s\n",DevicePathTxt);
231 if ((BDS_LOADER_TYPE)ReadUnaligned32(&BootOption->OptionalData->LoaderType) == BDS_LOADER_KERNEL_LINUX_ATAG) {
232 Print(L"\t- Arguments: %a\n",BootOption->OptionalData->Arguments.LinuxAtagArguments.CmdLine);
233 }
234
235 FreePool(DevicePathTxt);
236 DEBUG_CODE_END();
237
238 BootOptionCount++;
239 }
240
241 // Get the index of the boot device to delete
242 BootOptionSelected = 0;
243 while (BootOptionSelected == 0) {
244 Print(InputStatement);
245 Status = GetHIInputInteger (&BootOptionSelected);
246 if (EFI_ERROR(Status)) {
247 return Status;
248 } else if ((BootOptionSelected == 0) || (BootOptionSelected >= BootOptionCount)) {
249 Print(L"Invalid input (max %d)\n",BootOptionCount-1);
250 BootOptionSelected = 0;
251 }
252 }
253
254 // Get the structure of the Boot device to delete
255 Index = 1;
256 for (Entry = GetFirstNode (BootOptionsList);
257 !IsNull (BootOptionsList,Entry);
258 Entry = GetNextNode (BootOptionsList,Entry)
259 )
260 {
261 if (Index == BootOptionSelected) {
262 *BdsLoadOption = LOAD_OPTION_FROM_LINK(Entry);
263 break;
264 }
265 Index++;
266 }
267
268 return EFI_SUCCESS;
269 }
270
271 EFI_STATUS
272 BootMenuRemoveBootOption (
273 IN LIST_ENTRY *BootOptionsList
274 )
275 {
276 EFI_STATUS Status;
277 BDS_LOAD_OPTION *BootOption;
278
279 Status = BootMenuSelectBootOption (BootOptionsList,L"Delete entry: ",&BootOption);
280 if (EFI_ERROR(Status)) {
281 return Status;
282 }
283
284 // Delete the BDS Load option structures
285 BootOptionDelete (BootOption);
286
287 return EFI_SUCCESS;
288 }
289
290 EFI_STATUS
291 BootMenuUpdateBootOption (
292 IN LIST_ENTRY *BootOptionsList
293 )
294 {
295 EFI_STATUS Status;
296 BDS_LOAD_OPTION *BootOption;
297 BDS_LOAD_OPTION_SUPPORT *DeviceSupport;
298 BDS_LOADER_ARGUMENTS BootArguments;
299 CHAR16 BootDescription[BOOT_DEVICE_DESCRIPTION_MAX];
300 EFI_DEVICE_PATH* DevicePath;
301 BDS_LOADER_TYPE BootType;
302
303 Status = BootMenuSelectBootOption (BootOptionsList,L"Update entry: ",&BootOption);
304 if (EFI_ERROR(Status)) {
305 return Status;
306 }
307
308 // Get the device support for this Boot Option
309 Status = BootDeviceGetDeviceSupport (BootOption,&DeviceSupport);
310 if (EFI_ERROR(Status)) {
311 Print(L"Impossible to retrieve the supported device for the update\n");
312 return EFI_UNSUPPORTED;
313 }
314
315 Print(L"File path of the EFI Application or the kernel: ");
316 Status = DeviceSupport->UpdateDevicePathNode (BootOption->FilePathList, &DevicePath, NULL, NULL);
317 if (EFI_ERROR(Status)) {
318 Status = EFI_ABORTED;
319 goto EXIT;
320 }
321
322 BootType = (BDS_LOADER_TYPE)ReadUnaligned32((UINT32 *)(&BootOption->OptionalData->LoaderType));
323
324 // TODO: Allow adding an initrd to a boot entry without one
325 if (BootType == BDS_LOADER_KERNEL_LINUX_ATAG) {
326 if (ReadUnaligned16(&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathListLength) > 0
327 && (EFI_DEVICE_PATH_PROTOCOL *)ReadUnaligned32((UINT32 *)(&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathList)) != NULL) {
328
329 Print(L"File path of the initrd: ");
330 Status = DeviceSupport->UpdateDevicePathNode (
331 (EFI_DEVICE_PATH_PROTOCOL *)ReadUnaligned32((UINT32 *)(&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathList)),
332 &BootArguments.LinuxAtagArguments.InitrdPathList,
333 NULL,
334 NULL);
335 if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) {// EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd
336 Status = EFI_ABORTED;
337 goto EXIT;
338 }
339 } else {
340 BootArguments.LinuxAtagArguments.InitrdPathList = NULL;
341 BootArguments.LinuxAtagArguments.InitrdPathListLength = 0;
342 }
343
344 Print(L"Arguments to pass to the binary: ");
345 if (ReadUnaligned32((CONST UINT32*)&BootOption->OptionalData->Arguments.LinuxAtagArguments.CmdLine)) {
346 AsciiStrnCpy(BootArguments.LinuxAtagArguments.CmdLine,
347 BootOption->OptionalData->Arguments.LinuxAtagArguments.CmdLine,
348 BOOT_DEVICE_OPTION_MAX+1);
349 } else {
350 BootArguments.LinuxAtagArguments.CmdLine[0] = '\0';
351 }
352 Status = EditHIInputAscii (BootArguments.LinuxAtagArguments.CmdLine, BOOT_DEVICE_OPTION_MAX);
353 if (EFI_ERROR(Status)) {
354 Status = EFI_ABORTED;
355 goto FREE_DEVICE_PATH;
356 }
357 }
358
359 Print(L"Description for this new Entry: ");
360 Status = EditHIInputStr (BootDescription, BOOT_DEVICE_DESCRIPTION_MAX);
361 if (EFI_ERROR(Status)) {
362 Status = EFI_ABORTED;
363 goto FREE_DEVICE_PATH;
364 }
365
366 // Update the entry
367 Status = BootOptionUpdate (BootOption, BootOption->Attributes, BootDescription, DevicePath, BootType, &BootArguments);
368
369 FREE_DEVICE_PATH:
370 FreePool (DevicePath);
371
372 EXIT:
373 if (Status == EFI_ABORTED) {
374 Print(L"\n");
375 }
376 return Status;
377 }
378
379 struct BOOT_MANAGER_ENTRY {
380 CONST CHAR16* Description;
381 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);
382 } BootManagerEntries[] = {
383 { L"Add Boot Device Entry", BootMenuAddBootOption },
384 { L"Update Boot Device Entry", BootMenuUpdateBootOption },
385 { L"Remove Boot Device Entry", BootMenuRemoveBootOption },
386 };
387
388 EFI_STATUS
389 BootMenuManager (
390 IN LIST_ENTRY *BootOptionsList
391 )
392 {
393 UINTN Index;
394 UINTN OptionSelected;
395 UINTN BootManagerEntryCount;
396 EFI_STATUS Status;
397
398 BootManagerEntryCount = sizeof(BootManagerEntries) / sizeof(struct BOOT_MANAGER_ENTRY);
399
400 while (TRUE) {
401 // Display Boot Manager menu
402 for (Index = 0; Index < BootManagerEntryCount; Index++) {
403 Print(L"[%d] %s\n",Index+1,BootManagerEntries[Index]);
404 }
405 Print(L"[%d] Return to main menu\n",Index+1);
406
407 // Select which entry to call
408 Print(L"Choice: ");
409 Status = GetHIInputInteger (&OptionSelected);
410 if (EFI_ERROR(Status) || (OptionSelected == (BootManagerEntryCount+1))) {
411 if (EFI_ERROR(Status)) {
412 Print(L"\n");
413 }
414 return EFI_SUCCESS;
415 } else if ((OptionSelected > 0) && (OptionSelected <= BootManagerEntryCount)) {
416 Status = BootManagerEntries[OptionSelected-1].Callback (BootOptionsList);
417 }
418 }
419
420 return EFI_SUCCESS;
421 }
422
423 EFI_STATUS
424 BootEBL (
425 IN LIST_ENTRY *BootOptionsList
426 )
427 {
428 EFI_STATUS Status;
429
430 // Start EFI Shell
431 Status = BdsLoadApplication(mImageHandle, L"Ebl");
432 if (Status == EFI_NOT_FOUND) {
433 Print (L"Error: EFI Application not found.\n");
434 } else if (EFI_ERROR(Status)) {
435 Print (L"Error: Status Code: 0x%X\n",(UINT32)Status);
436 }
437
438 return Status;
439 }
440
441 struct BOOT_MAIN_ENTRY {
442 CONST CHAR16* Description;
443 EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList);
444 } BootMainEntries[] = {
445 { L"EBL", BootEBL },
446 { L"Boot Manager", BootMenuManager },
447 };
448
449
450 EFI_STATUS
451 BootMenuMain (
452 VOID
453 )
454 {
455 LIST_ENTRY BootOptionsList;
456 UINTN OptionCount;
457 UINTN BootOptionCount;
458 EFI_STATUS Status;
459 LIST_ENTRY *Entry;
460 BDS_LOAD_OPTION *BootOption;
461 UINTN BootOptionSelected;
462 UINTN Index;
463 UINTN BootMainEntryCount;
464
465 BootOption = NULL;
466 BootMainEntryCount = sizeof(BootMainEntries) / sizeof(struct BOOT_MAIN_ENTRY);
467
468 // Get Boot#### list
469 BootOptionList (&BootOptionsList);
470
471 while (TRUE) {
472 OptionCount = 1;
473
474 // Display the Boot options
475 for (Entry = GetFirstNode (&BootOptionsList);
476 !IsNull (&BootOptionsList,Entry);
477 Entry = GetNextNode (&BootOptionsList,Entry)
478 )
479 {
480 BootOption = LOAD_OPTION_FROM_LINK(Entry);
481
482 Print(L"[%d] %s\n",OptionCount,BootOption->Description);
483
484 DEBUG_CODE_BEGIN();
485 CHAR16* DevicePathTxt;
486 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
487
488 Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
489 if (EFI_ERROR(Status)) {
490 // You must provide an implementation of DevicePathToTextProtocol in your firmware (eg: DevicePathDxe)
491 DEBUG((EFI_D_ERROR,"Error: Bds requires DevicePathToTextProtocol\n"));
492 return Status;
493 }
494 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText(BootOption->FilePathList,TRUE,TRUE);
495
496 Print(L"\t- %s\n",DevicePathTxt);
497 if (ReadUnaligned32(&BootOption->OptionalData->LoaderType) == BDS_LOADER_KERNEL_LINUX_ATAG) {
498 if (ReadUnaligned16(&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathListLength) > 0
499 && (EFI_DEVICE_PATH_PROTOCOL *)ReadUnaligned32((UINT32 *)(&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathList)) != NULL) {
500 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText((EFI_DEVICE_PATH_PROTOCOL *)ReadUnaligned32((UINT32 *)(&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathList)),TRUE,TRUE);
501 Print(L"\t- Initrd: %s\n", DevicePathTxt);
502 }
503
504 Print(L"\t- Arguments: %a\n", BootOption->OptionalData->Arguments.LinuxAtagArguments.CmdLine);
505 }
506
507 Print(L"\t- LoaderType: %d\n", ReadUnaligned32 (&BootOption->OptionalData->LoaderType));
508
509 FreePool(DevicePathTxt);
510 DEBUG_CODE_END();
511
512 OptionCount++;
513 }
514 BootOptionCount = OptionCount-1;
515
516 // Display the hardcoded Boot entries
517 for (Index = 0; Index < BootMainEntryCount; Index++) {
518 Print(L"[%d] %s\n",OptionCount,BootMainEntries[Index]);
519 OptionCount++;
520 }
521
522 // Request the boot entry from the user
523 BootOptionSelected = 0;
524 while (BootOptionSelected == 0) {
525 Print(L"Start: ");
526 Status = GetHIInputInteger (&BootOptionSelected);
527 if (EFI_ERROR(Status) || (BootOptionSelected == 0) || (BootOptionSelected > OptionCount)) {
528 Print(L"Invalid input (max %d)\n",(OptionCount-1));
529 BootOptionSelected = 0;
530 }
531 }
532
533 // Start the selected entry
534 if (BootOptionSelected > BootOptionCount) {
535 // Start the hardcoded entry
536 Status = BootMainEntries[BootOptionSelected - BootOptionCount - 1].Callback (&BootOptionsList);
537 } else {
538 // Find the selected entry from the Boot#### list
539 Index = 1;
540 for (Entry = GetFirstNode (&BootOptionsList);
541 !IsNull (&BootOptionsList,Entry);
542 Entry = GetNextNode (&BootOptionsList,Entry)
543 )
544 {
545 if (Index == BootOptionSelected) {
546 BootOption = LOAD_OPTION_FROM_LINK(Entry);
547 break;
548 }
549 Index++;
550 }
551
552 Status = BootOptionStart (BootOption);
553 }
554 }
555
556 return Status;
557 }