]> Git Repo - J-u-boot.git/blob - cmd/bootflow.c
sunxi: h616: add Tanix TX1 support
[J-u-boot.git] / cmd / bootflow.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * 'bootflow' command
4  *
5  * Copyright 2021 Google LLC
6  * Written by Simon Glass <[email protected]>
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  */
27 static 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  */
68 static 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
78 static void show_header(void)
79 {
80         printf("Seq  Method       State   Uclass    Part  Name                      Filename\n");
81         printf("---  -----------  ------  --------  ----  ------------------------  ----------------\n");
82 }
83
84 static 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
129 static 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                 bootdev_clear_bootflows(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 = bootdev_add_bootflow(&bflow);
211                 if (ret) {
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
248 static 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
291 static 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
358 static 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:    %lx\n", (ulong)map_to_sysmem(bflow->buf));
397         printf("Size:      %x (%d bytes)\n", bflow->size, bflow->size);
398         printf("OS:        %s\n", bflow->os_name ? bflow->os_name : "(none)");
399         printf("Cmdline:   ");
400         if (bflow->cmdline)
401                 puts(bflow->cmdline);
402         else
403                 puts("(none)");
404         putc('\n');
405         if (bflow->x86_setup)
406                 printf("X86 setup: %p\n", bflow->x86_setup);
407         printf("Logo:      %s\n", bflow->logo ?
408                simple_xtoa((ulong)map_to_sysmem(bflow->logo)) : "(none)");
409         if (bflow->logo) {
410                 printf("Logo size: %x (%d bytes)\n", bflow->logo_size,
411                        bflow->logo_size);
412         }
413         printf("FDT:       %s\n", bflow->fdt_fname);
414         if (bflow->fdt_fname) {
415                 printf("FDT size:  %x (%d bytes)\n", bflow->fdt_size,
416                        bflow->fdt_size);
417                 printf("FDT addr:  %lx\n", bflow->fdt_addr);
418         }
419         printf("Error:     %d\n", bflow->err);
420         if (dump && bflow->buf) {
421                 /* Set some sort of maximum on the size */
422                 int size = min(bflow->size, 10 << 10);
423                 int i;
424
425                 printf("Contents:\n\n");
426                 for (i = 0; i < size; i++) {
427                         putc(bflow->buf[i]);
428                         if (!(i % 128) && ctrlc()) {
429                                 printf("...interrupted\n");
430                                 break;
431                         }
432                 }
433         }
434
435         return 0;
436 }
437
438 static int do_bootflow_read(struct cmd_tbl *cmdtp, int flag, int argc,
439                             char *const argv[])
440 {
441         struct bootstd_priv *std;
442         struct bootflow *bflow;
443         int ret;
444
445         ret = bootstd_get_priv(&std);
446         if (ret)
447                 return CMD_RET_FAILURE;
448
449         /*
450          * Require a current bootflow. Users can use 'bootflow scan -b' to
451          * automatically scan and boot, if needed.
452          */
453         if (!std->cur_bootflow) {
454                 printf("No bootflow selected\n");
455                 return CMD_RET_FAILURE;
456         }
457         bflow = std->cur_bootflow;
458         ret = bootflow_read_all(bflow);
459         if (ret) {
460                 printf("Failed: err=%dE\n", ret);
461                 return CMD_RET_FAILURE;
462         }
463
464         return 0;
465 }
466
467 static int do_bootflow_boot(struct cmd_tbl *cmdtp, int flag, int argc,
468                             char *const argv[])
469 {
470         struct bootstd_priv *std;
471         struct bootflow *bflow;
472         int ret;
473
474         ret = bootstd_get_priv(&std);
475         if (ret)
476                 return CMD_RET_FAILURE;
477
478         /*
479          * Require a current bootflow. Users can use 'bootflow scan -b' to
480          * automatically scan and boot, if needed.
481          */
482         if (!std->cur_bootflow) {
483                 printf("No bootflow selected\n");
484                 return CMD_RET_FAILURE;
485         }
486         bflow = std->cur_bootflow;
487         ret = bootflow_run_boot(NULL, bflow);
488         if (ret)
489                 return CMD_RET_FAILURE;
490
491         return 0;
492 }
493
494 static int do_bootflow_menu(struct cmd_tbl *cmdtp, int flag, int argc,
495                             char *const argv[])
496 {
497         struct bootstd_priv *std;
498         struct bootflow *bflow;
499         bool text_mode = false;
500         int ret;
501
502         if (!IS_ENABLED(CONFIG_EXPO)) {
503                 printf("Menu not supported\n");
504                 return CMD_RET_FAILURE;
505         }
506
507         if (argc > 1 && *argv[1] == '-')
508                 text_mode = strchr(argv[1], 't');
509
510         ret = bootstd_get_priv(&std);
511         if (ret)
512                 return CMD_RET_FAILURE;
513
514         ret = bootflow_handle_menu(std, text_mode, &bflow);
515         if (ret)
516                 return CMD_RET_FAILURE;
517
518         return 0;
519 }
520
521 static int do_bootflow_cmdline(struct cmd_tbl *cmdtp, int flag, int argc,
522                                char *const argv[])
523 {
524         struct bootstd_priv *std;
525         struct bootflow *bflow;
526         const char *op, *arg, *val = NULL;
527         int ret;
528
529         if (argc < 3)
530                 return CMD_RET_USAGE;
531
532         ret = bootstd_get_priv(&std);
533         if (ret)
534                 return CMD_RET_FAILURE;
535
536         bflow = std->cur_bootflow;
537         if (!bflow) {
538                 printf("No bootflow selected\n");
539                 return CMD_RET_FAILURE;
540         }
541
542         op = argv[1];
543         arg = argv[2];
544         if (*op == 's') {
545                 val = argv[3] ?: (const char *)BOOTFLOWCL_EMPTY;
546         }
547
548         switch (*op) {
549         case 'c':       /* clear */
550                 val = "";
551                 fallthrough;
552         case 's':       /* set */
553         case 'd':       /* delete */
554                 ret = bootflow_cmdline_set_arg(bflow, arg, val, true);
555                 break;
556         case 'g':       /* get */
557                 ret = bootflow_cmdline_get_arg(bflow, arg, &val);
558                 if (ret >= 0)
559                         printf("%.*s\n", ret, val);
560                 break;
561         case 'a':       /* auto */
562                 ret = bootflow_cmdline_auto(bflow, arg);
563                 break;
564         }
565         switch (ret) {
566         case -E2BIG:
567                 printf("Argument too long\n");
568                 break;
569         case -ENOENT:
570                 printf("Argument not found\n");
571                 break;
572         case -EINVAL:
573                 printf("Mismatched quotes\n");
574                 break;
575         case -EBADF:
576                 printf("Value must be quoted\n");
577                 break;
578         default:
579                 if (ret < 0)
580                         printf("Unknown error: %dE\n", ret);
581         }
582         if (ret < 0)
583                 return CMD_RET_FAILURE;
584
585         return 0;
586 }
587 #endif /* CONFIG_CMD_BOOTFLOW_FULL */
588
589 U_BOOT_LONGHELP(bootflow,
590 #ifdef CONFIG_CMD_BOOTFLOW_FULL
591         "scan [-abeGl] [bdev]  - scan for valid bootflows (-l list, -a all, -e errors, -b boot, -G no global)\n"
592         "bootflow list [-e]             - list scanned bootflows (-e errors)\n"
593         "bootflow select [<num>|<name>] - select a bootflow\n"
594         "bootflow info [-ds]            - show info on current bootflow (-d dump bootflow)\n"
595         "bootflow read                  - read all current-bootflow files\n"
596         "bootflow boot                  - boot current bootflow\n"
597         "bootflow menu [-t]             - show a menu of available bootflows\n"
598         "bootflow cmdline [set|get|clear|delete|auto] <param> [<value>] - update cmdline"
599 #else
600         "scan - boot first available bootflow\n"
601 #endif
602         );
603
604 U_BOOT_CMD_WITH_SUBCMDS(bootflow, "Boot flows", bootflow_help_text,
605         U_BOOT_SUBCMD_MKENT(scan, 3, 1, do_bootflow_scan),
606 #ifdef CONFIG_CMD_BOOTFLOW_FULL
607         U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootflow_list),
608         U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootflow_select),
609         U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootflow_info),
610         U_BOOT_SUBCMD_MKENT(read, 1, 1, do_bootflow_read),
611         U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot),
612         U_BOOT_SUBCMD_MKENT(menu, 2, 1, do_bootflow_menu),
613         U_BOOT_SUBCMD_MKENT(cmdline, 4, 1, do_bootflow_cmdline),
614 #endif
615 );
This page took 0.062363 seconds and 4 git commands to generate.