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