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