blob: a5c979079f4aed2e84ca3443b67b20c8462ef296 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Pali Rohárac91b472013-03-23 14:53:08 +00002/*
Pali Rohár10a953d2020-04-01 00:35:08 +02003 * (C) Copyright 2011-2013 Pali Rohár <pali@kernel.org>
Pali Rohárac91b472013-03-23 14:53:08 +00004 */
5
Masahisa Kojima015405a2022-04-28 17:09:41 +09006#include <charset.h>
Simon Glass9d8d3872023-01-06 08:52:26 -06007#include <cli.h>
Pali Rohárac91b472013-03-23 14:53:08 +00008#include <command.h>
9#include <ansi.h>
Masahisa Kojima767a9e62022-09-12 17:33:54 +090010#include <efi_config.h>
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +090011#include <efi_variable.h>
Simon Glass0af6e2d2019-08-01 09:46:52 -060012#include <env.h>
Simon Glass0f2af882020-05-10 11:40:05 -060013#include <log.h>
Pali Rohárac91b472013-03-23 14:53:08 +000014#include <menu.h>
Pali Rohárac91b472013-03-23 14:53:08 +000015#include <watchdog.h>
16#include <malloc.h>
Simon Glassdbd79542020-05-10 11:40:11 -060017#include <linux/delay.h>
Pali Rohárac91b472013-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 Kojima43d0ab22022-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 Kojima015405a2022-04-28 17:09:41 +090037enum boot_type {
38 BOOTMENU_TYPE_NONE = 0,
39 BOOTMENU_TYPE_BOOTMENU,
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +090040 BOOTMENU_TYPE_UEFI_BOOT_OPTION,
Masahisa Kojima015405a2022-04-28 17:09:41 +090041};
42
Pali Rohárac91b472013-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 Kojima1baf9642022-05-29 10:52:43 +090046 char *title; /* title of entry */
Pali Rohárac91b472013-03-23 14:53:08 +000047 char *command; /* hush command of entry */
Masahisa Kojima015405a2022-04-28 17:09:41 +090048 enum boot_type type; /* boot type of entry */
49 u16 bootorder; /* order for each boot type */
Pali Rohárac91b472013-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árac91b472013-03-23 14:53:08 +000054static char *bootmenu_getoption(unsigned short int n)
55{
Lan Yixun (dlan)981c7e02013-06-27 18:58:53 +080056 char name[MAX_ENV_SIZE];
Pali Rohárac91b472013-03-23 14:53:08 +000057
58 if (n > MAX_COUNT)
59 return NULL;
60
Lan Yixun (dlan)981c7e02013-06-27 18:58:53 +080061 sprintf(name, "bootmenu_%d", n);
Simon Glass64b723f2017-08-03 12:22:12 -060062 return env_get(name);
Pali Rohárac91b472013-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 Schuchardtd7dc8772022-05-01 23:17:18 +020074 printf(ANSI_CURSOR_POSITION, entry->num + 4, 7);
Pali Rohárac91b472013-03-23 14:53:08 +000075
76 if (reverse)
77 puts(ANSI_COLOR_REVERSE);
78
Masahisa Kojima1baf9642022-05-29 10:52:43 +090079 printf("%s", entry->title);
Pali Rohárac91b472013-03-23 14:53:08 +000080
81 if (reverse)
82 puts(ANSI_COLOR_RESET);
83}
84
Pali Rohárac91b472013-03-23 14:53:08 +000085static char *bootmenu_choice_entry(void *data)
86{
Simon Glass9d8d3872023-01-06 08:52:26 -060087 struct cli_ch_state s_cch, *cch = &s_cch;
Pali Rohárac91b472013-03-23 14:53:08 +000088 struct bootmenu_data *menu = data;
89 struct bootmenu_entry *iter;
Simon Glass05ecdf82023-01-06 08:52:22 -060090 enum bootmenu_key key = BKEY_NONE;
Pali Rohárac91b472013-03-23 14:53:08 +000091 int i;
92
Simon Glass9d8d3872023-01-06 08:52:26 -060093 cli_ch_init(cch);
94
Pali Rohárac91b472013-03-23 14:53:08 +000095 while (1) {
96 if (menu->delay >= 0) {
97 /* Autoboot was not stopped */
Simon Glass9d8d3872023-01-06 08:52:26 -060098 key = bootmenu_autoboot_loop(menu, cch);
Pali Rohárac91b472013-03-23 14:53:08 +000099 } else {
100 /* Some key was pressed, so autoboot was stopped */
Simon Glass9d8d3872023-01-06 08:52:26 -0600101 key = bootmenu_loop(menu, cch);
Pali Rohárac91b472013-03-23 14:53:08 +0000102 }
103
104 switch (key) {
Simon Glass05ecdf82023-01-06 08:52:22 -0600105 case BKEY_UP:
developerc1c0d2c2024-10-29 17:47:22 +0800106 menu->last_active = menu->active;
Pali Rohárac91b472013-03-23 14:53:08 +0000107 if (menu->active > 0)
108 --menu->active;
109 /* no menu key selected, regenerate menu */
110 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -0600111 case BKEY_DOWN:
developerc1c0d2c2024-10-29 17:47:22 +0800112 menu->last_active = menu->active;
Pali Rohárac91b472013-03-23 14:53:08 +0000113 if (menu->active < menu->count - 1)
114 ++menu->active;
115 /* no menu key selected, regenerate menu */
116 return NULL;
Simon Glass05ecdf82023-01-06 08:52:22 -0600117 case BKEY_SELECT:
Pali Rohárac91b472013-03-23 14:53:08 +0000118 iter = menu->first;
119 for (i = 0; i < menu->active; ++i)
120 iter = iter->next;
121 return iter->key;
Simon Glass05ecdf82023-01-06 08:52:22 -0600122 case BKEY_QUIT:
Svyatoslav Ryhel08f67c42024-01-17 12:55:46 +0200123 /* Quit by choosing the last entry */
Pali Rohár3c0629b2020-12-27 01:04:38 +0100124 iter = menu->first;
125 while (iter->next)
126 iter = iter->next;
127 return iter->key;
Pali Rohárac91b472013-03-23 14:53:08 +0000128 default:
129 break;
130 }
131 }
132
133 /* never happens */
134 debug("bootmenu: this should not happen");
135 return NULL;
136}
137
developerc1c0d2c2024-10-29 17:47:22 +0800138static bool bootmenu_need_reprint(void *data)
139{
140 struct bootmenu_data *menu = data;
141 bool need_reprint;
142
143 need_reprint = menu->last_active != menu->active;
144 menu->last_active = menu->active;
145
146 return need_reprint;
147}
148
Pali Rohárac91b472013-03-23 14:53:08 +0000149static void bootmenu_destroy(struct bootmenu_data *menu)
150{
151 struct bootmenu_entry *iter = menu->first;
152 struct bootmenu_entry *next;
153
154 while (iter) {
155 next = iter->next;
156 free(iter->title);
157 free(iter->command);
158 free(iter);
159 iter = next;
160 }
161 free(menu);
162}
163
Masahisa Kojima015405a2022-04-28 17:09:41 +0900164/**
165 * prepare_bootmenu_entry() - generate the bootmenu_xx entries
166 *
167 * This function read the "bootmenu_x" U-Boot environment variable
168 * and generate the bootmenu entries.
169 *
170 * @menu: pointer to the bootmenu structure
171 * @current: pointer to the last bootmenu entry list
172 * @index: pointer to the index of the last bootmenu entry,
173 * the number of bootmenu entry is added by this function
174 * Return: 1 on success, negative value on error
175 */
176static int prepare_bootmenu_entry(struct bootmenu_data *menu,
177 struct bootmenu_entry **current,
178 unsigned short int *index)
Pali Rohárac91b472013-03-23 14:53:08 +0000179{
Pali Rohárac91b472013-03-23 14:53:08 +0000180 char *sep;
Masahisa Kojima015405a2022-04-28 17:09:41 +0900181 const char *option;
182 unsigned short int i = *index;
183 struct bootmenu_entry *entry = NULL;
184 struct bootmenu_entry *iter = *current;
Frank Wunderlich6a3578e2018-10-05 11:41:59 +0200185
Pali Rohárac91b472013-03-23 14:53:08 +0000186 while ((option = bootmenu_getoption(i))) {
Masahisa Kojima015405a2022-04-28 17:09:41 +0900187
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900188 /* bootmenu_[num] format is "[title]=[commands]" */
Pali Rohárac91b472013-03-23 14:53:08 +0000189 sep = strchr(option, '=');
190 if (!sep) {
191 printf("Invalid bootmenu entry: %s\n", option);
192 break;
193 }
194
195 entry = malloc(sizeof(struct bootmenu_entry));
196 if (!entry)
Masahisa Kojima015405a2022-04-28 17:09:41 +0900197 return -ENOMEM;
Pali Rohárac91b472013-03-23 14:53:08 +0000198
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900199 entry->title = strndup(option, sep - option);
Pali Rohárac91b472013-03-23 14:53:08 +0000200 if (!entry->title) {
201 free(entry);
Masahisa Kojima015405a2022-04-28 17:09:41 +0900202 return -ENOMEM;
Pali Rohárac91b472013-03-23 14:53:08 +0000203 }
Pali Rohárac91b472013-03-23 14:53:08 +0000204
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900205 entry->command = strdup(sep + 1);
Pali Rohárac91b472013-03-23 14:53:08 +0000206 if (!entry->command) {
207 free(entry->title);
208 free(entry);
Masahisa Kojima015405a2022-04-28 17:09:41 +0900209 return -ENOMEM;
Pali Rohárac91b472013-03-23 14:53:08 +0000210 }
Pali Rohárac91b472013-03-23 14:53:08 +0000211
212 sprintf(entry->key, "%d", i);
213
214 entry->num = i;
215 entry->menu = menu;
Masahisa Kojima015405a2022-04-28 17:09:41 +0900216 entry->type = BOOTMENU_TYPE_BOOTMENU;
217 entry->bootorder = i;
Pali Rohárac91b472013-03-23 14:53:08 +0000218 entry->next = NULL;
219
220 if (!iter)
221 menu->first = entry;
222 else
223 iter->next = entry;
224
225 iter = entry;
226 ++i;
227
228 if (i == MAX_COUNT - 1)
229 break;
230 }
231
Masahisa Kojima015405a2022-04-28 17:09:41 +0900232 *index = i;
233 *current = iter;
234
235 return 1;
236}
237
Simon Glass315367a2023-02-05 15:36:28 -0700238#if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) && (IS_ENABLED(CONFIG_CMD_EFICONFIG))
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900239/**
240 * prepare_uefi_bootorder_entry() - generate the uefi bootmenu entries
241 *
Heinrich Schuchardt2013f902024-11-23 10:04:03 +0100242 * This function reads the "BootOrder" UEFI variable
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900243 * and generate the bootmenu entries in the order of "BootOrder".
244 *
245 * @menu: pointer to the bootmenu structure
246 * @current: pointer to the last bootmenu entry list
247 * @index: pointer to the index of the last bootmenu entry,
248 * the number of uefi entry is added by this function
249 * Return: 1 on success, negative value on error
250 */
251static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
252 struct bootmenu_entry **current,
253 unsigned short int *index)
254{
255 u16 *bootorder;
256 efi_status_t ret;
257 unsigned short j;
258 efi_uintn_t num, size;
259 void *load_option;
260 struct efi_load_option lo;
261 u16 varname[] = u"Boot####";
262 unsigned short int i = *index;
263 struct bootmenu_entry *entry = NULL;
264 struct bootmenu_entry *iter = *current;
265
266 bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
267 if (!bootorder)
268 return -ENOENT;
269
270 num = size / sizeof(u16);
271 for (j = 0; j < num; j++) {
272 entry = malloc(sizeof(struct bootmenu_entry));
273 if (!entry)
274 return -ENOMEM;
275
276 efi_create_indexed_name(varname, sizeof(varname),
277 "Boot", bootorder[j]);
278 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
279 if (!load_option)
280 continue;
281
282 ret = efi_deserialize_load_option(&lo, load_option, &size);
283 if (ret != EFI_SUCCESS) {
284 log_warning("Invalid load option for %ls\n", varname);
285 free(load_option);
286 free(entry);
287 continue;
288 }
289
290 if (lo.attributes & LOAD_OPTION_ACTIVE) {
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900291 char *buf;
292
293 buf = calloc(1, utf16_utf8_strlen(lo.label) + 1);
294 if (!buf) {
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900295 free(load_option);
296 free(entry);
297 free(bootorder);
298 return -ENOMEM;
299 }
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900300 entry->title = buf;
301 utf16_utf8_strncpy(&buf, lo.label, u16_strlen(lo.label));
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900302 entry->command = strdup("bootefi bootmgr");
303 sprintf(entry->key, "%d", i);
304 entry->num = i;
305 entry->menu = menu;
306 entry->type = BOOTMENU_TYPE_UEFI_BOOT_OPTION;
307 entry->bootorder = bootorder[j];
308 entry->next = NULL;
309
310 if (!iter)
311 menu->first = entry;
312 else
313 iter->next = entry;
314
315 iter = entry;
316 i++;
317 }
318
319 free(load_option);
320
321 if (i == MAX_COUNT - 1)
322 break;
323 }
324
325 free(bootorder);
326 *index = i;
327 *current = iter;
328
329 return 1;
330}
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900331#endif
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900332
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100333/**
334 * bootmenu_create() - create boot menu entries
335 *
336 * @uefi: consider UEFI boot options
337 * @delay: autostart delay in seconds
338 */
339static struct bootmenu_data *bootmenu_create(int uefi, int delay)
Masahisa Kojima015405a2022-04-28 17:09:41 +0900340{
341 int ret;
342 unsigned short int i = 0;
343 struct bootmenu_data *menu;
344 struct bootmenu_entry *iter = NULL;
345 struct bootmenu_entry *entry;
346 char *default_str;
347
348 menu = malloc(sizeof(struct bootmenu_data));
349 if (!menu)
350 return NULL;
351
352 menu->delay = delay;
353 menu->active = 0;
developerc1c0d2c2024-10-29 17:47:22 +0800354 menu->last_active = -1;
Masahisa Kojima015405a2022-04-28 17:09:41 +0900355 menu->first = NULL;
356
357 default_str = env_get("bootmenu_default");
358 if (default_str)
359 menu->active = (int)simple_strtol(default_str, NULL, 10);
360
361 ret = prepare_bootmenu_entry(menu, &iter, &i);
362 if (ret < 0)
363 goto cleanup;
364
Simon Glass315367a2023-02-05 15:36:28 -0700365#if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) && (IS_ENABLED(CONFIG_CMD_EFICONFIG))
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100366 if (uefi && i < MAX_COUNT - 1) {
Masahisa Kojima767a9e62022-09-12 17:33:54 +0900367 efi_status_t efi_ret;
368
369 /*
370 * UEFI specification requires booting from removal media using
371 * a architecture-specific default image name such as BOOTAA64.EFI.
372 */
Raymond Mao70a76c52023-06-19 14:22:58 -0700373 efi_ret = efi_bootmgr_update_media_device_boot_option();
Raymond Maoa35784d2023-06-19 14:22:59 -0700374 if (efi_ret != EFI_SUCCESS)
Masahisa Kojima767a9e62022-09-12 17:33:54 +0900375 goto cleanup;
376
377 ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
378 if (ret < 0 && ret != -ENOENT)
379 goto cleanup;
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900380 }
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900381#endif
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900382
Svyatoslav Ryhel08f67c42024-01-17 12:55:46 +0200383 /* Add Exit entry at the end */
Pali Rohárac91b472013-03-23 14:53:08 +0000384 if (i <= MAX_COUNT - 1) {
385 entry = malloc(sizeof(struct bootmenu_entry));
386 if (!entry)
387 goto cleanup;
388
Svyatoslav Ryhel08f67c42024-01-17 12:55:46 +0200389 /* Add Quit entry if exiting bootmenu is disabled */
Masahisa Kojima97cbcc42022-05-26 19:09:38 +0900390 if (!IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE))
Svyatoslav Ryhel08f67c42024-01-17 12:55:46 +0200391 entry->title = strdup("Exit");
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900392 else
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900393 entry->title = strdup("Quit");
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900394
Pali Rohárac91b472013-03-23 14:53:08 +0000395 if (!entry->title) {
396 free(entry);
397 goto cleanup;
398 }
399
400 entry->command = strdup("");
401 if (!entry->command) {
402 free(entry->title);
403 free(entry);
404 goto cleanup;
405 }
406
407 sprintf(entry->key, "%d", i);
408
409 entry->num = i;
410 entry->menu = menu;
Masahisa Kojima015405a2022-04-28 17:09:41 +0900411 entry->type = BOOTMENU_TYPE_NONE;
Pali Rohárac91b472013-03-23 14:53:08 +0000412 entry->next = NULL;
413
414 if (!iter)
415 menu->first = entry;
416 else
417 iter->next = entry;
418
419 iter = entry;
420 ++i;
421 }
422
423 menu->count = i;
Frank Wunderlich4931d962018-12-03 11:23:41 +0100424
425 if ((menu->active >= menu->count)||(menu->active < 0)) { //ensure active menuitem is inside menu
426 printf("active menuitem (%d) is outside menu (0..%d)\n",menu->active,menu->count-1);
427 menu->active=0;
428 }
429
Pali Rohárac91b472013-03-23 14:53:08 +0000430 return menu;
431
432cleanup:
433 bootmenu_destroy(menu);
434 return NULL;
435}
436
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700437static void menu_display_statusline(struct menu *m)
438{
439 struct bootmenu_entry *entry;
440 struct bootmenu_data *menu;
441
442 if (menu_default_choice(m, (void *)&entry) < 0)
443 return;
444
445 menu = entry->menu;
446
447 printf(ANSI_CURSOR_POSITION, 1, 1);
448 puts(ANSI_CLEAR_LINE);
Heinrich Schuchardtd7dc8772022-05-01 23:17:18 +0200449 printf(ANSI_CURSOR_POSITION, 2, 3);
450 puts("*** U-Boot Boot Menu ***");
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700451 puts(ANSI_CLEAR_LINE_TO_END);
452 printf(ANSI_CURSOR_POSITION, 3, 1);
453 puts(ANSI_CLEAR_LINE);
454
455 /* First 3 lines are bootmenu header + 2 empty lines between entries */
456 printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
457 puts(ANSI_CLEAR_LINE);
Heinrich Schuchardtd7dc8772022-05-01 23:17:18 +0200458 printf(ANSI_CURSOR_POSITION, menu->count + 6, 3);
Masahisa Kojima3a9eb072023-02-02 18:24:43 +0900459 puts("Press UP/DOWN to move, ENTER to select, ESC to quit");
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700460 puts(ANSI_CLEAR_LINE_TO_END);
461 printf(ANSI_CURSOR_POSITION, menu->count + 7, 1);
462 puts(ANSI_CLEAR_LINE);
463}
464
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900465static void handle_uefi_bootnext(void)
466{
467 u16 bootnext;
468 efi_status_t ret;
469 efi_uintn_t size;
470
471 /* Initialize EFI drivers */
472 ret = efi_init_obj_list();
473 if (ret != EFI_SUCCESS) {
474 log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
475 ret & ~EFI_ERROR_MASK);
476
477 return;
478 }
479
480 /* If UEFI BootNext variable is set, boot the BootNext load option */
481 size = sizeof(u16);
482 ret = efi_get_variable_int(u"BootNext",
483 &efi_global_variable_guid,
484 NULL, &size, &bootnext, NULL);
485 if (ret == EFI_SUCCESS)
486 /* BootNext does exist here, try to boot */
487 run_command("bootefi bootmgr", 0);
488}
489
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100490/**
491 * bootmenu_show - display boot menu
492 *
493 * @uefi: generated entries for UEFI boot options
494 * @delay: autoboot delay in seconds
495 */
496static enum bootmenu_ret bootmenu_show(int uefi, int delay)
Pali Rohárac91b472013-03-23 14:53:08 +0000497{
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900498 int cmd_ret;
Pali Rohárac91b472013-03-23 14:53:08 +0000499 int init = 0;
500 void *choice = NULL;
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900501 char *title = NULL;
Pali Rohárac91b472013-03-23 14:53:08 +0000502 char *command = NULL;
503 struct menu *menu;
Pali Rohárac91b472013-03-23 14:53:08 +0000504 struct bootmenu_entry *iter;
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900505 int ret = BOOTMENU_RET_SUCCESS;
506 struct bootmenu_data *bootmenu;
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900507 efi_status_t efi_ret = EFI_SUCCESS;
Pali Rohárac91b472013-03-23 14:53:08 +0000508 char *option, *sep;
509
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100510 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && uefi)
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900511 handle_uefi_bootnext();
512
Pali Rohárac91b472013-03-23 14:53:08 +0000513 /* If delay is 0 do not create menu, just run first entry */
514 if (delay == 0) {
515 option = bootmenu_getoption(0);
516 if (!option) {
517 puts("bootmenu option 0 was not found\n");
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900518 return BOOTMENU_RET_FAIL;
Pali Rohárac91b472013-03-23 14:53:08 +0000519 }
520 sep = strchr(option, '=');
521 if (!sep) {
522 puts("bootmenu option 0 is invalid\n");
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900523 return BOOTMENU_RET_FAIL;
Pali Rohárac91b472013-03-23 14:53:08 +0000524 }
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900525 cmd_ret = run_command(sep + 1, 0);
526 return (cmd_ret == CMD_RET_SUCCESS ? BOOTMENU_RET_SUCCESS : BOOTMENU_RET_FAIL);
Pali Rohárac91b472013-03-23 14:53:08 +0000527 }
528
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100529 bootmenu = bootmenu_create(uefi, delay);
Pali Rohárac91b472013-03-23 14:53:08 +0000530 if (!bootmenu)
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900531 return BOOTMENU_RET_FAIL;
Pali Rohárac91b472013-03-23 14:53:08 +0000532
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700533 menu = menu_create(NULL, bootmenu->delay, 1, menu_display_statusline,
534 bootmenu_print_entry, bootmenu_choice_entry,
developerc1c0d2c2024-10-29 17:47:22 +0800535 bootmenu_need_reprint, bootmenu);
Pali Rohárac91b472013-03-23 14:53:08 +0000536 if (!menu) {
537 bootmenu_destroy(bootmenu);
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900538 return BOOTMENU_RET_FAIL;
Pali Rohárac91b472013-03-23 14:53:08 +0000539 }
540
541 for (iter = bootmenu->first; iter; iter = iter->next) {
Masahisa Kojimaacc969e2022-03-24 22:54:33 +0900542 if (menu_item_add(menu, iter->key, iter) != 1)
Pali Rohárac91b472013-03-23 14:53:08 +0000543 goto cleanup;
544 }
545
546 /* Default menu entry is always first */
547 menu_default_set(menu, "0");
548
549 puts(ANSI_CURSOR_HIDE);
550 puts(ANSI_CLEAR_CONSOLE);
551 printf(ANSI_CURSOR_POSITION, 1, 1);
552
553 init = 1;
554
Masahisa Kojimaacc969e2022-03-24 22:54:33 +0900555 if (menu_get_choice(menu, &choice) == 1) {
Pali Rohárac91b472013-03-23 14:53:08 +0000556 iter = choice;
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900557 title = strdup(iter->title);
Pali Rohárac91b472013-03-23 14:53:08 +0000558 command = strdup(iter->command);
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900559
Svyatoslav Ryhel08f67c42024-01-17 12:55:46 +0200560 /* last entry exits bootmenu */
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900561 if (iter->num == iter->menu->count - 1) {
562 ret = BOOTMENU_RET_QUIT;
563 goto cleanup;
564 }
565 } else {
566 goto cleanup;
Pali Rohárac91b472013-03-23 14:53:08 +0000567 }
568
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900569 /*
570 * If the selected entry is UEFI BOOT####, set the BootNext variable.
571 * Then uefi bootmgr is invoked by the preset command in iter->command.
572 */
573 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
574 if (iter->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION) {
575 /*
576 * UEFI specification requires BootNext variable needs non-volatile
577 * attribute, but this BootNext is only used inside of U-Boot and
578 * removed by efi bootmgr once BootNext is processed.
579 * So this BootNext can be volatile.
580 */
581 efi_ret = efi_set_variable_int(u"BootNext", &efi_global_variable_guid,
582 EFI_VARIABLE_BOOTSERVICE_ACCESS |
583 EFI_VARIABLE_RUNTIME_ACCESS,
584 sizeof(u16), &iter->bootorder, false);
585 if (efi_ret != EFI_SUCCESS)
586 goto cleanup;
587 }
588 }
589
Pali Rohárac91b472013-03-23 14:53:08 +0000590cleanup:
591 menu_destroy(menu);
592 bootmenu_destroy(bootmenu);
593
594 if (init) {
595 puts(ANSI_CURSOR_SHOW);
596 puts(ANSI_CLEAR_CONSOLE);
597 printf(ANSI_CURSOR_POSITION, 1, 1);
598 }
599
600 if (title && command) {
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900601 debug("Starting entry '%s'\n", title);
Pali Rohárac91b472013-03-23 14:53:08 +0000602 free(title);
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900603 if (efi_ret == EFI_SUCCESS)
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900604 cmd_ret = run_command(command, 0);
Pali Rohárac91b472013-03-23 14:53:08 +0000605 free(command);
606 }
607
Tom Rinie510a3f2022-12-04 10:13:33 -0500608#ifdef CFG_POSTBOOTMENU
609 run_command(CFG_POSTBOOTMENU, 0);
Pali Rohárac91b472013-03-23 14:53:08 +0000610#endif
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900611
612 if (efi_ret != EFI_SUCCESS || cmd_ret != CMD_RET_SUCCESS)
613 ret = BOOTMENU_RET_FAIL;
614
615 return ret;
Pali Rohárac91b472013-03-23 14:53:08 +0000616}
617
Simon Glassec5f71a2019-07-20 20:51:24 -0600618#ifdef CONFIG_AUTOBOOT_MENU_SHOW
Pali Rohárac91b472013-03-23 14:53:08 +0000619int menu_show(int bootdelay)
620{
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900621 int ret;
622
623 while (1) {
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100624 ret = bootmenu_show(1, bootdelay);
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900625 bootdelay = -1;
626 if (ret == BOOTMENU_RET_UPDATED)
627 continue;
628
Masahisa Kojima97cbcc42022-05-26 19:09:38 +0900629 if (IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE)) {
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900630 if (ret == BOOTMENU_RET_QUIT) {
631 /* default boot process */
632 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR))
633 run_command("bootefi bootmgr", 0);
634
635 run_command("run bootcmd", 0);
636 }
637 } else {
638 break;
639 }
640 }
641
Pali Rohárac91b472013-03-23 14:53:08 +0000642 return -1; /* -1 - abort boot and run monitor code */
643}
644#endif
645
Simon Glassed38aef2020-05-10 11:40:03 -0600646int do_bootmenu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Pali Rohárac91b472013-03-23 14:53:08 +0000647{
648 char *delay_str = NULL;
649 int delay = 10;
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100650 int uefi = 0;
Pali Rohárac91b472013-03-23 14:53:08 +0000651
652#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
653 delay = CONFIG_BOOTDELAY;
654#endif
655
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100656 if (argc >= 2) {
657 if (!strcmp("-e", argv[1])) {
658 uefi = 1;
659 --argc;
660 ++argv;
661 }
662 }
Pali Rohárac91b472013-03-23 14:53:08 +0000663 if (argc >= 2)
664 delay_str = argv[1];
665
666 if (!delay_str)
Simon Glass64b723f2017-08-03 12:22:12 -0600667 delay_str = env_get("bootmenu_delay");
Pali Rohárac91b472013-03-23 14:53:08 +0000668
669 if (delay_str)
670 delay = (int)simple_strtol(delay_str, NULL, 10);
671
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100672 bootmenu_show(uefi, delay);
Pali Rohárac91b472013-03-23 14:53:08 +0000673 return 0;
674}
675
676U_BOOT_CMD(
677 bootmenu, 2, 1, do_bootmenu,
678 "ANSI terminal bootmenu",
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100679 "[-e] [delay]\n"
680 "-e - show UEFI entries\n"
681 "delay - show ANSI terminal bootmenu with autoboot delay"
Pali Rohárac91b472013-03-23 14:53:08 +0000682);