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