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