blob: 977a04b7d7697f56cf49a4b03f91f2449d956346 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Pali Roháre7abe912013-03-23 14:53:08 +00002/*
Pali Rohár53086652020-04-01 00:35:08 +02003 * (C) Copyright 2011-2013 Pali Rohár <pali@kernel.org>
Pali Roháre7abe912013-03-23 14:53:08 +00004 */
5
Masahisa Kojimaa3d0aa82022-04-28 17:09:41 +09006#include <charset.h>
Simon Glass32bab0e2023-01-06 08:52:26 -06007#include <cli.h>
Pali Roháre7abe912013-03-23 14:53:08 +00008#include <command.h>
9#include <ansi.h>
Masahisa Kojimac416f1c2022-09-12 17:33:54 +090010#include <efi_config.h>
Masahisa Kojimac606c002022-04-28 17:09:42 +090011#include <efi_variable.h>
Simon Glass7b51b572019-08-01 09:46:52 -060012#include <env.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060013#include <log.h>
Pali Roháre7abe912013-03-23 14:53:08 +000014#include <menu.h>
Pali Roháre7abe912013-03-23 14:53:08 +000015#include <watchdog.h>
16#include <malloc.h>
Simon Glassc05ed002020-05-10 11:40:11 -060017#include <linux/delay.h>
Pali Roháre7abe912013-03-23 14:53:08 +000018#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
Masahisa Kojima2158b0d2022-04-28 17:09:44 +090030enum bootmenu_ret {
31 BOOTMENU_RET_SUCCESS = 0,
32 BOOTMENU_RET_FAIL,
33 BOOTMENU_RET_QUIT,
34 BOOTMENU_RET_UPDATED,
35};
36
Masahisa Kojimaa3d0aa82022-04-28 17:09:41 +090037enum boot_type {
38 BOOTMENU_TYPE_NONE = 0,
39 BOOTMENU_TYPE_BOOTMENU,
Masahisa Kojimac606c002022-04-28 17:09:42 +090040 BOOTMENU_TYPE_UEFI_BOOT_OPTION,
Masahisa Kojimaa3d0aa82022-04-28 17:09:41 +090041};
42
Pali Roháre7abe912013-03-23 14:53:08 +000043struct bootmenu_entry {
44 unsigned short int num; /* unique number 0 .. MAX_COUNT */
45 char key[3]; /* key identifier of number */
Masahisa Kojimae85727d2022-05-29 10:52:43 +090046 char *title; /* title of entry */
Pali Roháre7abe912013-03-23 14:53:08 +000047 char *command; /* hush command of entry */
Masahisa Kojimaa3d0aa82022-04-28 17:09:41 +090048 enum boot_type type; /* boot type of entry */
49 u16 bootorder; /* order for each boot type */
Pali Roháre7abe912013-03-23 14:53:08 +000050 struct bootmenu_data *menu; /* this bootmenu */
51 struct bootmenu_entry *next; /* next menu entry (num+1) */
52};
53
Pali Roháre7abe912013-03-23 14:53:08 +000054static char *bootmenu_getoption(unsigned short int n)
55{
Lan Yixun (dlan)0eb33ad2013-06-27 18:58:53 +080056 char name[MAX_ENV_SIZE];
Pali Roháre7abe912013-03-23 14:53:08 +000057
58 if (n > MAX_COUNT)
59 return NULL;
60
Lan Yixun (dlan)0eb33ad2013-06-27 18:58:53 +080061 sprintf(name, "bootmenu_%d", n);
Simon Glass00caae62017-08-03 12:22:12 -060062 return env_get(name);
Pali Roháre7abe912013-03-23 14:53:08 +000063}
64
65static 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 */
Heinrich Schuchardtd1d7ed72022-05-01 23:17:18 +020074 printf(ANSI_CURSOR_POSITION, entry->num + 4, 7);
Pali Roháre7abe912013-03-23 14:53:08 +000075
76 if (reverse)
77 puts(ANSI_COLOR_REVERSE);
78
Masahisa Kojimae85727d2022-05-29 10:52:43 +090079 printf("%s", entry->title);
Pali Roháre7abe912013-03-23 14:53:08 +000080
81 if (reverse)
82 puts(ANSI_COLOR_RESET);
83}
84
Pali Roháre7abe912013-03-23 14:53:08 +000085static char *bootmenu_choice_entry(void *data)
86{
Simon Glass32bab0e2023-01-06 08:52:26 -060087 struct cli_ch_state s_cch, *cch = &s_cch;
Pali Roháre7abe912013-03-23 14:53:08 +000088 struct bootmenu_data *menu = data;
89 struct bootmenu_entry *iter;
Simon Glass2da4a152023-01-06 08:52:22 -060090 enum bootmenu_key key = BKEY_NONE;
Pali Roháre7abe912013-03-23 14:53:08 +000091 int i;
92
Simon Glass32bab0e2023-01-06 08:52:26 -060093 cli_ch_init(cch);
94
Pali Roháre7abe912013-03-23 14:53:08 +000095 while (1) {
96 if (menu->delay >= 0) {
97 /* Autoboot was not stopped */
Simon Glass32bab0e2023-01-06 08:52:26 -060098 key = bootmenu_autoboot_loop(menu, cch);
Pali Roháre7abe912013-03-23 14:53:08 +000099 } else {
100 /* Some key was pressed, so autoboot was stopped */
Simon Glass32bab0e2023-01-06 08:52:26 -0600101 key = bootmenu_loop(menu, cch);
Pali Roháre7abe912013-03-23 14:53:08 +0000102 }
103
104 switch (key) {
Simon Glass2da4a152023-01-06 08:52:22 -0600105 case BKEY_UP:
Pali Roháre7abe912013-03-23 14:53:08 +0000106 if (menu->active > 0)
107 --menu->active;
108 /* no menu key selected, regenerate menu */
109 return NULL;
Simon Glass2da4a152023-01-06 08:52:22 -0600110 case BKEY_DOWN:
Pali Roháre7abe912013-03-23 14:53:08 +0000111 if (menu->active < menu->count - 1)
112 ++menu->active;
113 /* no menu key selected, regenerate menu */
114 return NULL;
Simon Glass2da4a152023-01-06 08:52:22 -0600115 case BKEY_SELECT:
Pali Roháre7abe912013-03-23 14:53:08 +0000116 iter = menu->first;
117 for (i = 0; i < menu->active; ++i)
118 iter = iter->next;
119 return iter->key;
Simon Glass2da4a152023-01-06 08:52:22 -0600120 case BKEY_QUIT:
Svyatoslav Ryhel983d6e52024-01-17 12:55:46 +0200121 /* Quit by choosing the last entry */
Pali Rohár83a287a2020-12-27 01:04:38 +0100122 iter = menu->first;
123 while (iter->next)
124 iter = iter->next;
125 return iter->key;
Pali Roháre7abe912013-03-23 14:53:08 +0000126 default:
127 break;
128 }
129 }
130
131 /* never happens */
132 debug("bootmenu: this should not happen");
133 return NULL;
134}
135
136static 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
Masahisa Kojimaa3d0aa82022-04-28 17:09:41 +0900151/**
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 */
163static int prepare_bootmenu_entry(struct bootmenu_data *menu,
164 struct bootmenu_entry **current,
165 unsigned short int *index)
Pali Roháre7abe912013-03-23 14:53:08 +0000166{
Pali Roháre7abe912013-03-23 14:53:08 +0000167 char *sep;
Masahisa Kojimaa3d0aa82022-04-28 17:09:41 +0900168 const char *option;
169 unsigned short int i = *index;
170 struct bootmenu_entry *entry = NULL;
171 struct bootmenu_entry *iter = *current;
Frank Wunderlichf7bb20a2018-10-05 11:41:59 +0200172
Pali Roháre7abe912013-03-23 14:53:08 +0000173 while ((option = bootmenu_getoption(i))) {
Masahisa Kojimaa3d0aa82022-04-28 17:09:41 +0900174
Masahisa Kojimae85727d2022-05-29 10:52:43 +0900175 /* bootmenu_[num] format is "[title]=[commands]" */
Pali Roháre7abe912013-03-23 14:53:08 +0000176 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)
Masahisa Kojimaa3d0aa82022-04-28 17:09:41 +0900184 return -ENOMEM;
Pali Roháre7abe912013-03-23 14:53:08 +0000185
Masahisa Kojimae85727d2022-05-29 10:52:43 +0900186 entry->title = strndup(option, sep - option);
Pali Roháre7abe912013-03-23 14:53:08 +0000187 if (!entry->title) {
188 free(entry);
Masahisa Kojimaa3d0aa82022-04-28 17:09:41 +0900189 return -ENOMEM;
Pali Roháre7abe912013-03-23 14:53:08 +0000190 }
Pali Roháre7abe912013-03-23 14:53:08 +0000191
Masahisa Kojimae85727d2022-05-29 10:52:43 +0900192 entry->command = strdup(sep + 1);
Pali Roháre7abe912013-03-23 14:53:08 +0000193 if (!entry->command) {
194 free(entry->title);
195 free(entry);
Masahisa Kojimaa3d0aa82022-04-28 17:09:41 +0900196 return -ENOMEM;
Pali Roháre7abe912013-03-23 14:53:08 +0000197 }
Pali Roháre7abe912013-03-23 14:53:08 +0000198
199 sprintf(entry->key, "%d", i);
200
201 entry->num = i;
202 entry->menu = menu;
Masahisa Kojimaa3d0aa82022-04-28 17:09:41 +0900203 entry->type = BOOTMENU_TYPE_BOOTMENU;
204 entry->bootorder = i;
Pali Roháre7abe912013-03-23 14:53:08 +0000205 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
Masahisa Kojimaa3d0aa82022-04-28 17:09:41 +0900219 *index = i;
220 *current = iter;
221
222 return 1;
223}
224
Simon Glass6cd3a792023-02-05 15:36:28 -0700225#if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) && (IS_ENABLED(CONFIG_CMD_EFICONFIG))
Masahisa Kojimac606c002022-04-28 17:09:42 +0900226/**
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 */
238static 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) {
Masahisa Kojimae85727d2022-05-29 10:52:43 +0900278 char *buf;
279
280 buf = calloc(1, utf16_utf8_strlen(lo.label) + 1);
281 if (!buf) {
Masahisa Kojimac606c002022-04-28 17:09:42 +0900282 free(load_option);
283 free(entry);
284 free(bootorder);
285 return -ENOMEM;
286 }
Masahisa Kojimae85727d2022-05-29 10:52:43 +0900287 entry->title = buf;
288 utf16_utf8_strncpy(&buf, lo.label, u16_strlen(lo.label));
Masahisa Kojimac606c002022-04-28 17:09:42 +0900289 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}
Masahisa Kojimae85727d2022-05-29 10:52:43 +0900318#endif
Masahisa Kojimac606c002022-04-28 17:09:42 +0900319
Masahisa Kojimaa3d0aa82022-04-28 17:09:41 +0900320static 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
Simon Glass6cd3a792023-02-05 15:36:28 -0700345#if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) && (IS_ENABLED(CONFIG_CMD_EFICONFIG))
Masahisa Kojimae85727d2022-05-29 10:52:43 +0900346 if (i < MAX_COUNT - 1) {
Masahisa Kojimac416f1c2022-09-12 17:33:54 +0900347 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 */
Raymond Mao339b5272023-06-19 14:22:58 -0700353 efi_ret = efi_bootmgr_update_media_device_boot_option();
Raymond Mao9945bc42023-06-19 14:22:59 -0700354 if (efi_ret != EFI_SUCCESS)
Masahisa Kojimac416f1c2022-09-12 17:33:54 +0900355 goto cleanup;
356
357 ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
358 if (ret < 0 && ret != -ENOENT)
359 goto cleanup;
Masahisa Kojimac606c002022-04-28 17:09:42 +0900360 }
Masahisa Kojimae85727d2022-05-29 10:52:43 +0900361#endif
Masahisa Kojimac606c002022-04-28 17:09:42 +0900362
Svyatoslav Ryhel983d6e52024-01-17 12:55:46 +0200363 /* Add Exit entry at the end */
Pali Roháre7abe912013-03-23 14:53:08 +0000364 if (i <= MAX_COUNT - 1) {
365 entry = malloc(sizeof(struct bootmenu_entry));
366 if (!entry)
367 goto cleanup;
368
Svyatoslav Ryhel983d6e52024-01-17 12:55:46 +0200369 /* Add Quit entry if exiting bootmenu is disabled */
Masahisa Kojima83f73632022-05-26 19:09:38 +0900370 if (!IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE))
Svyatoslav Ryhel983d6e52024-01-17 12:55:46 +0200371 entry->title = strdup("Exit");
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900372 else
Masahisa Kojimae85727d2022-05-29 10:52:43 +0900373 entry->title = strdup("Quit");
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900374
Pali Roháre7abe912013-03-23 14:53:08 +0000375 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;
Masahisa Kojimaa3d0aa82022-04-28 17:09:41 +0900391 entry->type = BOOTMENU_TYPE_NONE;
Pali Roháre7abe912013-03-23 14:53:08 +0000392 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;
Frank Wunderlichbace2212018-12-03 11:23:41 +0100404
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
Pali Roháre7abe912013-03-23 14:53:08 +0000410 return menu;
411
412cleanup:
413 bootmenu_destroy(menu);
414 return NULL;
415}
416
Thirupathaiah Annapureddy5168d7a2020-03-18 11:38:42 -0700417static 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);
Heinrich Schuchardtd1d7ed72022-05-01 23:17:18 +0200429 printf(ANSI_CURSOR_POSITION, 2, 3);
430 puts("*** U-Boot Boot Menu ***");
Thirupathaiah Annapureddy5168d7a2020-03-18 11:38:42 -0700431 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);
Heinrich Schuchardtd1d7ed72022-05-01 23:17:18 +0200438 printf(ANSI_CURSOR_POSITION, menu->count + 6, 3);
Masahisa Kojima45f53192023-02-02 18:24:43 +0900439 puts("Press UP/DOWN to move, ENTER to select, ESC to quit");
Thirupathaiah Annapureddy5168d7a2020-03-18 11:38:42 -0700440 puts(ANSI_CLEAR_LINE_TO_END);
441 printf(ANSI_CURSOR_POSITION, menu->count + 7, 1);
442 puts(ANSI_CLEAR_LINE);
443}
444
Masahisa Kojimac606c002022-04-28 17:09:42 +0900445static 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
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900470static enum bootmenu_ret bootmenu_show(int delay)
Pali Roháre7abe912013-03-23 14:53:08 +0000471{
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900472 int cmd_ret;
Pali Roháre7abe912013-03-23 14:53:08 +0000473 int init = 0;
474 void *choice = NULL;
Masahisa Kojimae85727d2022-05-29 10:52:43 +0900475 char *title = NULL;
Pali Roháre7abe912013-03-23 14:53:08 +0000476 char *command = NULL;
477 struct menu *menu;
Pali Roháre7abe912013-03-23 14:53:08 +0000478 struct bootmenu_entry *iter;
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900479 int ret = BOOTMENU_RET_SUCCESS;
480 struct bootmenu_data *bootmenu;
Masahisa Kojimac606c002022-04-28 17:09:42 +0900481 efi_status_t efi_ret = EFI_SUCCESS;
Pali Roháre7abe912013-03-23 14:53:08 +0000482 char *option, *sep;
483
Masahisa Kojimac606c002022-04-28 17:09:42 +0900484 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR))
485 handle_uefi_bootnext();
486
Pali Roháre7abe912013-03-23 14:53:08 +0000487 /* 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");
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900492 return BOOTMENU_RET_FAIL;
Pali Roháre7abe912013-03-23 14:53:08 +0000493 }
494 sep = strchr(option, '=');
495 if (!sep) {
496 puts("bootmenu option 0 is invalid\n");
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900497 return BOOTMENU_RET_FAIL;
Pali Roháre7abe912013-03-23 14:53:08 +0000498 }
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900499 cmd_ret = run_command(sep + 1, 0);
500 return (cmd_ret == CMD_RET_SUCCESS ? BOOTMENU_RET_SUCCESS : BOOTMENU_RET_FAIL);
Pali Roháre7abe912013-03-23 14:53:08 +0000501 }
502
503 bootmenu = bootmenu_create(delay);
504 if (!bootmenu)
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900505 return BOOTMENU_RET_FAIL;
Pali Roháre7abe912013-03-23 14:53:08 +0000506
Thirupathaiah Annapureddy5168d7a2020-03-18 11:38:42 -0700507 menu = menu_create(NULL, bootmenu->delay, 1, menu_display_statusline,
508 bootmenu_print_entry, bootmenu_choice_entry,
509 bootmenu);
Pali Roháre7abe912013-03-23 14:53:08 +0000510 if (!menu) {
511 bootmenu_destroy(bootmenu);
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900512 return BOOTMENU_RET_FAIL;
Pali Roháre7abe912013-03-23 14:53:08 +0000513 }
514
515 for (iter = bootmenu->first; iter; iter = iter->next) {
Masahisa Kojima990f6632022-03-24 22:54:33 +0900516 if (menu_item_add(menu, iter->key, iter) != 1)
Pali Roháre7abe912013-03-23 14:53:08 +0000517 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
Masahisa Kojima990f6632022-03-24 22:54:33 +0900529 if (menu_get_choice(menu, &choice) == 1) {
Pali Roháre7abe912013-03-23 14:53:08 +0000530 iter = choice;
Masahisa Kojimae85727d2022-05-29 10:52:43 +0900531 title = strdup(iter->title);
Pali Roháre7abe912013-03-23 14:53:08 +0000532 command = strdup(iter->command);
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900533
Svyatoslav Ryhel983d6e52024-01-17 12:55:46 +0200534 /* last entry exits bootmenu */
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900535 if (iter->num == iter->menu->count - 1) {
536 ret = BOOTMENU_RET_QUIT;
537 goto cleanup;
538 }
539 } else {
540 goto cleanup;
Pali Roháre7abe912013-03-23 14:53:08 +0000541 }
542
Masahisa Kojimac606c002022-04-28 17:09:42 +0900543 /*
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
Pali Roháre7abe912013-03-23 14:53:08 +0000564cleanup:
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) {
Masahisa Kojimae85727d2022-05-29 10:52:43 +0900575 debug("Starting entry '%s'\n", title);
Pali Roháre7abe912013-03-23 14:53:08 +0000576 free(title);
Masahisa Kojimac606c002022-04-28 17:09:42 +0900577 if (efi_ret == EFI_SUCCESS)
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900578 cmd_ret = run_command(command, 0);
Pali Roháre7abe912013-03-23 14:53:08 +0000579 free(command);
580 }
581
Tom Rini2adbb292022-12-04 10:13:33 -0500582#ifdef CFG_POSTBOOTMENU
583 run_command(CFG_POSTBOOTMENU, 0);
Pali Roháre7abe912013-03-23 14:53:08 +0000584#endif
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900585
586 if (efi_ret != EFI_SUCCESS || cmd_ret != CMD_RET_SUCCESS)
587 ret = BOOTMENU_RET_FAIL;
588
589 return ret;
Pali Roháre7abe912013-03-23 14:53:08 +0000590}
591
Simon Glasse2313062019-07-20 20:51:24 -0600592#ifdef CONFIG_AUTOBOOT_MENU_SHOW
Pali Roháre7abe912013-03-23 14:53:08 +0000593int menu_show(int bootdelay)
594{
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900595 int ret;
596
597 while (1) {
598 ret = bootmenu_show(bootdelay);
599 bootdelay = -1;
600 if (ret == BOOTMENU_RET_UPDATED)
601 continue;
602
Masahisa Kojima83f73632022-05-26 19:09:38 +0900603 if (IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE)) {
Masahisa Kojima2158b0d2022-04-28 17:09:44 +0900604 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
Pali Roháre7abe912013-03-23 14:53:08 +0000616 return -1; /* -1 - abort boot and run monitor code */
617}
618#endif
619
Simon Glass09140112020-05-10 11:40:03 -0600620int do_bootmenu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Pali Roháre7abe912013-03-23 14:53:08 +0000621{
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)
Simon Glass00caae62017-08-03 12:22:12 -0600633 delay_str = env_get("bootmenu_delay");
Pali Roháre7abe912013-03-23 14:53:08 +0000634
635 if (delay_str)
636 delay = (int)simple_strtol(delay_str, NULL, 10);
637
638 bootmenu_show(delay);
639 return 0;
640}
641
642U_BOOT_CMD(
643 bootmenu, 2, 1, do_bootmenu,
644 "ANSI terminal bootmenu",
645 "[delay]\n"
646 " - show ANSI terminal bootmenu with autoboot delay"
647);