blob: d3108778c6f3dfaf85de320a1bf63d7dc6128dc4 [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;
Christian Marangib357fc62025-05-25 15:43:58 +0200117 case BKEY_SHORTCUT:
118 /* invalid shortcut, regenerate menu */
119 if (cch->shortcut_key >= menu->count - 1)
120 return NULL;
121 /* shortcut_key value for Exit is is -1 */
122 menu->active = cch->shortcut_key < 0 ? menu->count - 1 :
123 cch->shortcut_key;
124 fallthrough;
Simon Glass05ecdf82023-01-06 08:52:22 -0600125 case BKEY_SELECT:
Pali Rohárac91b472013-03-23 14:53:08 +0000126 iter = menu->first;
127 for (i = 0; i < menu->active; ++i)
128 iter = iter->next;
129 return iter->key;
Simon Glass05ecdf82023-01-06 08:52:22 -0600130 case BKEY_QUIT:
Svyatoslav Ryhel08f67c42024-01-17 12:55:46 +0200131 /* Quit by choosing the last entry */
Pali Rohár3c0629b2020-12-27 01:04:38 +0100132 iter = menu->first;
133 while (iter->next)
134 iter = iter->next;
135 return iter->key;
Pali Rohárac91b472013-03-23 14:53:08 +0000136 default:
137 break;
138 }
139 }
140
141 /* never happens */
142 debug("bootmenu: this should not happen");
143 return NULL;
144}
145
developerc1c0d2c2024-10-29 17:47:22 +0800146static bool bootmenu_need_reprint(void *data)
147{
148 struct bootmenu_data *menu = data;
149 bool need_reprint;
150
151 need_reprint = menu->last_active != menu->active;
152 menu->last_active = menu->active;
153
154 return need_reprint;
155}
156
Pali Rohárac91b472013-03-23 14:53:08 +0000157static void bootmenu_destroy(struct bootmenu_data *menu)
158{
159 struct bootmenu_entry *iter = menu->first;
160 struct bootmenu_entry *next;
161
162 while (iter) {
163 next = iter->next;
164 free(iter->title);
165 free(iter->command);
166 free(iter);
167 iter = next;
168 }
169 free(menu);
170}
171
Christian Marangib357fc62025-05-25 15:43:58 +0200172static char bootmenu_entry_shortcut_key(int index)
173{
174 switch (index) {
175 /* 1-9 shortcut key (0 reserved) */
176 case 0 ... 8:
177 return '1' + index;
178 /* a-z shortcut key */
179 case 9 ... 34:
180 return 'a' + index - 9;
181 /* We support shortcut for up to 34 options (0 reserved) */
182 default:
183 return -ENOENT;
184 }
185}
186
Masahisa Kojima015405a2022-04-28 17:09:41 +0900187/**
188 * prepare_bootmenu_entry() - generate the bootmenu_xx entries
189 *
190 * This function read the "bootmenu_x" U-Boot environment variable
191 * and generate the bootmenu entries.
192 *
193 * @menu: pointer to the bootmenu structure
194 * @current: pointer to the last bootmenu entry list
195 * @index: pointer to the index of the last bootmenu entry,
196 * the number of bootmenu entry is added by this function
197 * Return: 1 on success, negative value on error
198 */
199static int prepare_bootmenu_entry(struct bootmenu_data *menu,
200 struct bootmenu_entry **current,
201 unsigned short int *index)
Pali Rohárac91b472013-03-23 14:53:08 +0000202{
Pali Rohárac91b472013-03-23 14:53:08 +0000203 char *sep;
Masahisa Kojima015405a2022-04-28 17:09:41 +0900204 const char *option;
205 unsigned short int i = *index;
206 struct bootmenu_entry *entry = NULL;
207 struct bootmenu_entry *iter = *current;
Frank Wunderlich6a3578e2018-10-05 11:41:59 +0200208
Pali Rohárac91b472013-03-23 14:53:08 +0000209 while ((option = bootmenu_getoption(i))) {
Christian Marangib357fc62025-05-25 15:43:58 +0200210 char shortcut_key;
211 int len;
Masahisa Kojima015405a2022-04-28 17:09:41 +0900212
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900213 /* bootmenu_[num] format is "[title]=[commands]" */
Pali Rohárac91b472013-03-23 14:53:08 +0000214 sep = strchr(option, '=');
215 if (!sep) {
216 printf("Invalid bootmenu entry: %s\n", option);
217 break;
218 }
219
220 entry = malloc(sizeof(struct bootmenu_entry));
221 if (!entry)
Masahisa Kojima015405a2022-04-28 17:09:41 +0900222 return -ENOMEM;
Pali Rohárac91b472013-03-23 14:53:08 +0000223
Christian Marangib357fc62025-05-25 15:43:58 +0200224 /* Add shotcut key option: %c. %s\0 */
225 len = sep - option + 4;
226
227 entry->title = malloc(len);
Pali Rohárac91b472013-03-23 14:53:08 +0000228 if (!entry->title) {
229 free(entry);
Masahisa Kojima015405a2022-04-28 17:09:41 +0900230 return -ENOMEM;
Pali Rohárac91b472013-03-23 14:53:08 +0000231 }
Pali Rohárac91b472013-03-23 14:53:08 +0000232
Christian Marangib357fc62025-05-25 15:43:58 +0200233 shortcut_key = bootmenu_entry_shortcut_key(i);
234 /* Use emtpy space if entry doesn't support shortcut key */
235 snprintf(entry->title, len, "%c%c %s",
236 shortcut_key > 0 ? shortcut_key : ' ',
237 shortcut_key > 0 ? '.' : ' ',
238 option);
239
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900240 entry->command = strdup(sep + 1);
Pali Rohárac91b472013-03-23 14:53:08 +0000241 if (!entry->command) {
242 free(entry->title);
243 free(entry);
Masahisa Kojima015405a2022-04-28 17:09:41 +0900244 return -ENOMEM;
Pali Rohárac91b472013-03-23 14:53:08 +0000245 }
Pali Rohárac91b472013-03-23 14:53:08 +0000246
247 sprintf(entry->key, "%d", i);
248
249 entry->num = i;
250 entry->menu = menu;
Masahisa Kojima015405a2022-04-28 17:09:41 +0900251 entry->type = BOOTMENU_TYPE_BOOTMENU;
252 entry->bootorder = i;
Pali Rohárac91b472013-03-23 14:53:08 +0000253 entry->next = NULL;
254
255 if (!iter)
256 menu->first = entry;
257 else
258 iter->next = entry;
259
260 iter = entry;
261 ++i;
262
263 if (i == MAX_COUNT - 1)
264 break;
265 }
266
Masahisa Kojima015405a2022-04-28 17:09:41 +0900267 *index = i;
268 *current = iter;
269
270 return 1;
271}
272
Simon Glass315367a2023-02-05 15:36:28 -0700273#if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) && (IS_ENABLED(CONFIG_CMD_EFICONFIG))
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900274/**
275 * prepare_uefi_bootorder_entry() - generate the uefi bootmenu entries
276 *
Heinrich Schuchardt2013f902024-11-23 10:04:03 +0100277 * This function reads the "BootOrder" UEFI variable
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900278 * and generate the bootmenu entries in the order of "BootOrder".
279 *
280 * @menu: pointer to the bootmenu structure
281 * @current: pointer to the last bootmenu entry list
282 * @index: pointer to the index of the last bootmenu entry,
283 * the number of uefi entry is added by this function
284 * Return: 1 on success, negative value on error
285 */
286static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
287 struct bootmenu_entry **current,
288 unsigned short int *index)
289{
290 u16 *bootorder;
291 efi_status_t ret;
292 unsigned short j;
293 efi_uintn_t num, size;
294 void *load_option;
295 struct efi_load_option lo;
296 u16 varname[] = u"Boot####";
297 unsigned short int i = *index;
298 struct bootmenu_entry *entry = NULL;
299 struct bootmenu_entry *iter = *current;
300
301 bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
302 if (!bootorder)
303 return -ENOENT;
304
305 num = size / sizeof(u16);
306 for (j = 0; j < num; j++) {
307 entry = malloc(sizeof(struct bootmenu_entry));
308 if (!entry)
309 return -ENOMEM;
310
311 efi_create_indexed_name(varname, sizeof(varname),
312 "Boot", bootorder[j]);
313 load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
314 if (!load_option)
315 continue;
316
317 ret = efi_deserialize_load_option(&lo, load_option, &size);
318 if (ret != EFI_SUCCESS) {
319 log_warning("Invalid load option for %ls\n", varname);
320 free(load_option);
321 free(entry);
322 continue;
323 }
324
325 if (lo.attributes & LOAD_OPTION_ACTIVE) {
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900326 char *buf;
327
328 buf = calloc(1, utf16_utf8_strlen(lo.label) + 1);
329 if (!buf) {
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900330 free(load_option);
331 free(entry);
332 free(bootorder);
333 return -ENOMEM;
334 }
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900335 entry->title = buf;
336 utf16_utf8_strncpy(&buf, lo.label, u16_strlen(lo.label));
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900337 entry->command = strdup("bootefi bootmgr");
338 sprintf(entry->key, "%d", i);
339 entry->num = i;
340 entry->menu = menu;
341 entry->type = BOOTMENU_TYPE_UEFI_BOOT_OPTION;
342 entry->bootorder = bootorder[j];
343 entry->next = NULL;
344
345 if (!iter)
346 menu->first = entry;
347 else
348 iter->next = entry;
349
350 iter = entry;
351 i++;
352 }
353
354 free(load_option);
355
356 if (i == MAX_COUNT - 1)
357 break;
358 }
359
360 free(bootorder);
361 *index = i;
362 *current = iter;
363
364 return 1;
365}
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900366#endif
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900367
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100368/**
369 * bootmenu_create() - create boot menu entries
370 *
371 * @uefi: consider UEFI boot options
372 * @delay: autostart delay in seconds
373 */
374static struct bootmenu_data *bootmenu_create(int uefi, int delay)
Masahisa Kojima015405a2022-04-28 17:09:41 +0900375{
376 int ret;
377 unsigned short int i = 0;
378 struct bootmenu_data *menu;
379 struct bootmenu_entry *iter = NULL;
380 struct bootmenu_entry *entry;
381 char *default_str;
382
383 menu = malloc(sizeof(struct bootmenu_data));
384 if (!menu)
385 return NULL;
386
387 menu->delay = delay;
388 menu->active = 0;
developerc1c0d2c2024-10-29 17:47:22 +0800389 menu->last_active = -1;
Masahisa Kojima015405a2022-04-28 17:09:41 +0900390 menu->first = NULL;
391
392 default_str = env_get("bootmenu_default");
393 if (default_str)
394 menu->active = (int)simple_strtol(default_str, NULL, 10);
395
396 ret = prepare_bootmenu_entry(menu, &iter, &i);
397 if (ret < 0)
398 goto cleanup;
399
Simon Glass315367a2023-02-05 15:36:28 -0700400#if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) && (IS_ENABLED(CONFIG_CMD_EFICONFIG))
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100401 if (uefi && i < MAX_COUNT - 1) {
Masahisa Kojima767a9e62022-09-12 17:33:54 +0900402 efi_status_t efi_ret;
403
404 /*
405 * UEFI specification requires booting from removal media using
406 * a architecture-specific default image name such as BOOTAA64.EFI.
407 */
Raymond Mao70a76c52023-06-19 14:22:58 -0700408 efi_ret = efi_bootmgr_update_media_device_boot_option();
Raymond Maoa35784d2023-06-19 14:22:59 -0700409 if (efi_ret != EFI_SUCCESS)
Masahisa Kojima767a9e62022-09-12 17:33:54 +0900410 goto cleanup;
411
412 ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
413 if (ret < 0 && ret != -ENOENT)
414 goto cleanup;
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900415 }
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900416#endif
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900417
Svyatoslav Ryhel08f67c42024-01-17 12:55:46 +0200418 /* Add Exit entry at the end */
Pali Rohárac91b472013-03-23 14:53:08 +0000419 if (i <= MAX_COUNT - 1) {
420 entry = malloc(sizeof(struct bootmenu_entry));
421 if (!entry)
422 goto cleanup;
423
Svyatoslav Ryhel08f67c42024-01-17 12:55:46 +0200424 /* Add Quit entry if exiting bootmenu is disabled */
Masahisa Kojima97cbcc42022-05-26 19:09:38 +0900425 if (!IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE))
Christian Marangib357fc62025-05-25 15:43:58 +0200426 entry->title = strdup("0. Exit");
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900427 else
Christian Marangib357fc62025-05-25 15:43:58 +0200428 entry->title = strdup("0. Quit");
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900429
Pali Rohárac91b472013-03-23 14:53:08 +0000430 if (!entry->title) {
431 free(entry);
432 goto cleanup;
433 }
434
435 entry->command = strdup("");
436 if (!entry->command) {
437 free(entry->title);
438 free(entry);
439 goto cleanup;
440 }
441
442 sprintf(entry->key, "%d", i);
443
444 entry->num = i;
445 entry->menu = menu;
Masahisa Kojima015405a2022-04-28 17:09:41 +0900446 entry->type = BOOTMENU_TYPE_NONE;
Pali Rohárac91b472013-03-23 14:53:08 +0000447 entry->next = NULL;
448
449 if (!iter)
450 menu->first = entry;
451 else
452 iter->next = entry;
453
454 iter = entry;
455 ++i;
456 }
457
458 menu->count = i;
Frank Wunderlich4931d962018-12-03 11:23:41 +0100459
460 if ((menu->active >= menu->count)||(menu->active < 0)) { //ensure active menuitem is inside menu
461 printf("active menuitem (%d) is outside menu (0..%d)\n",menu->active,menu->count-1);
462 menu->active=0;
463 }
464
Pali Rohárac91b472013-03-23 14:53:08 +0000465 return menu;
466
467cleanup:
468 bootmenu_destroy(menu);
469 return NULL;
470}
471
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700472static void menu_display_statusline(struct menu *m)
473{
474 struct bootmenu_entry *entry;
475 struct bootmenu_data *menu;
476
477 if (menu_default_choice(m, (void *)&entry) < 0)
478 return;
479
480 menu = entry->menu;
481
482 printf(ANSI_CURSOR_POSITION, 1, 1);
483 puts(ANSI_CLEAR_LINE);
Heinrich Schuchardtd7dc8772022-05-01 23:17:18 +0200484 printf(ANSI_CURSOR_POSITION, 2, 3);
485 puts("*** U-Boot Boot Menu ***");
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700486 puts(ANSI_CLEAR_LINE_TO_END);
487 printf(ANSI_CURSOR_POSITION, 3, 1);
488 puts(ANSI_CLEAR_LINE);
489
490 /* First 3 lines are bootmenu header + 2 empty lines between entries */
491 printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
492 puts(ANSI_CLEAR_LINE);
Heinrich Schuchardtd7dc8772022-05-01 23:17:18 +0200493 printf(ANSI_CURSOR_POSITION, menu->count + 6, 3);
Masahisa Kojima3a9eb072023-02-02 18:24:43 +0900494 puts("Press UP/DOWN to move, ENTER to select, ESC to quit");
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700495 puts(ANSI_CLEAR_LINE_TO_END);
496 printf(ANSI_CURSOR_POSITION, menu->count + 7, 1);
497 puts(ANSI_CLEAR_LINE);
498}
499
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900500static void handle_uefi_bootnext(void)
501{
502 u16 bootnext;
503 efi_status_t ret;
504 efi_uintn_t size;
505
506 /* Initialize EFI drivers */
507 ret = efi_init_obj_list();
508 if (ret != EFI_SUCCESS) {
509 log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
510 ret & ~EFI_ERROR_MASK);
511
512 return;
513 }
514
515 /* If UEFI BootNext variable is set, boot the BootNext load option */
516 size = sizeof(u16);
517 ret = efi_get_variable_int(u"BootNext",
518 &efi_global_variable_guid,
519 NULL, &size, &bootnext, NULL);
520 if (ret == EFI_SUCCESS)
521 /* BootNext does exist here, try to boot */
522 run_command("bootefi bootmgr", 0);
523}
524
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100525/**
526 * bootmenu_show - display boot menu
527 *
528 * @uefi: generated entries for UEFI boot options
529 * @delay: autoboot delay in seconds
530 */
531static enum bootmenu_ret bootmenu_show(int uefi, int delay)
Pali Rohárac91b472013-03-23 14:53:08 +0000532{
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900533 int cmd_ret;
Pali Rohárac91b472013-03-23 14:53:08 +0000534 int init = 0;
535 void *choice = NULL;
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900536 char *title = NULL;
Pali Rohárac91b472013-03-23 14:53:08 +0000537 char *command = NULL;
538 struct menu *menu;
Pali Rohárac91b472013-03-23 14:53:08 +0000539 struct bootmenu_entry *iter;
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900540 int ret = BOOTMENU_RET_SUCCESS;
541 struct bootmenu_data *bootmenu;
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900542 efi_status_t efi_ret = EFI_SUCCESS;
Pali Rohárac91b472013-03-23 14:53:08 +0000543 char *option, *sep;
544
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100545 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && uefi)
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900546 handle_uefi_bootnext();
547
Pali Rohárac91b472013-03-23 14:53:08 +0000548 /* If delay is 0 do not create menu, just run first entry */
549 if (delay == 0) {
550 option = bootmenu_getoption(0);
551 if (!option) {
552 puts("bootmenu option 0 was not found\n");
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900553 return BOOTMENU_RET_FAIL;
Pali Rohárac91b472013-03-23 14:53:08 +0000554 }
555 sep = strchr(option, '=');
556 if (!sep) {
557 puts("bootmenu option 0 is invalid\n");
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900558 return BOOTMENU_RET_FAIL;
Pali Rohárac91b472013-03-23 14:53:08 +0000559 }
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900560 cmd_ret = run_command(sep + 1, 0);
561 return (cmd_ret == CMD_RET_SUCCESS ? BOOTMENU_RET_SUCCESS : BOOTMENU_RET_FAIL);
Pali Rohárac91b472013-03-23 14:53:08 +0000562 }
563
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100564 bootmenu = bootmenu_create(uefi, delay);
Pali Rohárac91b472013-03-23 14:53:08 +0000565 if (!bootmenu)
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900566 return BOOTMENU_RET_FAIL;
Pali Rohárac91b472013-03-23 14:53:08 +0000567
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700568 menu = menu_create(NULL, bootmenu->delay, 1, menu_display_statusline,
569 bootmenu_print_entry, bootmenu_choice_entry,
developerc1c0d2c2024-10-29 17:47:22 +0800570 bootmenu_need_reprint, bootmenu);
Pali Rohárac91b472013-03-23 14:53:08 +0000571 if (!menu) {
572 bootmenu_destroy(bootmenu);
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900573 return BOOTMENU_RET_FAIL;
Pali Rohárac91b472013-03-23 14:53:08 +0000574 }
575
576 for (iter = bootmenu->first; iter; iter = iter->next) {
Masahisa Kojimaacc969e2022-03-24 22:54:33 +0900577 if (menu_item_add(menu, iter->key, iter) != 1)
Pali Rohárac91b472013-03-23 14:53:08 +0000578 goto cleanup;
579 }
580
581 /* Default menu entry is always first */
582 menu_default_set(menu, "0");
583
584 puts(ANSI_CURSOR_HIDE);
585 puts(ANSI_CLEAR_CONSOLE);
586 printf(ANSI_CURSOR_POSITION, 1, 1);
587
588 init = 1;
589
Masahisa Kojimaacc969e2022-03-24 22:54:33 +0900590 if (menu_get_choice(menu, &choice) == 1) {
Pali Rohárac91b472013-03-23 14:53:08 +0000591 iter = choice;
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900592 title = strdup(iter->title);
Pali Rohárac91b472013-03-23 14:53:08 +0000593 command = strdup(iter->command);
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900594
Svyatoslav Ryhel08f67c42024-01-17 12:55:46 +0200595 /* last entry exits bootmenu */
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900596 if (iter->num == iter->menu->count - 1) {
597 ret = BOOTMENU_RET_QUIT;
598 goto cleanup;
599 }
600 } else {
601 goto cleanup;
Pali Rohárac91b472013-03-23 14:53:08 +0000602 }
603
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900604 /*
605 * If the selected entry is UEFI BOOT####, set the BootNext variable.
606 * Then uefi bootmgr is invoked by the preset command in iter->command.
607 */
608 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
609 if (iter->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION) {
610 /*
611 * UEFI specification requires BootNext variable needs non-volatile
612 * attribute, but this BootNext is only used inside of U-Boot and
613 * removed by efi bootmgr once BootNext is processed.
614 * So this BootNext can be volatile.
615 */
616 efi_ret = efi_set_variable_int(u"BootNext", &efi_global_variable_guid,
617 EFI_VARIABLE_BOOTSERVICE_ACCESS |
618 EFI_VARIABLE_RUNTIME_ACCESS,
619 sizeof(u16), &iter->bootorder, false);
620 if (efi_ret != EFI_SUCCESS)
621 goto cleanup;
622 }
623 }
624
Pali Rohárac91b472013-03-23 14:53:08 +0000625cleanup:
626 menu_destroy(menu);
627 bootmenu_destroy(bootmenu);
628
629 if (init) {
630 puts(ANSI_CURSOR_SHOW);
631 puts(ANSI_CLEAR_CONSOLE);
632 printf(ANSI_CURSOR_POSITION, 1, 1);
633 }
634
635 if (title && command) {
Masahisa Kojima1baf9642022-05-29 10:52:43 +0900636 debug("Starting entry '%s'\n", title);
Pali Rohárac91b472013-03-23 14:53:08 +0000637 free(title);
Masahisa Kojimaf90c7b22022-04-28 17:09:42 +0900638 if (efi_ret == EFI_SUCCESS)
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900639 cmd_ret = run_command(command, 0);
Pali Rohárac91b472013-03-23 14:53:08 +0000640 free(command);
641 }
642
Tom Rinie510a3f2022-12-04 10:13:33 -0500643#ifdef CFG_POSTBOOTMENU
644 run_command(CFG_POSTBOOTMENU, 0);
Pali Rohárac91b472013-03-23 14:53:08 +0000645#endif
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900646
647 if (efi_ret != EFI_SUCCESS || cmd_ret != CMD_RET_SUCCESS)
648 ret = BOOTMENU_RET_FAIL;
649
650 return ret;
Pali Rohárac91b472013-03-23 14:53:08 +0000651}
652
Simon Glassec5f71a2019-07-20 20:51:24 -0600653#ifdef CONFIG_AUTOBOOT_MENU_SHOW
Pali Rohárac91b472013-03-23 14:53:08 +0000654int menu_show(int bootdelay)
655{
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900656 int ret;
657
658 while (1) {
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100659 ret = bootmenu_show(1, bootdelay);
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900660 bootdelay = -1;
661 if (ret == BOOTMENU_RET_UPDATED)
662 continue;
663
Masahisa Kojima97cbcc42022-05-26 19:09:38 +0900664 if (IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE)) {
Masahisa Kojima43d0ab22022-04-28 17:09:44 +0900665 if (ret == BOOTMENU_RET_QUIT) {
666 /* default boot process */
667 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR))
668 run_command("bootefi bootmgr", 0);
669
670 run_command("run bootcmd", 0);
671 }
672 } else {
673 break;
674 }
675 }
676
Pali Rohárac91b472013-03-23 14:53:08 +0000677 return -1; /* -1 - abort boot and run monitor code */
678}
679#endif
680
Simon Glassed38aef2020-05-10 11:40:03 -0600681int do_bootmenu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Pali Rohárac91b472013-03-23 14:53:08 +0000682{
683 char *delay_str = NULL;
684 int delay = 10;
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100685 int uefi = 0;
Pali Rohárac91b472013-03-23 14:53:08 +0000686
687#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
688 delay = CONFIG_BOOTDELAY;
689#endif
690
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100691 if (argc >= 2) {
692 if (!strcmp("-e", argv[1])) {
693 uefi = 1;
694 --argc;
695 ++argv;
696 }
697 }
Pali Rohárac91b472013-03-23 14:53:08 +0000698 if (argc >= 2)
699 delay_str = argv[1];
700
701 if (!delay_str)
Simon Glass64b723f2017-08-03 12:22:12 -0600702 delay_str = env_get("bootmenu_delay");
Pali Rohárac91b472013-03-23 14:53:08 +0000703
704 if (delay_str)
705 delay = (int)simple_strtol(delay_str, NULL, 10);
706
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100707 bootmenu_show(uefi, delay);
Pali Rohárac91b472013-03-23 14:53:08 +0000708 return 0;
709}
710
711U_BOOT_CMD(
712 bootmenu, 2, 1, do_bootmenu,
713 "ANSI terminal bootmenu",
Heinrich Schuchardt0259d412024-11-27 08:06:30 +0100714 "[-e] [delay]\n"
715 "-e - show UEFI entries\n"
716 "delay - show ANSI terminal bootmenu with autoboot delay"
Pali Rohárac91b472013-03-23 14:53:08 +0000717);