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