blob: ad39ebe4379f31a2635e04e118e958e3ec66cc56 [file] [log] [blame]
Simon Glass2d653f62022-04-24 23:31:11 -06001// 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>
Simon Glasscbb607d2023-07-30 11:17:00 -060012#include <bootm.h>
Simon Glass2d653f62022-04-24 23:31:11 -060013#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
Simon Glassc8894342023-05-10 16:34:26 -060059 printf(", err=%dE\n", err);
Simon Glass2d653f62022-04-24 23:31:11 -060060}
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),
Simon Glasseccb25c2022-07-30 15:52:23 -060073 bflow->dev ? dev_get_uclass_name(dev_get_parent(bflow->dev)) :
Simon Glassc3867e22023-08-24 13:55:39 -060074 "(none)", bflow->part, bflow->name, bflow->fname ?: "");
Simon Glass2d653f62022-04-24 23:31:11 -060075 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
92static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc,
93 char *const argv[])
94{
95 struct bootstd_priv *std;
96 struct bootflow_iter iter;
Simon Glass91943ff2023-01-17 10:48:15 -070097 struct udevice *dev = NULL;
Simon Glass2d653f62022-04-24 23:31:11 -060098 struct bootflow bflow;
Simon Glass2b80bc12022-07-30 15:52:25 -060099 bool all = false, boot = false, errors = false, no_global = false;
Simon Glassd73420e2023-01-17 10:48:06 -0700100 bool list = false, no_hunter = false;
Simon Glass2d653f62022-04-24 23:31:11 -0600101 int num_valid = 0;
Simon Glass91943ff2023-01-17 10:48:15 -0700102 const char *label = NULL;
Simon Glass2d653f62022-04-24 23:31:11 -0600103 bool has_args;
104 int ret, i;
105 int flags;
106
107 ret = bootstd_get_priv(&std);
108 if (ret)
109 return CMD_RET_FAILURE;
Simon Glass2d653f62022-04-24 23:31:11 -0600110
111 has_args = argc > 1 && *argv[1] == '-';
112 if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL)) {
113 if (has_args) {
114 all = strchr(argv[1], 'a');
115 boot = strchr(argv[1], 'b');
116 errors = strchr(argv[1], 'e');
Simon Glass2b80bc12022-07-30 15:52:25 -0600117 no_global = strchr(argv[1], 'G');
Simon Glass2d653f62022-04-24 23:31:11 -0600118 list = strchr(argv[1], 'l');
Simon Glassd73420e2023-01-17 10:48:06 -0700119 no_hunter = strchr(argv[1], 'H');
Simon Glass2d653f62022-04-24 23:31:11 -0600120 argc--;
121 argv++;
122 }
Simon Glass91943ff2023-01-17 10:48:15 -0700123 if (argc > 1)
124 label = argv[1];
125 if (!label)
126 dev = std->cur_bootdev;
Simon Glass2d653f62022-04-24 23:31:11 -0600127 } else {
128 if (has_args) {
Simon Glassd8d40bc2023-05-06 08:27:09 -0600129 printf("Flags not supported: enable CONFIG_BOOTSTD_FULL\n");
Simon Glass2d653f62022-04-24 23:31:11 -0600130 return CMD_RET_USAGE;
131 }
132 boot = true;
133 }
134
135 std->cur_bootflow = NULL;
136
137 flags = 0;
138 if (list)
Simon Glass4f806f32023-02-22 12:17:03 -0700139 flags |= BOOTFLOWIF_SHOW;
Simon Glass2d653f62022-04-24 23:31:11 -0600140 if (all)
Simon Glass4f806f32023-02-22 12:17:03 -0700141 flags |= BOOTFLOWIF_ALL;
Simon Glass2b80bc12022-07-30 15:52:25 -0600142 if (no_global)
Simon Glass4f806f32023-02-22 12:17:03 -0700143 flags |= BOOTFLOWIF_SKIP_GLOBAL;
Simon Glassd73420e2023-01-17 10:48:06 -0700144 if (!no_hunter)
Simon Glass4f806f32023-02-22 12:17:03 -0700145 flags |= BOOTFLOWIF_HUNT;
Simon Glass2d653f62022-04-24 23:31:11 -0600146
147 /*
148 * If we have a device, just scan for bootflows attached to that device
149 */
Simon Glass91943ff2023-01-17 10:48:15 -0700150 if (list) {
151 printf("Scanning for bootflows ");
152 if (dev)
153 printf("in bootdev '%s'\n", dev->name);
154 else if (label)
155 printf("with label '%s'\n", label);
156 else
157 printf("in all bootdevs\n");
158 show_header();
159 }
160 if (dev)
Simon Glass2d653f62022-04-24 23:31:11 -0600161 bootdev_clear_bootflows(dev);
Simon Glass91943ff2023-01-17 10:48:15 -0700162 else
Simon Glass2d653f62022-04-24 23:31:11 -0600163 bootstd_clear_glob();
Simon Glass91943ff2023-01-17 10:48:15 -0700164 for (i = 0,
Simon Glass4b7cb052023-01-17 10:48:16 -0700165 ret = bootflow_scan_first(dev, label, &iter, flags, &bflow);
Simon Glass91943ff2023-01-17 10:48:15 -0700166 i < 1000 && ret != -ENODEV;
167 i++, ret = bootflow_scan_next(&iter, &bflow)) {
168 bflow.err = ret;
169 if (!ret)
170 num_valid++;
171 ret = bootdev_add_bootflow(&bflow);
172 if (ret) {
173 printf("Out of memory\n");
174 return CMD_RET_FAILURE;
Simon Glass2d653f62022-04-24 23:31:11 -0600175 }
Simon Glass91943ff2023-01-17 10:48:15 -0700176 if (list)
177 show_bootflow(i, &bflow, errors);
178 if (boot && !bflow.err)
179 bootflow_run_boot(&iter, &bflow);
Simon Glass2d653f62022-04-24 23:31:11 -0600180 }
181 bootflow_iter_uninit(&iter);
182 if (list)
183 show_footer(i, num_valid);
184
Simon Glassf9fb57c2023-04-24 13:49:48 +1200185 if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL) && !num_valid && !list)
186 printf("No bootflows found; try again with -l\n");
187
Simon Glass2d653f62022-04-24 23:31:11 -0600188 return 0;
189}
190
191#ifdef CONFIG_CMD_BOOTFLOW_FULL
192static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc,
193 char *const argv[])
194{
195 struct bootstd_priv *std;
196 struct udevice *dev;
197 struct bootflow *bflow;
198 int num_valid = 0;
199 bool errors = false;
200 int ret, i;
201
202 if (argc > 1 && *argv[1] == '-')
203 errors = strchr(argv[1], 'e');
204
205 ret = bootstd_get_priv(&std);
206 if (ret)
207 return CMD_RET_FAILURE;
208 dev = std->cur_bootdev;
209
210 /* If we have a device, just list bootflows attached to that device */
211 if (dev) {
212 printf("Showing bootflows for bootdev '%s'\n", dev->name);
213 show_header();
214 for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
215 !ret;
216 ret = bootdev_next_bootflow(&bflow), i++) {
217 num_valid += bflow->state == BOOTFLOWST_READY;
218 show_bootflow(i, bflow, errors);
219 }
220 } else {
221 printf("Showing all bootflows\n");
222 show_header();
223 for (ret = bootflow_first_glob(&bflow), i = 0;
224 !ret;
225 ret = bootflow_next_glob(&bflow), i++) {
226 num_valid += bflow->state == BOOTFLOWST_READY;
227 show_bootflow(i, bflow, errors);
228 }
229 }
230 show_footer(i, num_valid);
231
232 return 0;
233}
234
235static int do_bootflow_select(struct cmd_tbl *cmdtp, int flag, int argc,
236 char *const argv[])
237{
238 struct bootstd_priv *std;
239 struct bootflow *bflow, *found;
240 struct udevice *dev;
241 const char *name;
242 char *endp;
243 int seq, i;
244 int ret;
245
246 ret = bootstd_get_priv(&std);
247 if (ret)
248 return CMD_RET_FAILURE;
249;
250 if (argc < 2) {
251 std->cur_bootflow = NULL;
252 return 0;
253 }
254 dev = std->cur_bootdev;
255
256 name = argv[1];
257 seq = simple_strtol(name, &endp, 16);
258 found = NULL;
259
260 /*
261 * If we have a bootdev device, only allow selection of bootflows
262 * attached to that device
263 */
264 if (dev) {
265 for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
266 !ret;
267 ret = bootdev_next_bootflow(&bflow), i++) {
268 if (*endp ? !strcmp(bflow->name, name) : i == seq) {
269 found = bflow;
270 break;
271 }
272 }
273 } else {
274 for (ret = bootflow_first_glob(&bflow), i = 0;
275 !ret;
276 ret = bootflow_next_glob(&bflow), i++) {
277 if (*endp ? !strcmp(bflow->name, name) : i == seq) {
278 found = bflow;
279 break;
280 }
281 }
282 }
283
284 if (!found) {
285 printf("Cannot find bootflow '%s' ", name);
286 if (dev)
287 printf("in bootdev '%s' ", dev->name);
288 printf("(err=%d)\n", ret);
289 return CMD_RET_FAILURE;
290 }
291 std->cur_bootflow = found;
Simon Glassd42243f2023-07-12 09:04:35 -0600292 if (IS_ENABLED(CONFIG_BOOTSTD_FULL)) {
293 if (env_set("bootargs", found->cmdline)) {
294 printf("Cannot set bootargs\n");
295 return CMD_RET_FAILURE;
296 }
297 }
Simon Glass2d653f62022-04-24 23:31:11 -0600298
299 return 0;
300}
301
302static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc,
303 char *const argv[])
304{
305 struct bootstd_priv *std;
306 struct bootflow *bflow;
Simon Glasscbb607d2023-07-30 11:17:00 -0600307 bool x86_setup = false;
Simon Glass2d653f62022-04-24 23:31:11 -0600308 bool dump = false;
309 int ret;
310
Simon Glasscbb607d2023-07-30 11:17:00 -0600311 if (argc > 1 && *argv[1] == '-') {
Simon Glass2d653f62022-04-24 23:31:11 -0600312 dump = strchr(argv[1], 'd');
Simon Glasscbb607d2023-07-30 11:17:00 -0600313 x86_setup = strchr(argv[1], 's');
314 }
Simon Glass2d653f62022-04-24 23:31:11 -0600315
316 ret = bootstd_get_priv(&std);
317 if (ret)
318 return CMD_RET_FAILURE;
319
320 if (!std->cur_bootflow) {
321 printf("No bootflow selected\n");
322 return CMD_RET_FAILURE;
323 }
324 bflow = std->cur_bootflow;
325
Simon Glasscbb607d2023-07-30 11:17:00 -0600326 if (IS_ENABLED(CONFIG_X86) && x86_setup) {
327 zimage_dump(bflow->x86_setup, false);
328
329 return 0;
330 }
331
Simon Glass2d653f62022-04-24 23:31:11 -0600332 printf("Name: %s\n", bflow->name);
333 printf("Device: %s\n", bflow->dev->name);
334 printf("Block dev: %s\n", bflow->blk ? bflow->blk->name : "(none)");
335 printf("Method: %s\n", bflow->method->name);
336 printf("State: %s\n", bootflow_state_get_name(bflow->state));
337 printf("Partition: %d\n", bflow->part);
338 printf("Subdir: %s\n", bflow->subdir ? bflow->subdir : "(none)");
339 printf("Filename: %s\n", bflow->fname);
340 printf("Buffer: %lx\n", (ulong)map_to_sysmem(bflow->buf));
341 printf("Size: %x (%d bytes)\n", bflow->size, bflow->size);
Simon Glass2175e762023-01-06 08:52:33 -0600342 printf("OS: %s\n", bflow->os_name ? bflow->os_name : "(none)");
Simon Glassf4a91652023-07-12 09:04:34 -0600343 printf("Cmdline: ");
344 if (bflow->cmdline)
345 puts(bflow->cmdline);
346 else
347 puts("(none)");
348 putc('\n');
Simon Glass43b6fa92023-07-12 09:04:36 -0600349 if (bflow->x86_setup)
350 printf("X86 setup: %p\n", bflow->x86_setup);
Simon Glass24d8e1b2023-01-06 08:52:34 -0600351 printf("Logo: %s\n", bflow->logo ?
352 simple_xtoa((ulong)map_to_sysmem(bflow->logo)) : "(none)");
353 if (bflow->logo) {
354 printf("Logo size: %x (%d bytes)\n", bflow->logo_size,
355 bflow->logo_size);
356 }
Simon Glass7638c852023-01-17 10:47:56 -0700357 printf("FDT: %s\n", bflow->fdt_fname);
358 if (bflow->fdt_fname) {
359 printf("FDT size: %x (%d bytes)\n", bflow->fdt_size,
360 bflow->fdt_size);
361 printf("FDT addr: %lx\n", bflow->fdt_addr);
362 }
Simon Glass2d653f62022-04-24 23:31:11 -0600363 printf("Error: %d\n", bflow->err);
364 if (dump && bflow->buf) {
365 /* Set some sort of maximum on the size */
366 int size = min(bflow->size, 10 << 10);
367 int i;
368
369 printf("Contents:\n\n");
370 for (i = 0; i < size; i++) {
371 putc(bflow->buf[i]);
372 if (!(i % 128) && ctrlc()) {
373 printf("...interrupted\n");
374 break;
375 }
376 }
377 }
378
379 return 0;
380}
381
Simon Glassc2792242023-08-10 19:33:18 -0600382static int do_bootflow_read(struct cmd_tbl *cmdtp, int flag, int argc,
383 char *const argv[])
384{
385 struct bootstd_priv *std;
386 struct bootflow *bflow;
387 int ret;
388
389 ret = bootstd_get_priv(&std);
390 if (ret)
391 return CMD_RET_FAILURE;
392
393 /*
394 * Require a current bootflow. Users can use 'bootflow scan -b' to
395 * automatically scan and boot, if needed.
396 */
397 if (!std->cur_bootflow) {
398 printf("No bootflow selected\n");
399 return CMD_RET_FAILURE;
400 }
401 bflow = std->cur_bootflow;
402 ret = bootflow_read_all(bflow);
403 if (ret) {
404 printf("Failed: err=%dE\n", ret);
405 return CMD_RET_FAILURE;
406 }
407
408 return 0;
409}
410
Simon Glass2d653f62022-04-24 23:31:11 -0600411static int do_bootflow_boot(struct cmd_tbl *cmdtp, int flag, int argc,
412 char *const argv[])
413{
414 struct bootstd_priv *std;
415 struct bootflow *bflow;
416 int ret;
417
418 ret = bootstd_get_priv(&std);
419 if (ret)
420 return CMD_RET_FAILURE;
421
422 /*
423 * Require a current bootflow. Users can use 'bootflow scan -b' to
424 * automatically scan and boot, if needed.
425 */
426 if (!std->cur_bootflow) {
427 printf("No bootflow selected\n");
428 return CMD_RET_FAILURE;
429 }
430 bflow = std->cur_bootflow;
431 ret = bootflow_run_boot(NULL, bflow);
432 if (ret)
433 return CMD_RET_FAILURE;
434
435 return 0;
436}
Simon Glass02d929b2023-01-06 08:52:40 -0600437
438static int do_bootflow_menu(struct cmd_tbl *cmdtp, int flag, int argc,
439 char *const argv[])
440{
441 struct bootstd_priv *std;
442 struct bootflow *bflow;
443 bool text_mode = false;
444 int ret;
445
Tom Rinie0dda262023-04-06 10:03:33 -0400446 if (!IS_ENABLED(CONFIG_EXPO)) {
447 printf("Menu not supported\n");
448 return CMD_RET_FAILURE;
449 }
450
Simon Glass02d929b2023-01-06 08:52:40 -0600451 if (argc > 1 && *argv[1] == '-')
452 text_mode = strchr(argv[1], 't');
453
454 ret = bootstd_get_priv(&std);
455 if (ret)
456 return CMD_RET_FAILURE;
457
Tom Rinie0dda262023-04-06 10:03:33 -0400458 ret = bootflow_menu_run(std, text_mode, &bflow);
459 if (ret) {
460 if (ret == -EAGAIN)
461 printf("Nothing chosen\n");
462 else {
463 printf("Menu failed (err=%d)\n", ret);
464 return CMD_RET_FAILURE;
Simon Glass0041b1c2023-01-28 15:00:18 -0700465 }
Simon Glass02d929b2023-01-06 08:52:40 -0600466 }
467
468 printf("Selected: %s\n", bflow->os_name ? bflow->os_name : bflow->name);
469 std->cur_bootflow = bflow;
470
471 return 0;
472}
Simon Glass82c09382023-07-12 09:04:39 -0600473
474static int do_bootflow_cmdline(struct cmd_tbl *cmdtp, int flag, int argc,
475 char *const argv[])
476{
477 struct bootstd_priv *std;
478 struct bootflow *bflow;
479 const char *op, *arg, *val = NULL;
480 int ret;
481
482 if (argc < 3)
483 return CMD_RET_USAGE;
484
485 ret = bootstd_get_priv(&std);
486 if (ret)
487 return CMD_RET_FAILURE;
488
489 bflow = std->cur_bootflow;
490 if (!bflow) {
491 printf("No bootflow selected\n");
492 return CMD_RET_FAILURE;
493 }
494
495 op = argv[1];
496 arg = argv[2];
497 if (*op == 's') {
498 if (argc < 4)
499 return CMD_RET_USAGE;
500 val = argv[3];
501 }
502
503 switch (*op) {
504 case 'c': /* clear */
505 val = "";
506 fallthrough;
507 case 's': /* set */
508 case 'd': /* delete */
509 ret = bootflow_cmdline_set_arg(bflow, arg, val, true);
510 break;
511 case 'g': /* get */
512 ret = bootflow_cmdline_get_arg(bflow, arg, &val);
513 if (ret >= 0)
514 printf("%.*s\n", ret, val);
515 break;
Simon Glass33ebcb42023-07-12 09:04:42 -0600516 case 'a': /* auto */
517 ret = bootflow_cmdline_auto(bflow, arg);
518 break;
Simon Glass82c09382023-07-12 09:04:39 -0600519 }
520 switch (ret) {
521 case -E2BIG:
522 printf("Argument too long\n");
523 break;
524 case -ENOENT:
525 printf("Argument not found\n");
526 break;
527 case -EINVAL:
528 printf("Mismatched quotes\n");
529 break;
530 case -EBADF:
531 printf("Value must be quoted\n");
532 break;
533 default:
534 if (ret < 0)
535 printf("Unknown error: %dE\n", ret);
536 }
537 if (ret < 0)
538 return CMD_RET_FAILURE;
539
540 return 0;
541}
Simon Glass2d653f62022-04-24 23:31:11 -0600542#endif /* CONFIG_CMD_BOOTFLOW_FULL */
543
Tom Rini36162182023-10-07 15:13:08 -0400544U_BOOT_LONGHELP(bootflow,
Simon Glass2d653f62022-04-24 23:31:11 -0600545#ifdef CONFIG_CMD_BOOTFLOW_FULL
Simon Glass2b80bc12022-07-30 15:52:25 -0600546 "scan [-abeGl] [bdev] - scan for valid bootflows (-l list, -a all, -e errors, -b boot, -G no global)\n"
Simon Glass2d653f62022-04-24 23:31:11 -0600547 "bootflow list [-e] - list scanned bootflows (-e errors)\n"
548 "bootflow select [<num>|<name>] - select a bootflow\n"
Simon Glasscbb607d2023-07-30 11:17:00 -0600549 "bootflow info [-ds] - show info on current bootflow (-d dump bootflow)\n"
Simon Glassc2792242023-08-10 19:33:18 -0600550 "bootflow read - read all current-bootflow files\n"
551 "bootflow boot - boot current bootflow\n"
Simon Glass82c09382023-07-12 09:04:39 -0600552 "bootflow menu [-t] - show a menu of available bootflows\n"
Tom Rini36162182023-10-07 15:13:08 -0400553 "bootflow cmdline [set|get|clear|delete|auto] <param> [<value>] - update cmdline"
Simon Glass2d653f62022-04-24 23:31:11 -0600554#else
Tom Rini36162182023-10-07 15:13:08 -0400555 "scan - boot first available bootflow\n"
Simon Glass2d653f62022-04-24 23:31:11 -0600556#endif
Tom Rini36162182023-10-07 15:13:08 -0400557 );
Simon Glass2d653f62022-04-24 23:31:11 -0600558
559U_BOOT_CMD_WITH_SUBCMDS(bootflow, "Boot flows", bootflow_help_text,
560 U_BOOT_SUBCMD_MKENT(scan, 3, 1, do_bootflow_scan),
561#ifdef CONFIG_CMD_BOOTFLOW_FULL
562 U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootflow_list),
563 U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootflow_select),
564 U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootflow_info),
Simon Glassc2792242023-08-10 19:33:18 -0600565 U_BOOT_SUBCMD_MKENT(read, 1, 1, do_bootflow_read),
Simon Glass02d929b2023-01-06 08:52:40 -0600566 U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot),
567 U_BOOT_SUBCMD_MKENT(menu, 2, 1, do_bootflow_menu),
Simon Glass82c09382023-07-12 09:04:39 -0600568 U_BOOT_SUBCMD_MKENT(cmdline, 4, 1, do_bootflow_cmdline),
Simon Glass2d653f62022-04-24 23:31:11 -0600569#endif
570);