blob: ffa63a4628da102c5579bc93bea8870a57d639bf [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 *
242 * This function read the "BootOrder" UEFI variable
243 * 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
Masahisa Kojima015405a2022-04-28 17:09:41 +0900333static struct bootmenu_data *bootmenu_create(int delay)
334{
335 int ret;
336 unsigned short int i = 0;
337 struct bootmenu_data *menu;
338 struct bootmenu_entry *iter = NULL;
339 struct bootmenu_entry *entry;
340 char *default_str;
341
342 menu = malloc(sizeof(struct bootmenu_data));
343 if (!menu)
344 return NULL;
345
346 menu->delay = delay;
347 menu->active = 0;
developerc1c0d2c2024-10-29 17:47:22 +0800348 menu->last_active = -1;
Masahisa Kojima015405a2022-04-28 17:09:41 +0900349 menu->first = NULL;
350
351 default_str = env_get("bootmenu_default");
352 if (default_str)
353 menu->active = (int)simple_strtol(default_str, NULL, 10);
354
355 ret = prepare_bootmenu_entry(menu, &iter, &i);
356 if (ret < 0)
357 goto cleanup;
358
Simon Glass315367a2023-02-05 15:36:28 -0700359#if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) && (IS_ENABLED(CONFIG_CMD_EFICONFIG))
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900360 if (i < MAX_COUNT - 1) {
Masahisa Kojima767a9e62022-09-12 17:33:54 +0900361 efi_status_t efi_ret;
362
363 /*
364 * UEFI specification requires booting from removal media using
365 * a architecture-specific default image name such as BOOTAA64.EFI.
366 */
Raymond Mao70a76c52023-06-19 14:22:58 -0700367 efi_ret = efi_bootmgr_update_media_device_boot_option();
Raymond Maoa35784d2023-06-19 14:22:59 -0700368 if (efi_ret != EFI_SUCCESS)
Masahisa Kojima767a9e62022-09-12 17:33:54 +0900369 goto cleanup;
370
371 ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
372 if (ret < 0 && ret != -ENOENT)
373 goto cleanup;
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900374 }
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900375#endif
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900376
Svyatoslav Ryhel08f67c42024-01-17 12:55:46 +0200377 /* Add Exit entry at the end */
Pali Rohárac91b472013-03-23 14:53:08 +0000378 if (i <= MAX_COUNT - 1) {
379 entry = malloc(sizeof(struct bootmenu_entry));
380 if (!entry)
381 goto cleanup;
382
Svyatoslav Ryhel08f67c42024-01-17 12:55:46 +0200383 /* Add Quit entry if exiting bootmenu is disabled */
Masahisa Kojima97cbcc42022-05-26 19:09:38 +0900384 if (!IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE))
Svyatoslav Ryhel08f67c42024-01-17 12:55:46 +0200385 entry->title = strdup("Exit");
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900386 else
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900387 entry->title = strdup("Quit");
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900388
Pali Rohárac91b472013-03-23 14:53:08 +0000389 if (!entry->title) {
390 free(entry);
391 goto cleanup;
392 }
393
394 entry->command = strdup("");
395 if (!entry->command) {
396 free(entry->title);
397 free(entry);
398 goto cleanup;
399 }
400
401 sprintf(entry->key, "%d", i);
402
403 entry->num = i;
404 entry->menu = menu;
Masahisa Kojima015405a2022-04-28 17:09:41 +0900405 entry->type = BOOTMENU_TYPE_NONE;
Pali Rohárac91b472013-03-23 14:53:08 +0000406 entry->next = NULL;
407
408 if (!iter)
409 menu->first = entry;
410 else
411 iter->next = entry;
412
413 iter = entry;
414 ++i;
415 }
416
417 menu->count = i;
Frank Wunderlich4931d962018-12-03 11:23:41 +0100418
419 if ((menu->active >= menu->count)||(menu->active < 0)) { //ensure active menuitem is inside menu
420 printf("active menuitem (%d) is outside menu (0..%d)\n",menu->active,menu->count-1);
421 menu->active=0;
422 }
423
Pali Rohárac91b472013-03-23 14:53:08 +0000424 return menu;
425
426cleanup:
427 bootmenu_destroy(menu);
428 return NULL;
429}
430
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700431static void menu_display_statusline(struct menu *m)
432{
433 struct bootmenu_entry *entry;
434 struct bootmenu_data *menu;
435
436 if (menu_default_choice(m, (void *)&entry) < 0)
437 return;
438
439 menu = entry->menu;
440
441 printf(ANSI_CURSOR_POSITION, 1, 1);
442 puts(ANSI_CLEAR_LINE);
Heinrich Schuchardtd7dc8772022-05-01 23:17:18 +0200443 printf(ANSI_CURSOR_POSITION, 2, 3);
444 puts("*** U-Boot Boot Menu ***");
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700445 puts(ANSI_CLEAR_LINE_TO_END);
446 printf(ANSI_CURSOR_POSITION, 3, 1);
447 puts(ANSI_CLEAR_LINE);
448
449 /* First 3 lines are bootmenu header + 2 empty lines between entries */
450 printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
451 puts(ANSI_CLEAR_LINE);
Heinrich Schuchardtd7dc8772022-05-01 23:17:18 +0200452 printf(ANSI_CURSOR_POSITION, menu->count + 6, 3);
Masahisa Kojima3a9eb072023-02-02 18:24:43 +0900453 puts("Press UP/DOWN to move, ENTER to select, ESC to quit");
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700454 puts(ANSI_CLEAR_LINE_TO_END);
455 printf(ANSI_CURSOR_POSITION, menu->count + 7, 1);
456 puts(ANSI_CLEAR_LINE);
457}
458
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900459static void handle_uefi_bootnext(void)
460{
461 u16 bootnext;
462 efi_status_t ret;
463 efi_uintn_t size;
464
465 /* Initialize EFI drivers */
466 ret = efi_init_obj_list();
467 if (ret != EFI_SUCCESS) {
468 log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
469 ret & ~EFI_ERROR_MASK);
470
471 return;
472 }
473
474 /* If UEFI BootNext variable is set, boot the BootNext load option */
475 size = sizeof(u16);
476 ret = efi_get_variable_int(u"BootNext",
477 &efi_global_variable_guid,
478 NULL, &size, &bootnext, NULL);
479 if (ret == EFI_SUCCESS)
480 /* BootNext does exist here, try to boot */
481 run_command("bootefi bootmgr", 0);
482}
483
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900484static enum bootmenu_ret bootmenu_show(int delay)
Pali Rohárac91b472013-03-23 14:53:08 +0000485{
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900486 int cmd_ret;
Pali Rohárac91b472013-03-23 14:53:08 +0000487 int init = 0;
488 void *choice = NULL;
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900489 char *title = NULL;
Pali Rohárac91b472013-03-23 14:53:08 +0000490 char *command = NULL;
491 struct menu *menu;
Pali Rohárac91b472013-03-23 14:53:08 +0000492 struct bootmenu_entry *iter;
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900493 int ret = BOOTMENU_RET_SUCCESS;
494 struct bootmenu_data *bootmenu;
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900495 efi_status_t efi_ret = EFI_SUCCESS;
Pali Rohárac91b472013-03-23 14:53:08 +0000496 char *option, *sep;
497
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900498 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR))
499 handle_uefi_bootnext();
500
Pali Rohárac91b472013-03-23 14:53:08 +0000501 /* If delay is 0 do not create menu, just run first entry */
502 if (delay == 0) {
503 option = bootmenu_getoption(0);
504 if (!option) {
505 puts("bootmenu option 0 was not found\n");
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900506 return BOOTMENU_RET_FAIL;
Pali Rohárac91b472013-03-23 14:53:08 +0000507 }
508 sep = strchr(option, '=');
509 if (!sep) {
510 puts("bootmenu option 0 is invalid\n");
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900511 return BOOTMENU_RET_FAIL;
Pali Rohárac91b472013-03-23 14:53:08 +0000512 }
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900513 cmd_ret = run_command(sep + 1, 0);
514 return (cmd_ret == CMD_RET_SUCCESS ? BOOTMENU_RET_SUCCESS : BOOTMENU_RET_FAIL);
Pali Rohárac91b472013-03-23 14:53:08 +0000515 }
516
517 bootmenu = bootmenu_create(delay);
518 if (!bootmenu)
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900519 return BOOTMENU_RET_FAIL;
Pali Rohárac91b472013-03-23 14:53:08 +0000520
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700521 menu = menu_create(NULL, bootmenu->delay, 1, menu_display_statusline,
522 bootmenu_print_entry, bootmenu_choice_entry,
developerc1c0d2c2024-10-29 17:47:22 +0800523 bootmenu_need_reprint, bootmenu);
Pali Rohárac91b472013-03-23 14:53:08 +0000524 if (!menu) {
525 bootmenu_destroy(bootmenu);
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900526 return BOOTMENU_RET_FAIL;
Pali Rohárac91b472013-03-23 14:53:08 +0000527 }
528
529 for (iter = bootmenu->first; iter; iter = iter->next) {
Masahisa Kojimaacc969e2022-03-24 22:54:33 +0900530 if (menu_item_add(menu, iter->key, iter) != 1)
Pali Rohárac91b472013-03-23 14:53:08 +0000531 goto cleanup;
532 }
533
534 /* Default menu entry is always first */
535 menu_default_set(menu, "0");
536
537 puts(ANSI_CURSOR_HIDE);
538 puts(ANSI_CLEAR_CONSOLE);
539 printf(ANSI_CURSOR_POSITION, 1, 1);
540
541 init = 1;
542
Masahisa Kojimaacc969e2022-03-24 22:54:33 +0900543 if (menu_get_choice(menu, &choice) == 1) {
Pali Rohárac91b472013-03-23 14:53:08 +0000544 iter = choice;
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900545 title = strdup(iter->title);
Pali Rohárac91b472013-03-23 14:53:08 +0000546 command = strdup(iter->command);
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900547
Svyatoslav Ryhel08f67c42024-01-17 12:55:46 +0200548 /* last entry exits bootmenu */
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900549 if (iter->num == iter->menu->count - 1) {
550 ret = BOOTMENU_RET_QUIT;
551 goto cleanup;
552 }
553 } else {
554 goto cleanup;
Pali Rohárac91b472013-03-23 14:53:08 +0000555 }
556
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900557 /*
558 * If the selected entry is UEFI BOOT####, set the BootNext variable.
559 * Then uefi bootmgr is invoked by the preset command in iter->command.
560 */
561 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
562 if (iter->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION) {
563 /*
564 * UEFI specification requires BootNext variable needs non-volatile
565 * attribute, but this BootNext is only used inside of U-Boot and
566 * removed by efi bootmgr once BootNext is processed.
567 * So this BootNext can be volatile.
568 */
569 efi_ret = efi_set_variable_int(u"BootNext", &efi_global_variable_guid,
570 EFI_VARIABLE_BOOTSERVICE_ACCESS |
571 EFI_VARIABLE_RUNTIME_ACCESS,
572 sizeof(u16), &iter->bootorder, false);
573 if (efi_ret != EFI_SUCCESS)
574 goto cleanup;
575 }
576 }
577
Pali Rohárac91b472013-03-23 14:53:08 +0000578cleanup:
579 menu_destroy(menu);
580 bootmenu_destroy(bootmenu);
581
582 if (init) {
583 puts(ANSI_CURSOR_SHOW);
584 puts(ANSI_CLEAR_CONSOLE);
585 printf(ANSI_CURSOR_POSITION, 1, 1);
586 }
587
588 if (title && command) {
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900589 debug("Starting entry '%s'\n", title);
Pali Rohárac91b472013-03-23 14:53:08 +0000590 free(title);
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900591 if (efi_ret == EFI_SUCCESS)
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900592 cmd_ret = run_command(command, 0);
Pali Rohárac91b472013-03-23 14:53:08 +0000593 free(command);
594 }
595
Tom Rinie510a3f2022-12-04 10:13:33 -0500596#ifdef CFG_POSTBOOTMENU
597 run_command(CFG_POSTBOOTMENU, 0);
Pali Rohárac91b472013-03-23 14:53:08 +0000598#endif
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900599
600 if (efi_ret != EFI_SUCCESS || cmd_ret != CMD_RET_SUCCESS)
601 ret = BOOTMENU_RET_FAIL;
602
603 return ret;
Pali Rohárac91b472013-03-23 14:53:08 +0000604}
605
Simon Glassec5f71a2019-07-20 20:51:24 -0600606#ifdef CONFIG_AUTOBOOT_MENU_SHOW
Pali Rohárac91b472013-03-23 14:53:08 +0000607int menu_show(int bootdelay)
608{
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900609 int ret;
610
611 while (1) {
612 ret = bootmenu_show(bootdelay);
613 bootdelay = -1;
614 if (ret == BOOTMENU_RET_UPDATED)
615 continue;
616
Masahisa Kojima97cbcc42022-05-26 19:09:38 +0900617 if (IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE)) {
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900618 if (ret == BOOTMENU_RET_QUIT) {
619 /* default boot process */
620 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR))
621 run_command("bootefi bootmgr", 0);
622
623 run_command("run bootcmd", 0);
624 }
625 } else {
626 break;
627 }
628 }
629
Pali Rohárac91b472013-03-23 14:53:08 +0000630 return -1; /* -1 - abort boot and run monitor code */
631}
632#endif
633
Simon Glassed38aef2020-05-10 11:40:03 -0600634int do_bootmenu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Pali Rohárac91b472013-03-23 14:53:08 +0000635{
636 char *delay_str = NULL;
637 int delay = 10;
638
639#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
640 delay = CONFIG_BOOTDELAY;
641#endif
642
643 if (argc >= 2)
644 delay_str = argv[1];
645
646 if (!delay_str)
Simon Glass64b723f2017-08-03 12:22:12 -0600647 delay_str = env_get("bootmenu_delay");
Pali Rohárac91b472013-03-23 14:53:08 +0000648
649 if (delay_str)
650 delay = (int)simple_strtol(delay_str, NULL, 10);
651
652 bootmenu_show(delay);
653 return 0;
654}
655
656U_BOOT_CMD(
657 bootmenu, 2, 1, do_bootmenu,
658 "ANSI terminal bootmenu",
659 "[delay]\n"
660 " - show ANSI terminal bootmenu with autoboot delay"
661);