1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * (C) Copyright 2011-2013 Pali Rohár <pali@kernel.org>
4 */
5
6 #include <charset.h>
7 #include <cli.h>
8 #include <command.h>
9 #include <ansi.h>
10 #include <efi_config.h>
11 #include <efi_variable.h>
12 #include <env.h>
13 #include <log.h>
14 #include <menu.h>
15 #include <watchdog.h>
16 #include <malloc.h>
17 #include <linux/delay.h>
18 #include <linux/string.h>
19
20 /* maximum bootmenu entries */
21 #define MAX_COUNT 99
22
23 /* maximal size of bootmenu env
24 * 9 = strlen("bootmenu_")
25 * 2 = strlen(MAX_COUNT)
26 * 1 = NULL term
27 */
28 #define MAX_ENV_SIZE (9 + 2 + 1)
29
30 enum bootmenu_ret {
31 BOOTMENU_RET_SUCCESS = 0,
32 BOOTMENU_RET_FAIL,
33 BOOTMENU_RET_QUIT,
34 BOOTMENU_RET_UPDATED,
35 };
36
37 enum boot_type {
38 BOOTMENU_TYPE_NONE = 0,
39 BOOTMENU_TYPE_BOOTMENU,
40 BOOTMENU_TYPE_UEFI_BOOT_OPTION,
41 };
42
43 struct bootmenu_entry {
44 unsigned short int num; /* unique number 0 .. MAX_COUNT */
45 char key[3]; /* key identifier of number */
46 char *title; /* title of entry */
47 char *command; /* hush command of entry */
48 enum boot_type type; /* boot type of entry */
49 u16 bootorder; /* order for each boot type */
50 struct bootmenu_data *menu; /* this bootmenu */
51 struct bootmenu_entry *next; /* next menu entry (num+1) */
52 };
53
bootmenu_getoption(unsigned short int n)54 static char *bootmenu_getoption(unsigned short int n)
55 {
56 char name[MAX_ENV_SIZE];
57
58 if (n > MAX_COUNT)
59 return NULL;
60
61 sprintf(name, "bootmenu_%d", n);
62 return env_get(name);
63 }
64
bootmenu_print_entry(void * data)65 static void bootmenu_print_entry(void *data)
66 {
67 struct bootmenu_entry *entry = data;
68 int reverse = (entry->menu->active == entry->num);
69
70 /*
71 * Move cursor to line where the entry will be drown (entry->num)
72 * First 3 lines contain bootmenu header + 1 empty line
73 */
74 printf(ANSI_CURSOR_POSITION, entry->num + 4, 7);
75
76 if (reverse)
77 puts(ANSI_COLOR_REVERSE);
78
79 printf("%s", entry->title);
80
81 if (reverse)
82 puts(ANSI_COLOR_RESET);
83 }
84
bootmenu_choice_entry(void * data)85 static char *bootmenu_choice_entry(void *data)
86 {
87 struct cli_ch_state s_cch, *cch = &s_cch;
88 struct bootmenu_data *menu = data;
89 struct bootmenu_entry *iter;
90 enum bootmenu_key key = BKEY_NONE;
91 int i;
92
93 cli_ch_init(cch);
94
95 while (1) {
96 if (menu->delay >= 0) {
97 /* Autoboot was not stopped */
98 key = bootmenu_autoboot_loop(menu, cch);
99 } else {
100 /* Some key was pressed, so autoboot was stopped */
101 key = bootmenu_loop(menu, cch);
102 }
103
104 switch (key) {
105 case BKEY_UP:
106 menu->last_active = menu->active;
107 if (menu->active > 0)
108 --menu->active;
109 /* no menu key selected, regenerate menu */
110 return NULL;
111 case BKEY_DOWN:
112 menu->last_active = menu->active;
113 if (menu->active < menu->count - 1)
114 ++menu->active;
115 /* no menu key selected, regenerate menu */
116 return NULL;
117 case BKEY_SHORTCUT:
118 /* invalid shortcut, regenerate menu */
119 if (cch->shortcut_key >= menu->count - 1)
120 return NULL;
121 /* shortcut_key value for Exit is is -1 */
122 menu->active = cch->shortcut_key < 0 ? menu->count - 1 :
123 cch->shortcut_key;
124 fallthrough;
125 case BKEY_SELECT:
126 iter = menu->first;
127 for (i = 0; i < menu->active; ++i)
128 iter = iter->next;
129 return iter->key;
130 case BKEY_QUIT:
131 /* Quit by choosing the last entry */
132 iter = menu->first;
133 while (iter->next)
134 iter = iter->next;
135 return iter->key;
136 default:
137 break;
138 }
139 }
140
141 /* never happens */
142 debug("bootmenu: this should not happen");
143 return NULL;
144 }
145
bootmenu_need_reprint(void * data)146 static bool bootmenu_need_reprint(void *data)
147 {
148 struct bootmenu_data *menu = data;
149 bool need_reprint;
150
151 need_reprint = menu->last_active != menu->active;
152 menu->last_active = menu->active;
153
154 return need_reprint;
155 }
156
bootmenu_destroy(struct bootmenu_data * menu)157 static void bootmenu_destroy(struct bootmenu_data *menu)
158 {
159 struct bootmenu_entry *iter = menu->first;
160 struct bootmenu_entry *next;
161
162 while (iter) {
163 next = iter->next;
164 free(iter->title);
165 free(iter->command);
166 free(iter);
167 iter = next;
168 }
169 free(menu);
170 }
171
bootmenu_entry_shortcut_key(int index)172 static char bootmenu_entry_shortcut_key(int index)
173 {
174 switch (index) {
175 /* 1-9 shortcut key (0 reserved) */
176 case 0 ... 8:
177 return '1' + index;
178 /* a-z shortcut key */
179 case 9 ... 34:
180 return 'a' + index - 9;
181 /* We support shortcut for up to 34 options (0 reserved) */
182 default:
183 return -ENOENT;
184 }
185 }
186
187 /**
188 * prepare_bootmenu_entry() - generate the bootmenu_xx entries
189 *
190 * This function read the "bootmenu_x" U-Boot environment variable
191 * and generate the bootmenu entries.
192 *
193 * @menu: pointer to the bootmenu structure
194 * @current: pointer to the last bootmenu entry list
195 * @index: pointer to the index of the last bootmenu entry,
196 * the number of bootmenu entry is added by this function
197 * Return: 1 on success, negative value on error
198 */
prepare_bootmenu_entry(struct bootmenu_data * menu,struct bootmenu_entry ** current,unsigned short int * index)199 static int prepare_bootmenu_entry(struct bootmenu_data *menu,
200 struct bootmenu_entry **current,
201 unsigned short int *index)
202 {
203 char *sep;
204 const char *option;
205 unsigned short int i = *index;
206 struct bootmenu_entry *entry = NULL;
207 struct bootmenu_entry *iter = *current;
208
209 while ((option = bootmenu_getoption(i))) {
210 char shortcut_key;
211 int len;
212
213 /* bootmenu_[num] format is "[title]=[commands]" */
214 sep = strchr(option, '=');
215 if (!sep) {
216 printf("Invalid bootmenu entry: %s\n", option);
217 break;
218 }
219
220 entry = malloc(sizeof(struct bootmenu_entry));
221 if (!entry)
222 return -ENOMEM;
223
224 /* Add shotcut key option: %c. %s\0 */
225 len = sep - option + 4;
226
227 entry->title = malloc(len);
228 if (!entry->title) {
229 free(entry);
230 return -ENOMEM;
231 }
232
233 shortcut_key = bootmenu_entry_shortcut_key(i);
234 /* Use emtpy space if entry doesn't support shortcut key */
235 snprintf(entry->title, len, "%c%c %s",
236 shortcut_key > 0 ? shortcut_key : ' ',
237 shortcut_key > 0 ? '.' : ' ',
238 option);
239
240 entry->command = strdup(sep + 1);
241 if (!entry->command) {
242 free(entry->title);
243 free(entry);
244 return -ENOMEM;
245 }
246
247 sprintf(entry->key, "%d", i);
248
249 entry->num = i;
250 entry->menu = menu;
251 entry->type = BOOTMENU_TYPE_BOOTMENU;
252 entry->bootorder = i;
253 entry->next = NULL;
254
255 if (!iter)
256 menu->first = entry;
257 else
258 iter->next = entry;
259
260 iter = entry;
261 ++i;
262
263 if (i == MAX_COUNT - 1)
264 break;
265 }
266
267 *index = i;
268 *current = iter;
269
270 return 1;
271 }
272
273 #if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) && (IS_ENABLED(CONFIG_CMD_EFICONFIG))
274 /**
275 * prepare_uefi_bootorder_entry() - generate the uefi bootmenu entries
276 *
277 * This function reads the "BootOrder" UEFI variable
278 * and generate the bootmenu entries in the order of "BootOrder".
279 *
280 * @menu: pointer to the bootmenu structure
281 * @current: pointer to the last bootmenu entry list
282 * @index: pointer to the index of the last bootmenu entry,
283 * the number of uefi entry is added by this function
284 * Return: 1 on success, negative value on error
285 */
prepare_uefi_bootorder_entry(struct bootmenu_data * menu,struct bootmenu_entry ** current,unsigned short int * index)286 static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
287 struct bootmenu_entry **current,
288 unsigned short int *index)
289 {
290 u16 *bootorder;
291 efi_status_t ret;
292 unsigned short j;
293 efi_uintn_t num, size;
294 void *load_option;
295 struct efi_load_option lo;
296 u16 varname[] = u"Boot####";
297 unsigned short int i = *index;
298 struct bootmenu_entry *entry = NULL;
299 struct bootmenu_entry *iter = *current;
300
301 bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
302 if (!bootorder)
303 return -ENOENT;
304
305 num = size / sizeof(u16);
306 for (j = 0; j < num; j++) {
307 entry = malloc(sizeof(struct bootmenu_entry));
308 if (!entry)
309 return -ENOMEM;
310
311 efi_create_indexed_name(varname, sizeof(varname),
312 "Boot", bootorder[j]);
313 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
314 if (!load_option)
315 continue;
316
317 ret = efi_deserialize_load_option(&lo, load_option, &size);
318 if (ret != EFI_SUCCESS) {
319 log_warning("Invalid load option for %ls\n", varname);
320 free(load_option);
321 free(entry);
322 continue;
323 }
324
325 if (lo.attributes & LOAD_OPTION_ACTIVE) {
326 char *buf;
327
328 buf = calloc(1, utf16_utf8_strlen(lo.label) + 1);
329 if (!buf) {
330 free(load_option);
331 free(entry);
332 free(bootorder);
333 return -ENOMEM;
334 }
335 entry->title = buf;
336 utf16_utf8_strncpy(&buf, lo.label, u16_strlen(lo.label));
337 entry->command = strdup("bootefi bootmgr");
338 sprintf(entry->key, "%d", i);
339 entry->num = i;
340 entry->menu = menu;
341 entry->type = BOOTMENU_TYPE_UEFI_BOOT_OPTION;
342 entry->bootorder = bootorder[j];
343 entry->next = NULL;
344
345 if (!iter)
346 menu->first = entry;
347 else
348 iter->next = entry;
349
350 iter = entry;
351 i++;
352 }
353
354 free(load_option);
355
356 if (i == MAX_COUNT - 1)
357 break;
358 }
359
360 free(bootorder);
361 *index = i;
362 *current = iter;
363
364 return 1;
365 }
366 #endif
367
368 /**
369 * bootmenu_create() - create boot menu entries
370 *
371 * @uefi: consider UEFI boot options
372 * @delay: autostart delay in seconds
373 */
bootmenu_create(int uefi,int delay)374 static struct bootmenu_data *bootmenu_create(int uefi, int delay)
375 {
376 int ret;
377 unsigned short int i = 0;
378 struct bootmenu_data *menu;
379 struct bootmenu_entry *iter = NULL;
380 struct bootmenu_entry *entry;
381 char *default_str;
382
383 menu = malloc(sizeof(struct bootmenu_data));
384 if (!menu)
385 return NULL;
386
387 menu->delay = delay;
388 menu->active = 0;
389 menu->last_active = -1;
390 menu->first = NULL;
391
392 default_str = env_get("bootmenu_default");
393 if (default_str)
394 menu->active = (int)simple_strtol(default_str, NULL, 10);
395
396 ret = prepare_bootmenu_entry(menu, &iter, &i);
397 if (ret < 0)
398 goto cleanup;
399
400 #if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) && (IS_ENABLED(CONFIG_CMD_EFICONFIG))
401 if (uefi && i < MAX_COUNT - 1) {
402 efi_status_t efi_ret;
403
404 /*
405 * UEFI specification requires booting from removal media using
406 * a architecture-specific default image name such as BOOTAA64.EFI.
407 */
408 efi_ret = efi_bootmgr_update_media_device_boot_option();
409 if (efi_ret != EFI_SUCCESS)
410 goto cleanup;
411
412 ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
413 if (ret < 0 && ret != -ENOENT)
414 goto cleanup;
415 }
416 #endif
417
418 /* Add Exit entry at the end */
419 if (i <= MAX_COUNT - 1) {
420 entry = malloc(sizeof(struct bootmenu_entry));
421 if (!entry)
422 goto cleanup;
423
424 /* Add Quit entry if exiting bootmenu is disabled */
425 if (!IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE))
426 entry->title = strdup("0. Exit");
427 else
428 entry->title = strdup("0. Quit");
429
430 if (!entry->title) {
431 free(entry);
432 goto cleanup;
433 }
434
435 entry->command = strdup("");
436 if (!entry->command) {
437 free(entry->title);
438 free(entry);
439 goto cleanup;
440 }
441
442 sprintf(entry->key, "%d", i);
443
444 entry->num = i;
445 entry->menu = menu;
446 entry->type = BOOTMENU_TYPE_NONE;
447 entry->next = NULL;
448
449 if (!iter)
450 menu->first = entry;
451 else
452 iter->next = entry;
453
454 iter = entry;
455 ++i;
456 }
457
458 menu->count = i;
459
460 if ((menu->active >= menu->count)||(menu->active < 0)) { //ensure active menuitem is inside menu
461 printf("active menuitem (%d) is outside menu (0..%d)\n",menu->active,menu->count-1);
462 menu->active=0;
463 }
464
465 return menu;
466
467 cleanup:
468 bootmenu_destroy(menu);
469 return NULL;
470 }
471
menu_display_statusline(struct menu * m)472 static void menu_display_statusline(struct menu *m)
473 {
474 struct bootmenu_entry *entry;
475 struct bootmenu_data *menu;
476
477 if (menu_default_choice(m, (void *)&entry) < 0)
478 return;
479
480 menu = entry->menu;
481
482 printf(ANSI_CURSOR_POSITION, 1, 1);
483 puts(ANSI_CLEAR_LINE);
484 printf(ANSI_CURSOR_POSITION, 2, 3);
485 puts("*** U-Boot Boot Menu ***");
486 puts(ANSI_CLEAR_LINE_TO_END);
487 printf(ANSI_CURSOR_POSITION, 3, 1);
488 puts(ANSI_CLEAR_LINE);
489
490 /* First 3 lines are bootmenu header + 2 empty lines between entries */
491 printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
492 puts(ANSI_CLEAR_LINE);
493 printf(ANSI_CURSOR_POSITION, menu->count + 6, 3);
494 puts("Press UP/DOWN to move, ENTER to select, ESC to quit");
495 puts(ANSI_CLEAR_LINE_TO_END);
496 printf(ANSI_CURSOR_POSITION, menu->count + 7, 1);
497 puts(ANSI_CLEAR_LINE);
498 }
499
handle_uefi_bootnext(void)500 static void handle_uefi_bootnext(void)
501 {
502 u16 bootnext;
503 efi_status_t ret;
504 efi_uintn_t size;
505
506 /* Initialize EFI drivers */
507 ret = efi_init_obj_list();
508 if (ret != EFI_SUCCESS) {
509 log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
510 ret & ~EFI_ERROR_MASK);
511
512 return;
513 }
514
515 /* If UEFI BootNext variable is set, boot the BootNext load option */
516 size = sizeof(u16);
517 ret = efi_get_variable_int(u"BootNext",
518 &efi_global_variable_guid,
519 NULL, &size, &bootnext, NULL);
520 if (ret == EFI_SUCCESS)
521 /* BootNext does exist here, try to boot */
522 run_command("bootefi bootmgr", 0);
523 }
524
525 /**
526 * bootmenu_show - display boot menu
527 *
528 * @uefi: generated entries for UEFI boot options
529 * @delay: autoboot delay in seconds
530 */
bootmenu_show(int uefi,int delay)531 static enum bootmenu_ret bootmenu_show(int uefi, int delay)
532 {
533 int cmd_ret;
534 int init = 0;
535 void *choice = NULL;
536 char *title = NULL;
537 char *command = NULL;
538 struct menu *menu;
539 struct bootmenu_entry *iter;
540 int ret = BOOTMENU_RET_SUCCESS;
541 struct bootmenu_data *bootmenu;
542 efi_status_t efi_ret = EFI_SUCCESS;
543 char *option, *sep;
544
545 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && uefi)
546 handle_uefi_bootnext();
547
548 /* If delay is 0 do not create menu, just run first entry */
549 if (delay == 0) {
550 option = bootmenu_getoption(0);
551 if (!option) {
552 puts("bootmenu option 0 was not found\n");
553 return BOOTMENU_RET_FAIL;
554 }
555 sep = strchr(option, '=');
556 if (!sep) {
557 puts("bootmenu option 0 is invalid\n");
558 return BOOTMENU_RET_FAIL;
559 }
560 cmd_ret = run_command(sep + 1, 0);
561 return (cmd_ret == CMD_RET_SUCCESS ? BOOTMENU_RET_SUCCESS : BOOTMENU_RET_FAIL);
562 }
563
564 bootmenu = bootmenu_create(uefi, delay);
565 if (!bootmenu)
566 return BOOTMENU_RET_FAIL;
567
568 menu = menu_create(NULL, bootmenu->delay, 1, menu_display_statusline,
569 bootmenu_print_entry, bootmenu_choice_entry,
570 bootmenu_need_reprint, bootmenu);
571 if (!menu) {
572 bootmenu_destroy(bootmenu);
573 return BOOTMENU_RET_FAIL;
574 }
575
576 for (iter = bootmenu->first; iter; iter = iter->next) {
577 if (menu_item_add(menu, iter->key, iter) != 1)
578 goto cleanup;
579 }
580
581 /* Default menu entry is always first */
582 menu_default_set(menu, "0");
583
584 puts(ANSI_CURSOR_HIDE);
585 puts(ANSI_CLEAR_CONSOLE);
586 printf(ANSI_CURSOR_POSITION, 1, 1);
587
588 init = 1;
589
590 if (menu_get_choice(menu, &choice) == 1) {
591 iter = choice;
592 title = strdup(iter->title);
593 command = strdup(iter->command);
594
595 /* last entry exits bootmenu */
596 if (iter->num == iter->menu->count - 1) {
597 ret = BOOTMENU_RET_QUIT;
598 goto cleanup;
599 }
600 } else {
601 goto cleanup;
602 }
603
604 /*
605 * If the selected entry is UEFI BOOT####, set the BootNext variable.
606 * Then uefi bootmgr is invoked by the preset command in iter->command.
607 */
608 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
609 if (iter->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION) {
610 /*
611 * UEFI specification requires BootNext variable needs non-volatile
612 * attribute, but this BootNext is only used inside of U-Boot and
613 * removed by efi bootmgr once BootNext is processed.
614 * So this BootNext can be volatile.
615 */
616 efi_ret = efi_set_variable_int(u"BootNext", &efi_global_variable_guid,
617 EFI_VARIABLE_BOOTSERVICE_ACCESS |
618 EFI_VARIABLE_RUNTIME_ACCESS,
619 sizeof(u16), &iter->bootorder, false);
620 if (efi_ret != EFI_SUCCESS)
621 goto cleanup;
622 }
623 }
624
625 cleanup:
626 menu_destroy(menu);
627 bootmenu_destroy(bootmenu);
628
629 if (init) {
630 puts(ANSI_CURSOR_SHOW);
631 puts(ANSI_CLEAR_CONSOLE);
632 printf(ANSI_CURSOR_POSITION, 1, 1);
633 }
634
635 if (title && command) {
636 debug("Starting entry '%s'\n", title);
637 free(title);
638 if (efi_ret == EFI_SUCCESS)
639 cmd_ret = run_command(command, 0);
640 free(command);
641 }
642
643 #ifdef CFG_POSTBOOTMENU
644 run_command(CFG_POSTBOOTMENU, 0);
645 #endif
646
647 if (efi_ret != EFI_SUCCESS || cmd_ret != CMD_RET_SUCCESS)
648 ret = BOOTMENU_RET_FAIL;
649
650 return ret;
651 }
652
653 #ifdef CONFIG_AUTOBOOT_MENU_SHOW
menu_show(int bootdelay)654 int menu_show(int bootdelay)
655 {
656 int ret;
657
658 while (1) {
659 ret = bootmenu_show(1, bootdelay);
660 bootdelay = -1;
661 if (ret == BOOTMENU_RET_UPDATED)
662 continue;
663
664 if (IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE)) {
665 if (ret == BOOTMENU_RET_QUIT) {
666 /* default boot process */
667 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR))
668 run_command("bootefi bootmgr", 0);
669
670 run_command("run bootcmd", 0);
671 }
672 } else {
673 break;
674 }
675 }
676
677 return -1; /* -1 - abort boot and run monitor code */
678 }
679 #endif
680
do_bootmenu(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])681 int do_bootmenu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
682 {
683 char *delay_str = NULL;
684 int delay = 10;
685 int uefi = 0;
686
687 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
688 delay = CONFIG_BOOTDELAY;
689 #endif
690
691 if (argc >= 2) {
692 if (!strcmp("-e", argv[1])) {
693 uefi = 1;
694 --argc;
695 ++argv;
696 }
697 }
698 if (argc >= 2)
699 delay_str = argv[1];
700
701 if (!delay_str)
702 delay_str = env_get("bootmenu_delay");
703
704 if (delay_str)
705 delay = (int)simple_strtol(delay_str, NULL, 10);
706
707 bootmenu_show(uefi, delay);
708 return 0;
709 }
710
711 U_BOOT_CMD(
712 bootmenu, 2, 1, do_bootmenu,
713 "ANSI terminal bootmenu",
714 "[-e] [delay]\n"
715 "-e - show UEFI entries\n"
716 "delay - show ANSI terminal bootmenu with autoboot delay"
717 );
718