uboot/cmd/bootflow.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * 'bootflow' command
   4 *
   5 * Copyright 2021 Google LLC
   6 * Written by Simon Glass <sjg@chromium.org>
   7 */
   8
   9#include <common.h>
  10#include <bootdev.h>
  11#include <bootflow.h>
  12#include <bootstd.h>
  13#include <command.h>
  14#include <console.h>
  15#include <dm.h>
  16#include <mapmem.h>
  17
  18/**
  19 * report_bootflow_err() - Report where a bootflow failed
  20 *
  21 * When a bootflow does not make it to the 'loaded' state, something went wrong.
  22 * Print a helpful message if there is an error
  23 *
  24 * @bflow: Bootflow to process
  25 * @err: Error code (0 if none)
  26 */
  27static void report_bootflow_err(struct bootflow *bflow, int err)
  28{
  29        if (!err)
  30                return;
  31
  32        /* Indent out to 'Method' */
  33        printf("     ** ");
  34
  35        switch (bflow->state) {
  36        case BOOTFLOWST_BASE:
  37                printf("No media/partition found");
  38                break;
  39        case BOOTFLOWST_MEDIA:
  40                printf("No partition found");
  41                break;
  42        case BOOTFLOWST_PART:
  43                printf("No filesystem found");
  44                break;
  45        case BOOTFLOWST_FS:
  46                printf("File not found");
  47                break;
  48        case BOOTFLOWST_FILE:
  49                printf("File cannot be loaded");
  50                break;
  51        case BOOTFLOWST_READY:
  52                printf("Ready");
  53                break;
  54        case BOOTFLOWST_COUNT:
  55                break;
  56        }
  57
  58        printf(", err=%dE\n", err);
  59}
  60
  61/**
  62 * show_bootflow() - Show the status of a bootflow
  63 *
  64 * @seq: Bootflow index
  65 * @bflow: Bootflow to show
  66 * @errors: True to show the error received, if any
  67 */
  68static void show_bootflow(int index, struct bootflow *bflow, bool errors)
  69{
  70        printf("%3x  %-11s  %-6s  %-9.9s %4x  %-25.25s %s\n", index,
  71               bflow->method->name, bootflow_state_get_name(bflow->state),
  72               bflow->dev ? dev_get_uclass_name(dev_get_parent(bflow->dev)) :
  73               "(none)", bflow->part, bflow->name, bflow->fname);
  74        if (errors)
  75                report_bootflow_err(bflow, bflow->err);
  76}
  77
  78static void show_header(void)
  79{
  80        printf("Seq  Method       State   Uclass    Part  Name                      Filename\n");
  81        printf("---  -----------  ------  --------  ----  ------------------------  ----------------\n");
  82}
  83
  84static void show_footer(int count, int num_valid)
  85{
  86        printf("---  -----------  ------  --------  ----  ------------------------  ----------------\n");
  87        printf("(%d bootflow%s, %d valid)\n", count, count != 1 ? "s" : "",
  88               num_valid);
  89}
  90
  91static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc,
  92                            char *const argv[])
  93{
  94        struct bootstd_priv *std;
  95        struct bootflow_iter iter;
  96        struct udevice *dev = NULL;
  97        struct bootflow bflow;
  98        bool all = false, boot = false, errors = false, no_global = false;
  99        bool list = false, no_hunter = false;
 100        int num_valid = 0;
 101        const char *label = NULL;
 102        bool has_args;
 103        int ret, i;
 104        int flags;
 105
 106        ret = bootstd_get_priv(&std);
 107        if (ret)
 108                return CMD_RET_FAILURE;
 109
 110        has_args = argc > 1 && *argv[1] == '-';
 111        if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL)) {
 112                if (has_args) {
 113                        all = strchr(argv[1], 'a');
 114                        boot = strchr(argv[1], 'b');
 115                        errors = strchr(argv[1], 'e');
 116                        no_global = strchr(argv[1], 'G');
 117                        list = strchr(argv[1], 'l');
 118                        no_hunter = strchr(argv[1], 'H');
 119                        argc--;
 120                        argv++;
 121                }
 122                if (argc > 1)
 123                        label = argv[1];
 124                if (!label)
 125                        dev = std->cur_bootdev;
 126        } else {
 127                if (has_args) {
 128                        printf("Flags not supported: enable CONFIG_BOOTSTD_FULL\n");
 129                        return CMD_RET_USAGE;
 130                }
 131                boot = true;
 132        }
 133
 134        std->cur_bootflow = NULL;
 135
 136        flags = 0;
 137        if (list)
 138                flags |= BOOTFLOWIF_SHOW;
 139        if (all)
 140                flags |= BOOTFLOWIF_ALL;
 141        if (no_global)
 142                flags |= BOOTFLOWIF_SKIP_GLOBAL;
 143        if (!no_hunter)
 144                flags |= BOOTFLOWIF_HUNT;
 145
 146        /*
 147         * If we have a device, just scan for bootflows attached to that device
 148         */
 149        if (list) {
 150                printf("Scanning for bootflows ");
 151                if (dev)
 152                        printf("in bootdev '%s'\n", dev->name);
 153                else if (label)
 154                        printf("with label '%s'\n", label);
 155                else
 156                        printf("in all bootdevs\n");
 157                show_header();
 158        }
 159        if (dev)
 160                bootdev_clear_bootflows(dev);
 161        else
 162                bootstd_clear_glob();
 163        for (i = 0,
 164             ret = bootflow_scan_first(dev, label, &iter, flags, &bflow);
 165             i < 1000 && ret != -ENODEV;
 166             i++, ret = bootflow_scan_next(&iter, &bflow)) {
 167                bflow.err = ret;
 168                if (!ret)
 169                        num_valid++;
 170                ret = bootdev_add_bootflow(&bflow);
 171                if (ret) {
 172                        printf("Out of memory\n");
 173                        return CMD_RET_FAILURE;
 174                }
 175                if (list)
 176                        show_bootflow(i, &bflow, errors);
 177                if (boot && !bflow.err)
 178                        bootflow_run_boot(&iter, &bflow);
 179        }
 180        bootflow_iter_uninit(&iter);
 181        if (list)
 182                show_footer(i, num_valid);
 183
 184        if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL) && !num_valid && !list)
 185                printf("No bootflows found; try again with -l\n");
 186
 187        return 0;
 188}
 189
 190#ifdef CONFIG_CMD_BOOTFLOW_FULL
 191static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc,
 192                            char *const argv[])
 193{
 194        struct bootstd_priv *std;
 195        struct udevice *dev;
 196        struct bootflow *bflow;
 197        int num_valid = 0;
 198        bool errors = false;
 199        int ret, i;
 200
 201        if (argc > 1 && *argv[1] == '-')
 202                errors = strchr(argv[1], 'e');
 203
 204        ret = bootstd_get_priv(&std);
 205        if (ret)
 206                return CMD_RET_FAILURE;
 207        dev = std->cur_bootdev;
 208
 209        /* If we have a device, just list bootflows attached to that device */
 210        if (dev) {
 211                printf("Showing bootflows for bootdev '%s'\n", dev->name);
 212                show_header();
 213                for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
 214                     !ret;
 215                     ret = bootdev_next_bootflow(&bflow), i++) {
 216                        num_valid += bflow->state == BOOTFLOWST_READY;
 217                        show_bootflow(i, bflow, errors);
 218                }
 219        } else {
 220                printf("Showing all bootflows\n");
 221                show_header();
 222                for (ret = bootflow_first_glob(&bflow), i = 0;
 223                     !ret;
 224                     ret = bootflow_next_glob(&bflow), i++) {
 225                        num_valid += bflow->state == BOOTFLOWST_READY;
 226                        show_bootflow(i, bflow, errors);
 227                }
 228        }
 229        show_footer(i, num_valid);
 230
 231        return 0;
 232}
 233
 234static int do_bootflow_select(struct cmd_tbl *cmdtp, int flag, int argc,
 235                              char *const argv[])
 236{
 237        struct bootstd_priv *std;
 238        struct bootflow *bflow, *found;
 239        struct udevice *dev;
 240        const char *name;
 241        char *endp;
 242        int seq, i;
 243        int ret;
 244
 245        ret = bootstd_get_priv(&std);
 246        if (ret)
 247                return CMD_RET_FAILURE;
 248;
 249        if (argc < 2) {
 250                std->cur_bootflow = NULL;
 251                return 0;
 252        }
 253        dev = std->cur_bootdev;
 254
 255        name = argv[1];
 256        seq = simple_strtol(name, &endp, 16);
 257        found = NULL;
 258
 259        /*
 260         * If we have a bootdev device, only allow selection of bootflows
 261         * attached to that device
 262         */
 263        if (dev) {
 264                for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
 265                     !ret;
 266                     ret = bootdev_next_bootflow(&bflow), i++) {
 267                        if (*endp ? !strcmp(bflow->name, name) : i == seq) {
 268                                found = bflow;
 269                                break;
 270                        }
 271                }
 272        } else {
 273                for (ret = bootflow_first_glob(&bflow), i = 0;
 274                     !ret;
 275                     ret = bootflow_next_glob(&bflow), i++) {
 276                        if (*endp ? !strcmp(bflow->name, name) : i == seq) {
 277                                found = bflow;
 278                                break;
 279                        }
 280                }
 281        }
 282
 283        if (!found) {
 284                printf("Cannot find bootflow '%s' ", name);
 285                if (dev)
 286                        printf("in bootdev '%s' ", dev->name);
 287                printf("(err=%d)\n", ret);
 288                return CMD_RET_FAILURE;
 289        }
 290        std->cur_bootflow = found;
 291
 292        return 0;
 293}
 294
 295static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc,
 296                            char *const argv[])
 297{
 298        struct bootstd_priv *std;
 299        struct bootflow *bflow;
 300        bool dump = false;
 301        int ret;
 302
 303        if (argc > 1 && *argv[1] == '-')
 304                dump = strchr(argv[1], 'd');
 305
 306        ret = bootstd_get_priv(&std);
 307        if (ret)
 308                return CMD_RET_FAILURE;
 309
 310        if (!std->cur_bootflow) {
 311                printf("No bootflow selected\n");
 312                return CMD_RET_FAILURE;
 313        }
 314        bflow = std->cur_bootflow;
 315
 316        printf("Name:      %s\n", bflow->name);
 317        printf("Device:    %s\n", bflow->dev->name);
 318        printf("Block dev: %s\n", bflow->blk ? bflow->blk->name : "(none)");
 319        printf("Method:    %s\n", bflow->method->name);
 320        printf("State:     %s\n", bootflow_state_get_name(bflow->state));
 321        printf("Partition: %d\n", bflow->part);
 322        printf("Subdir:    %s\n", bflow->subdir ? bflow->subdir : "(none)");
 323        printf("Filename:  %s\n", bflow->fname);
 324        printf("Buffer:    %lx\n", (ulong)map_to_sysmem(bflow->buf));
 325        printf("Size:      %x (%d bytes)\n", bflow->size, bflow->size);
 326        printf("OS:        %s\n", bflow->os_name ? bflow->os_name : "(none)");
 327        printf("Logo:      %s\n", bflow->logo ?
 328               simple_xtoa((ulong)map_to_sysmem(bflow->logo)) : "(none)");
 329        if (bflow->logo) {
 330                printf("Logo size: %x (%d bytes)\n", bflow->logo_size,
 331                       bflow->logo_size);
 332        }
 333        printf("FDT:       %s\n", bflow->fdt_fname);
 334        if (bflow->fdt_fname) {
 335                printf("FDT size:  %x (%d bytes)\n", bflow->fdt_size,
 336                       bflow->fdt_size);
 337                printf("FDT addr:  %lx\n", bflow->fdt_addr);
 338        }
 339        printf("Error:     %d\n", bflow->err);
 340        if (dump && bflow->buf) {
 341                /* Set some sort of maximum on the size */
 342                int size = min(bflow->size, 10 << 10);
 343                int i;
 344
 345                printf("Contents:\n\n");
 346                for (i = 0; i < size; i++) {
 347                        putc(bflow->buf[i]);
 348                        if (!(i % 128) && ctrlc()) {
 349                                printf("...interrupted\n");
 350                                break;
 351                        }
 352                }
 353        }
 354
 355        return 0;
 356}
 357
 358static int do_bootflow_boot(struct cmd_tbl *cmdtp, int flag, int argc,
 359                            char *const argv[])
 360{
 361        struct bootstd_priv *std;
 362        struct bootflow *bflow;
 363        int ret;
 364
 365        ret = bootstd_get_priv(&std);
 366        if (ret)
 367                return CMD_RET_FAILURE;
 368
 369        /*
 370         * Require a current bootflow. Users can use 'bootflow scan -b' to
 371         * automatically scan and boot, if needed.
 372         */
 373        if (!std->cur_bootflow) {
 374                printf("No bootflow selected\n");
 375                return CMD_RET_FAILURE;
 376        }
 377        bflow = std->cur_bootflow;
 378        ret = bootflow_run_boot(NULL, bflow);
 379        if (ret)
 380                return CMD_RET_FAILURE;
 381
 382        return 0;
 383}
 384
 385static int do_bootflow_menu(struct cmd_tbl *cmdtp, int flag, int argc,
 386                            char *const argv[])
 387{
 388        struct bootstd_priv *std;
 389        struct bootflow *bflow;
 390        bool text_mode = false;
 391        int ret;
 392
 393        if (!IS_ENABLED(CONFIG_EXPO)) {
 394                printf("Menu not supported\n");
 395                return CMD_RET_FAILURE;
 396        }
 397
 398        if (argc > 1 && *argv[1] == '-')
 399                text_mode = strchr(argv[1], 't');
 400
 401        ret = bootstd_get_priv(&std);
 402        if (ret)
 403                return CMD_RET_FAILURE;
 404
 405        ret = bootflow_menu_run(std, text_mode, &bflow);
 406        if (ret) {
 407                if (ret == -EAGAIN)
 408                        printf("Nothing chosen\n");
 409                else {
 410                        printf("Menu failed (err=%d)\n", ret);
 411                        return CMD_RET_FAILURE;
 412                }
 413        }
 414
 415        printf("Selected: %s\n", bflow->os_name ? bflow->os_name : bflow->name);
 416        std->cur_bootflow = bflow;
 417
 418        return 0;
 419}
 420#endif /* CONFIG_CMD_BOOTFLOW_FULL */
 421
 422#ifdef CONFIG_SYS_LONGHELP
 423static char bootflow_help_text[] =
 424#ifdef CONFIG_CMD_BOOTFLOW_FULL
 425        "scan [-abeGl] [bdev]  - scan for valid bootflows (-l list, -a all, -e errors, -b boot, -G no global)\n"
 426        "bootflow list [-e]             - list scanned bootflows (-e errors)\n"
 427        "bootflow select [<num>|<name>] - select a bootflow\n"
 428        "bootflow info [-d]             - show info on current bootflow (-d dump bootflow)\n"
 429        "bootflow boot                  - boot current bootflow (or first available if none selected)\n"
 430        "bootflow menu [-t]             - show a menu of available bootflows";
 431#else
 432        "scan - boot first available bootflow\n";
 433#endif
 434#endif /* CONFIG_SYS_LONGHELP */
 435
 436U_BOOT_CMD_WITH_SUBCMDS(bootflow, "Boot flows", bootflow_help_text,
 437        U_BOOT_SUBCMD_MKENT(scan, 3, 1, do_bootflow_scan),
 438#ifdef CONFIG_CMD_BOOTFLOW_FULL
 439        U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootflow_list),
 440        U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootflow_select),
 441        U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootflow_info),
 442        U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot),
 443        U_BOOT_SUBCMD_MKENT(menu, 2, 1, do_bootflow_menu),
 444#endif
 445);
 446