]> git.ipfire.org Git - thirdparty/u-boot.git/blame - cmd/bootflow.c
cmd: sbi: Correctly display unknown implementation IDs
[thirdparty/u-boot.git] / cmd / bootflow.c
CommitLineData
2d653f68
SG
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>
cbb607d2 12#include <bootm.h>
2d653f68
SG
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
c8894348 59 printf(", err=%dE\n", err);
2d653f68
SG
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),
eccb25cd 73 bflow->dev ? dev_get_uclass_name(dev_get_parent(bflow->dev)) :
c3867e2e 74 "(none)", bflow->part, bflow->name, bflow->fname ?: "");
2d653f68
SG
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
3fa53b95
SG
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
2d653f68
SG
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;
91943ff7 135 struct udevice *dev = NULL;
2d653f68 136 struct bootflow bflow;
2b80bc1e 137 bool all = false, boot = false, errors = false, no_global = false;
a4bee0b4 138 bool list = false, no_hunter = false, menu = false, text_mode = false;
2d653f68 139 int num_valid = 0;
91943ff7 140 const char *label = NULL;
2d653f68
SG
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;
2d653f68
SG
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');
2b80bc1e 155 no_global = strchr(argv[1], 'G');
2d653f68 156 list = strchr(argv[1], 'l');
d73420e4 157 no_hunter = strchr(argv[1], 'H');
a4bee0b4
SG
158 menu = strchr(argv[1], 'm');
159 text_mode = strchr(argv[1], 't');
2d653f68
SG
160 argc--;
161 argv++;
162 }
91943ff7
SG
163 if (argc > 1)
164 label = argv[1];
165 if (!label)
166 dev = std->cur_bootdev;
2d653f68
SG
167 } else {
168 if (has_args) {
d8d40bc3 169 printf("Flags not supported: enable CONFIG_BOOTSTD_FULL\n");
2d653f68
SG
170 return CMD_RET_USAGE;
171 }
172 boot = true;
173 }
174
175 std->cur_bootflow = NULL;
176
177 flags = 0;
178 if (list)
4f806f31 179 flags |= BOOTFLOWIF_SHOW;
2d653f68 180 if (all)
4f806f31 181 flags |= BOOTFLOWIF_ALL;
2b80bc1e 182 if (no_global)
4f806f31 183 flags |= BOOTFLOWIF_SKIP_GLOBAL;
d73420e4 184 if (!no_hunter)
4f806f31 185 flags |= BOOTFLOWIF_HUNT;
2d653f68
SG
186
187 /*
188 * If we have a device, just scan for bootflows attached to that device
189 */
91943ff7
SG
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)
2d653f68 201 bootdev_clear_bootflows(dev);
91943ff7 202 else
2d653f68 203 bootstd_clear_glob();
91943ff7 204 for (i = 0,
4b7cb058 205 ret = bootflow_scan_first(dev, label, &iter, flags, &bflow);
91943ff7
SG
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;
2d653f68 215 }
91943ff7
SG
216 if (list)
217 show_bootflow(i, &bflow, errors);
a4bee0b4 218 if (!menu && boot && !bflow.err)
91943ff7 219 bootflow_run_boot(&iter, &bflow);
2d653f68
SG
220 }
221 bootflow_iter_uninit(&iter);
222 if (list)
223 show_footer(i, num_valid);
224
a4bee0b4
SG
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 }
f9fb57c6 244
2d653f68
SG
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;
d42243fe
SG
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 }
2d653f68
SG
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;
cbb607d2 364 bool x86_setup = false;
2d653f68
SG
365 bool dump = false;
366 int ret;
367
cbb607d2 368 if (argc > 1 && *argv[1] == '-') {
2d653f68 369 dump = strchr(argv[1], 'd');
cbb607d2
SG
370 x86_setup = strchr(argv[1], 's');
371 }
2d653f68
SG
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
cbb607d2
SG
383 if (IS_ENABLED(CONFIG_X86) && x86_setup) {
384 zimage_dump(bflow->x86_setup, false);
385
386 return 0;
387 }
388
2d653f68
SG
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);
2175e76a 399 printf("OS: %s\n", bflow->os_name ? bflow->os_name : "(none)");
f4a91655
SG
400 printf("Cmdline: ");
401 if (bflow->cmdline)
402 puts(bflow->cmdline);
403 else
404 puts("(none)");
405 putc('\n');
43b6fa9c
SG
406 if (bflow->x86_setup)
407 printf("X86 setup: %p\n", bflow->x86_setup);
24d8e1b3
SG
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 }
7638c851
SG
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 }
2d653f68
SG
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
c279224e
SG
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
2d653f68
SG
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}
02d929bf
SG
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
e0dda26c
TR
503 if (!IS_ENABLED(CONFIG_EXPO)) {
504 printf("Menu not supported\n");
505 return CMD_RET_FAILURE;
506 }
507
02d929bf
SG
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
3fa53b95
SG
515 ret = bootflow_handle_menu(std, text_mode, &bflow);
516 if (ret)
517 return CMD_RET_FAILURE;
02d929bf
SG
518
519 return 0;
520}
82c0938f
SG
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') {
921f63e5 546 val = argv[3] ?: (const char *)BOOTFLOWCL_EMPTY;
82c0938f
SG
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;
33ebcb46
SG
562 case 'a': /* auto */
563 ret = bootflow_cmdline_auto(bflow, arg);
564 break;
82c0938f
SG
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}
2d653f68
SG
588#endif /* CONFIG_CMD_BOOTFLOW_FULL */
589
3616218b 590U_BOOT_LONGHELP(bootflow,
2d653f68 591#ifdef CONFIG_CMD_BOOTFLOW_FULL
2b80bc1e 592 "scan [-abeGl] [bdev] - scan for valid bootflows (-l list, -a all, -e errors, -b boot, -G no global)\n"
2d653f68
SG
593 "bootflow list [-e] - list scanned bootflows (-e errors)\n"
594 "bootflow select [<num>|<name>] - select a bootflow\n"
cbb607d2 595 "bootflow info [-ds] - show info on current bootflow (-d dump bootflow)\n"
c279224e
SG
596 "bootflow read - read all current-bootflow files\n"
597 "bootflow boot - boot current bootflow\n"
82c0938f 598 "bootflow menu [-t] - show a menu of available bootflows\n"
3616218b 599 "bootflow cmdline [set|get|clear|delete|auto] <param> [<value>] - update cmdline"
2d653f68 600#else
3616218b 601 "scan - boot first available bootflow\n"
2d653f68 602#endif
3616218b 603 );
2d653f68
SG
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),
c279224e 611 U_BOOT_SUBCMD_MKENT(read, 1, 1, do_bootflow_read),
02d929bf
SG
612 U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot),
613 U_BOOT_SUBCMD_MKENT(menu, 2, 1, do_bootflow_menu),
82c0938f 614 U_BOOT_SUBCMD_MKENT(cmdline, 4, 1, do_bootflow_cmdline),
2d653f68
SG
615#endif
616);