]> Git Repo - J-u-boot.git/blob - cmd/bootmenu.c
Merge patch series "dwc3: gadget: properly fix cache operations"
[J-u-boot.git] / cmd / bootmenu.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2011-2013 Pali Rohár <[email protected]>
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
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
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
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                         if (menu->active > 0)
107                                 --menu->active;
108                         /* no menu key selected, regenerate menu */
109                         return NULL;
110                 case BKEY_DOWN:
111                         if (menu->active < menu->count - 1)
112                                 ++menu->active;
113                         /* no menu key selected, regenerate menu */
114                         return NULL;
115                 case BKEY_SELECT:
116                         iter = menu->first;
117                         for (i = 0; i < menu->active; ++i)
118                                 iter = iter->next;
119                         return iter->key;
120                 case BKEY_QUIT:
121                         /* Quit by choosing the last entry */
122                         iter = menu->first;
123                         while (iter->next)
124                                 iter = iter->next;
125                         return iter->key;
126                 default:
127                         break;
128                 }
129         }
130
131         /* never happens */
132         debug("bootmenu: this should not happen");
133         return NULL;
134 }
135
136 static void bootmenu_destroy(struct bootmenu_data *menu)
137 {
138         struct bootmenu_entry *iter = menu->first;
139         struct bootmenu_entry *next;
140
141         while (iter) {
142                 next = iter->next;
143                 free(iter->title);
144                 free(iter->command);
145                 free(iter);
146                 iter = next;
147         }
148         free(menu);
149 }
150
151 /**
152  * prepare_bootmenu_entry() - generate the bootmenu_xx entries
153  *
154  * This function read the "bootmenu_x" U-Boot environment variable
155  * and generate the bootmenu entries.
156  *
157  * @menu:       pointer to the bootmenu structure
158  * @current:    pointer to the last bootmenu entry list
159  * @index:      pointer to the index of the last bootmenu entry,
160  *              the number of bootmenu entry is added by this function
161  * Return:      1 on success, negative value on error
162  */
163 static int prepare_bootmenu_entry(struct bootmenu_data *menu,
164                                   struct bootmenu_entry **current,
165                                   unsigned short int *index)
166 {
167         char *sep;
168         const char *option;
169         unsigned short int i = *index;
170         struct bootmenu_entry *entry = NULL;
171         struct bootmenu_entry *iter = *current;
172
173         while ((option = bootmenu_getoption(i))) {
174
175                 /* bootmenu_[num] format is "[title]=[commands]" */
176                 sep = strchr(option, '=');
177                 if (!sep) {
178                         printf("Invalid bootmenu entry: %s\n", option);
179                         break;
180                 }
181
182                 entry = malloc(sizeof(struct bootmenu_entry));
183                 if (!entry)
184                         return -ENOMEM;
185
186                 entry->title = strndup(option, sep - option);
187                 if (!entry->title) {
188                         free(entry);
189                         return -ENOMEM;
190                 }
191
192                 entry->command = strdup(sep + 1);
193                 if (!entry->command) {
194                         free(entry->title);
195                         free(entry);
196                         return -ENOMEM;
197                 }
198
199                 sprintf(entry->key, "%d", i);
200
201                 entry->num = i;
202                 entry->menu = menu;
203                 entry->type = BOOTMENU_TYPE_BOOTMENU;
204                 entry->bootorder = i;
205                 entry->next = NULL;
206
207                 if (!iter)
208                         menu->first = entry;
209                 else
210                         iter->next = entry;
211
212                 iter = entry;
213                 ++i;
214
215                 if (i == MAX_COUNT - 1)
216                         break;
217         }
218
219         *index = i;
220         *current = iter;
221
222         return 1;
223 }
224
225 #if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) && (IS_ENABLED(CONFIG_CMD_EFICONFIG))
226 /**
227  * prepare_uefi_bootorder_entry() - generate the uefi bootmenu entries
228  *
229  * This function read the "BootOrder" UEFI variable
230  * and generate the bootmenu entries in the order of "BootOrder".
231  *
232  * @menu:       pointer to the bootmenu structure
233  * @current:    pointer to the last bootmenu entry list
234  * @index:      pointer to the index of the last bootmenu entry,
235  *              the number of uefi entry is added by this function
236  * Return:      1 on success, negative value on error
237  */
238 static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
239                                         struct bootmenu_entry **current,
240                                         unsigned short int *index)
241 {
242         u16 *bootorder;
243         efi_status_t ret;
244         unsigned short j;
245         efi_uintn_t num, size;
246         void *load_option;
247         struct efi_load_option lo;
248         u16 varname[] = u"Boot####";
249         unsigned short int i = *index;
250         struct bootmenu_entry *entry = NULL;
251         struct bootmenu_entry *iter = *current;
252
253         bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
254         if (!bootorder)
255                 return -ENOENT;
256
257         num = size / sizeof(u16);
258         for (j = 0; j < num; j++) {
259                 entry = malloc(sizeof(struct bootmenu_entry));
260                 if (!entry)
261                         return -ENOMEM;
262
263                 efi_create_indexed_name(varname, sizeof(varname),
264                                         "Boot", bootorder[j]);
265                 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
266                 if (!load_option)
267                         continue;
268
269                 ret = efi_deserialize_load_option(&lo, load_option, &size);
270                 if (ret != EFI_SUCCESS) {
271                         log_warning("Invalid load option for %ls\n", varname);
272                         free(load_option);
273                         free(entry);
274                         continue;
275                 }
276
277                 if (lo.attributes & LOAD_OPTION_ACTIVE) {
278                         char *buf;
279
280                         buf = calloc(1, utf16_utf8_strlen(lo.label) + 1);
281                         if (!buf) {
282                                 free(load_option);
283                                 free(entry);
284                                 free(bootorder);
285                                 return -ENOMEM;
286                         }
287                         entry->title = buf;
288                         utf16_utf8_strncpy(&buf, lo.label, u16_strlen(lo.label));
289                         entry->command = strdup("bootefi bootmgr");
290                         sprintf(entry->key, "%d", i);
291                         entry->num = i;
292                         entry->menu = menu;
293                         entry->type = BOOTMENU_TYPE_UEFI_BOOT_OPTION;
294                         entry->bootorder = bootorder[j];
295                         entry->next = NULL;
296
297                         if (!iter)
298                                 menu->first = entry;
299                         else
300                                 iter->next = entry;
301
302                         iter = entry;
303                         i++;
304                 }
305
306                 free(load_option);
307
308                 if (i == MAX_COUNT - 1)
309                         break;
310         }
311
312         free(bootorder);
313         *index = i;
314         *current = iter;
315
316         return 1;
317 }
318 #endif
319
320 static struct bootmenu_data *bootmenu_create(int delay)
321 {
322         int ret;
323         unsigned short int i = 0;
324         struct bootmenu_data *menu;
325         struct bootmenu_entry *iter = NULL;
326         struct bootmenu_entry *entry;
327         char *default_str;
328
329         menu = malloc(sizeof(struct bootmenu_data));
330         if (!menu)
331                 return NULL;
332
333         menu->delay = delay;
334         menu->active = 0;
335         menu->first = NULL;
336
337         default_str = env_get("bootmenu_default");
338         if (default_str)
339                 menu->active = (int)simple_strtol(default_str, NULL, 10);
340
341         ret = prepare_bootmenu_entry(menu, &iter, &i);
342         if (ret < 0)
343                 goto cleanup;
344
345 #if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) && (IS_ENABLED(CONFIG_CMD_EFICONFIG))
346         if (i < MAX_COUNT - 1) {
347                 efi_status_t efi_ret;
348
349                 /*
350                  * UEFI specification requires booting from removal media using
351                  * a architecture-specific default image name such as BOOTAA64.EFI.
352                  */
353                 efi_ret = efi_bootmgr_update_media_device_boot_option();
354                 if (efi_ret != EFI_SUCCESS)
355                         goto cleanup;
356
357                 ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
358                 if (ret < 0 && ret != -ENOENT)
359                         goto cleanup;
360         }
361 #endif
362
363         /* Add Exit entry at the end */
364         if (i <= MAX_COUNT - 1) {
365                 entry = malloc(sizeof(struct bootmenu_entry));
366                 if (!entry)
367                         goto cleanup;
368
369                 /* Add Quit entry if exiting bootmenu is disabled */
370                 if (!IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE))
371                         entry->title = strdup("Exit");
372                 else
373                         entry->title = strdup("Quit");
374
375                 if (!entry->title) {
376                         free(entry);
377                         goto cleanup;
378                 }
379
380                 entry->command = strdup("");
381                 if (!entry->command) {
382                         free(entry->title);
383                         free(entry);
384                         goto cleanup;
385                 }
386
387                 sprintf(entry->key, "%d", i);
388
389                 entry->num = i;
390                 entry->menu = menu;
391                 entry->type = BOOTMENU_TYPE_NONE;
392                 entry->next = NULL;
393
394                 if (!iter)
395                         menu->first = entry;
396                 else
397                         iter->next = entry;
398
399                 iter = entry;
400                 ++i;
401         }
402
403         menu->count = i;
404
405         if ((menu->active >= menu->count)||(menu->active < 0)) { //ensure active menuitem is inside menu
406                 printf("active menuitem (%d) is outside menu (0..%d)\n",menu->active,menu->count-1);
407                 menu->active=0;
408         }
409
410         return menu;
411
412 cleanup:
413         bootmenu_destroy(menu);
414         return NULL;
415 }
416
417 static void menu_display_statusline(struct menu *m)
418 {
419         struct bootmenu_entry *entry;
420         struct bootmenu_data *menu;
421
422         if (menu_default_choice(m, (void *)&entry) < 0)
423                 return;
424
425         menu = entry->menu;
426
427         printf(ANSI_CURSOR_POSITION, 1, 1);
428         puts(ANSI_CLEAR_LINE);
429         printf(ANSI_CURSOR_POSITION, 2, 3);
430         puts("*** U-Boot Boot Menu ***");
431         puts(ANSI_CLEAR_LINE_TO_END);
432         printf(ANSI_CURSOR_POSITION, 3, 1);
433         puts(ANSI_CLEAR_LINE);
434
435         /* First 3 lines are bootmenu header + 2 empty lines between entries */
436         printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
437         puts(ANSI_CLEAR_LINE);
438         printf(ANSI_CURSOR_POSITION, menu->count + 6, 3);
439         puts("Press UP/DOWN to move, ENTER to select, ESC to quit");
440         puts(ANSI_CLEAR_LINE_TO_END);
441         printf(ANSI_CURSOR_POSITION, menu->count + 7, 1);
442         puts(ANSI_CLEAR_LINE);
443 }
444
445 static void handle_uefi_bootnext(void)
446 {
447         u16 bootnext;
448         efi_status_t ret;
449         efi_uintn_t size;
450
451         /* Initialize EFI drivers */
452         ret = efi_init_obj_list();
453         if (ret != EFI_SUCCESS) {
454                 log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
455                         ret & ~EFI_ERROR_MASK);
456
457                 return;
458         }
459
460         /* If UEFI BootNext variable is set, boot the BootNext load option */
461         size = sizeof(u16);
462         ret = efi_get_variable_int(u"BootNext",
463                                    &efi_global_variable_guid,
464                                    NULL, &size, &bootnext, NULL);
465         if (ret == EFI_SUCCESS)
466                 /* BootNext does exist here, try to boot */
467                 run_command("bootefi bootmgr", 0);
468 }
469
470 static enum bootmenu_ret bootmenu_show(int delay)
471 {
472         int cmd_ret;
473         int init = 0;
474         void *choice = NULL;
475         char *title = NULL;
476         char *command = NULL;
477         struct menu *menu;
478         struct bootmenu_entry *iter;
479         int ret = BOOTMENU_RET_SUCCESS;
480         struct bootmenu_data *bootmenu;
481         efi_status_t efi_ret = EFI_SUCCESS;
482         char *option, *sep;
483
484         if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR))
485                 handle_uefi_bootnext();
486
487         /* If delay is 0 do not create menu, just run first entry */
488         if (delay == 0) {
489                 option = bootmenu_getoption(0);
490                 if (!option) {
491                         puts("bootmenu option 0 was not found\n");
492                         return BOOTMENU_RET_FAIL;
493                 }
494                 sep = strchr(option, '=');
495                 if (!sep) {
496                         puts("bootmenu option 0 is invalid\n");
497                         return BOOTMENU_RET_FAIL;
498                 }
499                 cmd_ret = run_command(sep + 1, 0);
500                 return (cmd_ret == CMD_RET_SUCCESS ? BOOTMENU_RET_SUCCESS : BOOTMENU_RET_FAIL);
501         }
502
503         bootmenu = bootmenu_create(delay);
504         if (!bootmenu)
505                 return BOOTMENU_RET_FAIL;
506
507         menu = menu_create(NULL, bootmenu->delay, 1, menu_display_statusline,
508                            bootmenu_print_entry, bootmenu_choice_entry,
509                            bootmenu);
510         if (!menu) {
511                 bootmenu_destroy(bootmenu);
512                 return BOOTMENU_RET_FAIL;
513         }
514
515         for (iter = bootmenu->first; iter; iter = iter->next) {
516                 if (menu_item_add(menu, iter->key, iter) != 1)
517                         goto cleanup;
518         }
519
520         /* Default menu entry is always first */
521         menu_default_set(menu, "0");
522
523         puts(ANSI_CURSOR_HIDE);
524         puts(ANSI_CLEAR_CONSOLE);
525         printf(ANSI_CURSOR_POSITION, 1, 1);
526
527         init = 1;
528
529         if (menu_get_choice(menu, &choice) == 1) {
530                 iter = choice;
531                 title = strdup(iter->title);
532                 command = strdup(iter->command);
533
534                 /* last entry exits bootmenu */
535                 if (iter->num == iter->menu->count - 1) {
536                         ret = BOOTMENU_RET_QUIT;
537                         goto cleanup;
538                 }
539         } else {
540                 goto cleanup;
541         }
542
543         /*
544          * If the selected entry is UEFI BOOT####, set the BootNext variable.
545          * Then uefi bootmgr is invoked by the preset command in iter->command.
546          */
547         if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
548                 if (iter->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION) {
549                         /*
550                          * UEFI specification requires BootNext variable needs non-volatile
551                          * attribute, but this BootNext is only used inside of U-Boot and
552                          * removed by efi bootmgr once BootNext is processed.
553                          * So this BootNext can be volatile.
554                          */
555                         efi_ret = efi_set_variable_int(u"BootNext", &efi_global_variable_guid,
556                                                        EFI_VARIABLE_BOOTSERVICE_ACCESS |
557                                                        EFI_VARIABLE_RUNTIME_ACCESS,
558                                                        sizeof(u16), &iter->bootorder, false);
559                         if (efi_ret != EFI_SUCCESS)
560                                 goto cleanup;
561                 }
562         }
563
564 cleanup:
565         menu_destroy(menu);
566         bootmenu_destroy(bootmenu);
567
568         if (init) {
569                 puts(ANSI_CURSOR_SHOW);
570                 puts(ANSI_CLEAR_CONSOLE);
571                 printf(ANSI_CURSOR_POSITION, 1, 1);
572         }
573
574         if (title && command) {
575                 debug("Starting entry '%s'\n", title);
576                 free(title);
577                 if (efi_ret == EFI_SUCCESS)
578                         cmd_ret = run_command(command, 0);
579                 free(command);
580         }
581
582 #ifdef CFG_POSTBOOTMENU
583         run_command(CFG_POSTBOOTMENU, 0);
584 #endif
585
586         if (efi_ret != EFI_SUCCESS || cmd_ret != CMD_RET_SUCCESS)
587                 ret = BOOTMENU_RET_FAIL;
588
589         return ret;
590 }
591
592 #ifdef CONFIG_AUTOBOOT_MENU_SHOW
593 int menu_show(int bootdelay)
594 {
595         int ret;
596
597         while (1) {
598                 ret = bootmenu_show(bootdelay);
599                 bootdelay = -1;
600                 if (ret == BOOTMENU_RET_UPDATED)
601                         continue;
602
603                 if (IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE)) {
604                         if (ret == BOOTMENU_RET_QUIT) {
605                                 /* default boot process */
606                                 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR))
607                                         run_command("bootefi bootmgr", 0);
608
609                                 run_command("run bootcmd", 0);
610                         }
611                 } else {
612                         break;
613                 }
614         }
615
616         return -1; /* -1 - abort boot and run monitor code */
617 }
618 #endif
619
620 int do_bootmenu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
621 {
622         char *delay_str = NULL;
623         int delay = 10;
624
625 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
626         delay = CONFIG_BOOTDELAY;
627 #endif
628
629         if (argc >= 2)
630                 delay_str = argv[1];
631
632         if (!delay_str)
633                 delay_str = env_get("bootmenu_delay");
634
635         if (delay_str)
636                 delay = (int)simple_strtol(delay_str, NULL, 10);
637
638         bootmenu_show(delay);
639         return 0;
640 }
641
642 U_BOOT_CMD(
643         bootmenu, 2, 1, do_bootmenu,
644         "ANSI terminal bootmenu",
645         "[delay]\n"
646         "    - show ANSI terminal bootmenu with autoboot delay"
647 );
This page took 0.063355 seconds and 4 git commands to generate.