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 <bootm.h> 13#include <bootstd.h> 14#include <command.h> 15#include <console.h> 16#include <dm.h> 17#include <mapmem.h> 18 19/** 20 * report_bootflow_err() - Report where a bootflow failed 21 * 22 * When a bootflow does not make it to the 'loaded' state, something went wrong. 23 * Print a helpful message if there is an error 24 * 25 * @bflow: Bootflow to process 26 * @err: Error code (0 if none) 27 */ 28static void report_bootflow_err(struct bootflow *bflow, int err) 29{ 30 if (!err) 31 return; 32 33 /* Indent out to 'Method' */ 34 printf(" ** "); 35 36 switch (bflow->state) { 37 case BOOTFLOWST_BASE: 38 printf("No media/partition found"); 39 break; 40 case BOOTFLOWST_MEDIA: 41 printf("No partition found"); 42 break; 43 case BOOTFLOWST_PART: 44 printf("No filesystem found"); 45 break; 46 case BOOTFLOWST_FS: 47 printf("File not found"); 48 break; 49 case BOOTFLOWST_FILE: 50 printf("File cannot be loaded"); 51 break; 52 case BOOTFLOWST_READY: 53 printf("Ready"); 54 break; 55 case BOOTFLOWST_COUNT: 56 break; 57 } 58 59 printf(", err=%dE\n", err); 60} 61 62/** 63 * show_bootflow() - Show the status of a bootflow 64 * 65 * @seq: Bootflow index 66 * @bflow: Bootflow to show 67 * @errors: True to show the error received, if any 68 */ 69static void show_bootflow(int index, struct bootflow *bflow, bool errors) 70{ 71 printf("%3x %-11s %-6s %-9.9s %4x %-25.25s %s\n", index, 72 bflow->method->name, bootflow_state_get_name(bflow->state), 73 bflow->dev ? dev_get_uclass_name(dev_get_parent(bflow->dev)) : 74 "(none)", bflow->part, bflow->name, bflow->fname ?: ""); 75 if (errors) 76 report_bootflow_err(bflow, bflow->err); 77} 78 79static void show_header(void) 80{ 81 printf("Seq Method State Uclass Part Name Filename\n"); 82 printf("--- ----------- ------ -------- ---- ------------------------ ----------------\n"); 83} 84 85static void show_footer(int count, int num_valid) 86{ 87 printf("--- ----------- ------ -------- ---- ------------------------ ----------------\n"); 88 printf("(%d bootflow%s, %d valid)\n", count, count != 1 ? "s" : "", 89 num_valid); 90} 91 92/** 93 * bootflow_handle_menu() - Handle running the menu and updating cur bootflow 94 * 95 * This shows the menu, allows the user to select something and then prints 96 * what happened 97 * 98 * @std: bootstd information 99 * @text_mode: true to run the menu in text mode 100 * @bflowp: Returns selected bootflow, on success 101 * Return: 0 on success (a bootflow was selected), -EAGAIN if nothing was 102 * chosen, other -ve value on other error 103 */ 104__maybe_unused static int bootflow_handle_menu(struct bootstd_priv *std, 105 bool text_mode, 106 struct bootflow **bflowp) 107{ 108 struct bootflow *bflow; 109 int ret; 110 111 ret = bootflow_menu_run(std, text_mode, &bflow); 112 if (ret) { 113 if (ret == -EAGAIN) { 114 printf("Nothing chosen\n"); 115 std->cur_bootflow = NULL; 116 } else { 117 printf("Menu failed (err=%d)\n", ret); 118 } 119 120 return ret; 121 } 122 123 printf("Selected: %s\n", bflow->os_name ? bflow->os_name : bflow->name); 124 std->cur_bootflow = bflow; 125 *bflowp = bflow; 126 127 return 0; 128} 129 130static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc, 131 char *const argv[]) 132{ 133 struct bootstd_priv *std; 134 struct bootflow_iter iter; 135 struct udevice *dev = NULL; 136 struct bootflow bflow; 137 bool all = false, boot = false, errors = false, no_global = false; 138 bool list = false, no_hunter = false, menu = false, text_mode = false; 139 int num_valid = 0; 140 const char *label = NULL; 141 bool has_args; 142 int ret, i; 143 int flags; 144 145 ret = bootstd_get_priv(&std); 146 if (ret) 147 return CMD_RET_FAILURE; 148 149 has_args = argc > 1 && *argv[1] == '-'; 150 if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL)) { 151 if (has_args) { 152 all = strchr(argv[1], 'a'); 153 boot = strchr(argv[1], 'b'); 154 errors = strchr(argv[1], 'e'); 155 no_global = strchr(argv[1], 'G'); 156 list = strchr(argv[1], 'l'); 157 no_hunter = strchr(argv[1], 'H'); 158 menu = strchr(argv[1], 'm'); 159 text_mode = strchr(argv[1], 't'); 160 argc--; 161 argv++; 162 } 163 if (argc > 1) 164 label = argv[1]; 165 if (!label) 166 dev = std->cur_bootdev; 167 } else { 168 if (has_args) { 169 printf("Flags not supported: enable CONFIG_BOOTSTD_FULL\n"); 170 return CMD_RET_USAGE; 171 } 172 boot = true; 173 } 174 175 std->cur_bootflow = NULL; 176 177 flags = 0; 178 if (list) 179 flags |= BOOTFLOWIF_SHOW; 180 if (all) 181 flags |= BOOTFLOWIF_ALL; 182 if (no_global) 183 flags |= BOOTFLOWIF_SKIP_GLOBAL; 184 if (!no_hunter) 185 flags |= BOOTFLOWIF_HUNT; 186 187 /* 188 * If we have a device, just scan for bootflows attached to that device 189 */ 190 if (list) { 191 printf("Scanning for bootflows "); 192 if (dev) 193 printf("in bootdev '%s'\n", dev->name); 194 else if (label) 195 printf("with label '%s'\n", label); 196 else 197 printf("in all bootdevs\n"); 198 show_header(); 199 } 200 if (dev) 201 bootdev_clear_bootflows(dev); 202 else 203 bootstd_clear_glob(); 204 for (i = 0, 205 ret = bootflow_scan_first(dev, label, &iter, flags, &bflow); 206 i < 1000 && ret != -ENODEV; 207 i++, ret = bootflow_scan_next(&iter, &bflow)) { 208 bflow.err = ret; 209 if (!ret) 210 num_valid++; 211 ret = bootdev_add_bootflow(&bflow); 212 if (ret) { 213 printf("Out of memory\n"); 214 return CMD_RET_FAILURE; 215 } 216 if (list) 217 show_bootflow(i, &bflow, errors); 218 if (!menu && boot && !bflow.err) 219 bootflow_run_boot(&iter, &bflow); 220 } 221 bootflow_iter_uninit(&iter); 222 if (list) 223 show_footer(i, num_valid); 224 225 if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL) && IS_ENABLED(CONFIG_EXPO)) { 226 if (!num_valid && !list) { 227 printf("No bootflows found; try again with -l\n"); 228 } else if (menu) { 229 struct bootflow *sel_bflow; 230 231 ret = bootflow_handle_menu(std, text_mode, &sel_bflow); 232 if (!ret && boot) { 233 ret = console_clear(); 234 if (ret) { 235 log_err("Failed to clear console: %dE\n", 236 ret); 237 return ret; 238 } 239 240 bootflow_run_boot(NULL, sel_bflow); 241 } 242 } 243 } 244 245 return 0; 246} 247 248#ifdef CONFIG_CMD_BOOTFLOW_FULL 249static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc, 250 char *const argv[]) 251{ 252 struct bootstd_priv *std; 253 struct udevice *dev; 254 struct bootflow *bflow; 255 int num_valid = 0; 256 bool errors = false; 257 int ret, i; 258 259 if (argc > 1 && *argv[1] == '-') 260 errors = strchr(argv[1], 'e'); 261 262 ret = bootstd_get_priv(&std); 263 if (ret) 264 return CMD_RET_FAILURE; 265 dev = std->cur_bootdev; 266 267 /* If we have a device, just list bootflows attached to that device */ 268 if (dev) { 269 printf("Showing bootflows for bootdev '%s'\n", dev->name); 270 show_header(); 271 for (ret = bootdev_first_bootflow(dev, &bflow), i = 0; 272 !ret; 273 ret = bootdev_next_bootflow(&bflow), i++) { 274 num_valid += bflow->state == BOOTFLOWST_READY; 275 show_bootflow(i, bflow, errors); 276 } 277 } else { 278 printf("Showing all bootflows\n"); 279 show_header(); 280 for (ret = bootflow_first_glob(&bflow), i = 0; 281 !ret; 282 ret = bootflow_next_glob(&bflow), i++) { 283 num_valid += bflow->state == BOOTFLOWST_READY; 284 show_bootflow(i, bflow, errors); 285 } 286 } 287 show_footer(i, num_valid); 288 289 return 0; 290} 291 292static int do_bootflow_select(struct cmd_tbl *cmdtp, int flag, int argc, 293 char *const argv[]) 294{ 295 struct bootstd_priv *std; 296 struct bootflow *bflow, *found; 297 struct udevice *dev; 298 const char *name; 299 char *endp; 300 int seq, i; 301 int ret; 302 303 ret = bootstd_get_priv(&std); 304 if (ret) 305 return CMD_RET_FAILURE; 306; 307 if (argc < 2) { 308 std->cur_bootflow = NULL; 309 return 0; 310 } 311 dev = std->cur_bootdev; 312 313 name = argv[1]; 314 seq = simple_strtol(name, &endp, 16); 315 found = NULL; 316 317 /* 318 * If we have a bootdev device, only allow selection of bootflows 319 * attached to that device 320 */ 321 if (dev) { 322 for (ret = bootdev_first_bootflow(dev, &bflow), i = 0; 323 !ret; 324 ret = bootdev_next_bootflow(&bflow), i++) { 325 if (*endp ? !strcmp(bflow->name, name) : i == seq) { 326 found = bflow; 327 break; 328 } 329 } 330 } else { 331 for (ret = bootflow_first_glob(&bflow), i = 0; 332 !ret; 333 ret = bootflow_next_glob(&bflow), i++) { 334 if (*endp ? !strcmp(bflow->name, name) : i == seq) { 335 found = bflow; 336 break; 337 } 338 } 339 } 340 341 if (!found) { 342 printf("Cannot find bootflow '%s' ", name); 343 if (dev) 344 printf("in bootdev '%s' ", dev->name); 345 printf("(err=%d)\n", ret); 346 return CMD_RET_FAILURE; 347 } 348 std->cur_bootflow = found; 349 if (IS_ENABLED(CONFIG_BOOTSTD_FULL)) { 350 if (env_set("bootargs", found->cmdline)) { 351 printf("Cannot set bootargs\n"); 352 return CMD_RET_FAILURE; 353 } 354 } 355 356 return 0; 357} 358 359static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc, 360 char *const argv[]) 361{ 362 struct bootstd_priv *std; 363 struct bootflow *bflow; 364 bool x86_setup = false; 365 bool dump = false; 366 int ret; 367 368 if (argc > 1 && *argv[1] == '-') { 369 dump = strchr(argv[1], 'd'); 370 x86_setup = strchr(argv[1], 's'); 371 } 372 373 ret = bootstd_get_priv(&std); 374 if (ret) 375 return CMD_RET_FAILURE; 376 377 if (!std->cur_bootflow) { 378 printf("No bootflow selected\n"); 379 return CMD_RET_FAILURE; 380 } 381 bflow = std->cur_bootflow; 382 383 if (IS_ENABLED(CONFIG_X86) && x86_setup) { 384 zimage_dump(bflow->x86_setup, false); 385 386 return 0; 387 } 388 389 printf("Name: %s\n", bflow->name); 390 printf("Device: %s\n", bflow->dev->name); 391 printf("Block dev: %s\n", bflow->blk ? bflow->blk->name : "(none)"); 392 printf("Method: %s\n", bflow->method->name); 393 printf("State: %s\n", bootflow_state_get_name(bflow->state)); 394 printf("Partition: %d\n", bflow->part); 395 printf("Subdir: %s\n", bflow->subdir ? bflow->subdir : "(none)"); 396 printf("Filename: %s\n", bflow->fname); 397 printf("Buffer: %lx\n", (ulong)map_to_sysmem(bflow->buf)); 398 printf("Size: %x (%d bytes)\n", bflow->size, bflow->size); 399 printf("OS: %s\n", bflow->os_name ? bflow->os_name : "(none)"); 400 printf("Cmdline: "); 401 if (bflow->cmdline) 402 puts(bflow->cmdline); 403 else 404 puts("(none)"); 405 putc('\n'); 406 if (bflow->x86_setup) 407 printf("X86 setup: %p\n", bflow->x86_setup); 408 printf("Logo: %s\n", bflow->logo ? 409 simple_xtoa((ulong)map_to_sysmem(bflow->logo)) : "(none)"); 410 if (bflow->logo) { 411 printf("Logo size: %x (%d bytes)\n", bflow->logo_size, 412 bflow->logo_size); 413 } 414 printf("FDT: %s\n", bflow->fdt_fname); 415 if (bflow->fdt_fname) { 416 printf("FDT size: %x (%d bytes)\n", bflow->fdt_size, 417 bflow->fdt_size); 418 printf("FDT addr: %lx\n", bflow->fdt_addr); 419 } 420 printf("Error: %d\n", bflow->err); 421 if (dump && bflow->buf) { 422 /* Set some sort of maximum on the size */ 423 int size = min(bflow->size, 10 << 10); 424 int i; 425 426 printf("Contents:\n\n"); 427 for (i = 0; i < size; i++) { 428 putc(bflow->buf[i]); 429 if (!(i % 128) && ctrlc()) { 430 printf("...interrupted\n"); 431 break; 432 } 433 } 434 } 435 436 return 0; 437} 438 439static int do_bootflow_read(struct cmd_tbl *cmdtp, int flag, int argc, 440 char *const argv[]) 441{ 442 struct bootstd_priv *std; 443 struct bootflow *bflow; 444 int ret; 445 446 ret = bootstd_get_priv(&std); 447 if (ret) 448 return CMD_RET_FAILURE; 449 450 /* 451 * Require a current bootflow. Users can use 'bootflow scan -b' to 452 * automatically scan and boot, if needed. 453 */ 454 if (!std->cur_bootflow) { 455 printf("No bootflow selected\n"); 456 return CMD_RET_FAILURE; 457 } 458 bflow = std->cur_bootflow; 459 ret = bootflow_read_all(bflow); 460 if (ret) { 461 printf("Failed: err=%dE\n", ret); 462 return CMD_RET_FAILURE; 463 } 464 465 return 0; 466} 467 468static int do_bootflow_boot(struct cmd_tbl *cmdtp, int flag, int argc, 469 char *const argv[]) 470{ 471 struct bootstd_priv *std; 472 struct bootflow *bflow; 473 int ret; 474 475 ret = bootstd_get_priv(&std); 476 if (ret) 477 return CMD_RET_FAILURE; 478 479 /* 480 * Require a current bootflow. Users can use 'bootflow scan -b' to 481 * automatically scan and boot, if needed. 482 */ 483 if (!std->cur_bootflow) { 484 printf("No bootflow selected\n"); 485 return CMD_RET_FAILURE; 486 } 487 bflow = std->cur_bootflow; 488 ret = bootflow_run_boot(NULL, bflow); 489 if (ret) 490 return CMD_RET_FAILURE; 491 492 return 0; 493} 494 495static int do_bootflow_menu(struct cmd_tbl *cmdtp, int flag, int argc, 496 char *const argv[]) 497{ 498 struct bootstd_priv *std; 499 struct bootflow *bflow; 500 bool text_mode = false; 501 int ret; 502 503 if (!IS_ENABLED(CONFIG_EXPO)) { 504 printf("Menu not supported\n"); 505 return CMD_RET_FAILURE; 506 } 507 508 if (argc > 1 && *argv[1] == '-') 509 text_mode = strchr(argv[1], 't'); 510 511 ret = bootstd_get_priv(&std); 512 if (ret) 513 return CMD_RET_FAILURE; 514 515 ret = bootflow_handle_menu(std, text_mode, &bflow); 516 if (ret) 517 return CMD_RET_FAILURE; 518 519 return 0; 520} 521 522static int do_bootflow_cmdline(struct cmd_tbl *cmdtp, int flag, int argc, 523 char *const argv[]) 524{ 525 struct bootstd_priv *std; 526 struct bootflow *bflow; 527 const char *op, *arg, *val = NULL; 528 int ret; 529 530 if (argc < 3) 531 return CMD_RET_USAGE; 532 533 ret = bootstd_get_priv(&std); 534 if (ret) 535 return CMD_RET_FAILURE; 536 537 bflow = std->cur_bootflow; 538 if (!bflow) { 539 printf("No bootflow selected\n"); 540 return CMD_RET_FAILURE; 541 } 542 543 op = argv[1]; 544 arg = argv[2]; 545 if (*op == 's') { 546 val = argv[3] ?: (const char *)BOOTFLOWCL_EMPTY; 547 } 548 549 switch (*op) { 550 case 'c': /* clear */ 551 val = ""; 552 fallthrough; 553 case 's': /* set */ 554 case 'd': /* delete */ 555 ret = bootflow_cmdline_set_arg(bflow, arg, val, true); 556 break; 557 case 'g': /* get */ 558 ret = bootflow_cmdline_get_arg(bflow, arg, &val); 559 if (ret >= 0) 560 printf("%.*s\n", ret, val); 561 break; 562 case 'a': /* auto */ 563 ret = bootflow_cmdline_auto(bflow, arg); 564 break; 565 } 566 switch (ret) { 567 case -E2BIG: 568 printf("Argument too long\n"); 569 break; 570 case -ENOENT: 571 printf("Argument not found\n"); 572 break; 573 case -EINVAL: 574 printf("Mismatched quotes\n"); 575 break; 576 case -EBADF: 577 printf("Value must be quoted\n"); 578 break; 579 default: 580 if (ret < 0) 581 printf("Unknown error: %dE\n", ret); 582 } 583 if (ret < 0) 584 return CMD_RET_FAILURE; 585 586 return 0; 587} 588#endif /* CONFIG_CMD_BOOTFLOW_FULL */ 589 590U_BOOT_LONGHELP(bootflow, 591#ifdef CONFIG_CMD_BOOTFLOW_FULL 592 "scan [-abeGl] [bdev] - scan for valid bootflows (-l list, -a all, -e errors, -b boot, -G no global)\n" 593 "bootflow list [-e] - list scanned bootflows (-e errors)\n" 594 "bootflow select [<num>|<name>] - select a bootflow\n" 595 "bootflow info [-ds] - show info on current bootflow (-d dump bootflow)\n" 596 "bootflow read - read all current-bootflow files\n" 597 "bootflow boot - boot current bootflow\n" 598 "bootflow menu [-t] - show a menu of available bootflows\n" 599 "bootflow cmdline [set|get|clear|delete|auto] <param> [<value>] - update cmdline" 600#else 601 "scan - boot first available bootflow\n" 602#endif 603 ); 604 605U_BOOT_CMD_WITH_SUBCMDS(bootflow, "Boot flows", bootflow_help_text, 606 U_BOOT_SUBCMD_MKENT(scan, 3, 1, do_bootflow_scan), 607#ifdef CONFIG_CMD_BOOTFLOW_FULL 608 U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootflow_list), 609 U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootflow_select), 610 U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootflow_info), 611 U_BOOT_SUBCMD_MKENT(read, 1, 1, do_bootflow_read), 612 U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot), 613 U_BOOT_SUBCMD_MKENT(menu, 2, 1, do_bootflow_menu), 614 U_BOOT_SUBCMD_MKENT(cmdline, 4, 1, do_bootflow_cmdline), 615#endif 616); 617