Merge tag 'dm-pull-26jul22' of https://gitlab.denx.de/u-boot/custodians/u-boot-dm.git
[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=%d\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                dev_get_uclass_name(dev_get_parent(bflow->dev)), bflow->part,
73                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;
97         struct bootflow bflow;
98         bool all = false, boot = false, errors = false, list = false;
99         int num_valid = 0;
100         bool has_args;
101         int ret, i;
102         int flags;
103
104         ret = bootstd_get_priv(&std);
105         if (ret)
106                 return CMD_RET_FAILURE;
107         dev = std->cur_bootdev;
108
109         has_args = argc > 1 && *argv[1] == '-';
110         if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL)) {
111                 if (has_args) {
112                         all = strchr(argv[1], 'a');
113                         boot = strchr(argv[1], 'b');
114                         errors = strchr(argv[1], 'e');
115                         list = strchr(argv[1], 'l');
116                         argc--;
117                         argv++;
118                 }
119                 if (argc > 1) {
120                         const char *label = argv[1];
121
122                         if (bootdev_find_by_any(label, &dev))
123                                 return CMD_RET_FAILURE;
124                 }
125         } else {
126                 if (has_args) {
127                         printf("Flags not supported: enable CONFIG_BOOTFLOW_FULL\n");
128                         return CMD_RET_USAGE;
129                 }
130                 boot = true;
131         }
132
133         std->cur_bootflow = NULL;
134
135         flags = 0;
136         if (list)
137                 flags |= BOOTFLOWF_SHOW;
138         if (all)
139                 flags |= BOOTFLOWF_ALL;
140
141         /*
142          * If we have a device, just scan for bootflows attached to that device
143          */
144         if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL) && dev) {
145                 if (list) {
146                         printf("Scanning for bootflows in bootdev '%s'\n",
147                                dev->name);
148                         show_header();
149                 }
150                 bootdev_clear_bootflows(dev);
151                 for (i = 0,
152                      ret = bootflow_scan_bootdev(dev, &iter, flags, &bflow);
153                      i < 1000 && ret != -ENODEV;
154                      i++, ret = bootflow_scan_next(&iter, &bflow)) {
155                         bflow.err = ret;
156                         if (!ret)
157                                 num_valid++;
158                         ret = bootdev_add_bootflow(&bflow);
159                         if (ret) {
160                                 printf("Out of memory\n");
161                                 return CMD_RET_FAILURE;
162                         }
163                         if (list)
164                                 show_bootflow(i, &bflow, errors);
165                         if (boot && !bflow.err)
166                                 bootflow_run_boot(&iter, &bflow);
167                 }
168         } else {
169                 if (list) {
170                         printf("Scanning for bootflows in all bootdevs\n");
171                         show_header();
172                 }
173                 bootstd_clear_glob();
174
175                 for (i = 0,
176                      ret = bootflow_scan_first(&iter, flags, &bflow);
177                      i < 1000 && ret != -ENODEV;
178                      i++, ret = bootflow_scan_next(&iter, &bflow)) {
179                         bflow.err = ret;
180                         if (!ret)
181                                 num_valid++;
182                         ret = bootdev_add_bootflow(&bflow);
183                         if (ret) {
184                                 printf("Out of memory\n");
185                                 return CMD_RET_FAILURE;
186                         }
187                         if (list)
188                                 show_bootflow(i, &bflow, errors);
189                         if (boot && !bflow.err)
190                                 bootflow_run_boot(&iter, &bflow);
191                 }
192         }
193         bootflow_iter_uninit(&iter);
194         if (list)
195                 show_footer(i, num_valid);
196
197         return 0;
198 }
199
200 #ifdef CONFIG_CMD_BOOTFLOW_FULL
201 static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc,
202                             char *const argv[])
203 {
204         struct bootstd_priv *std;
205         struct udevice *dev;
206         struct bootflow *bflow;
207         int num_valid = 0;
208         bool errors = false;
209         int ret, i;
210
211         if (argc > 1 && *argv[1] == '-')
212                 errors = strchr(argv[1], 'e');
213
214         ret = bootstd_get_priv(&std);
215         if (ret)
216                 return CMD_RET_FAILURE;
217         dev = std->cur_bootdev;
218
219         /* If we have a device, just list bootflows attached to that device */
220         if (dev) {
221                 printf("Showing bootflows for bootdev '%s'\n", dev->name);
222                 show_header();
223                 for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
224                      !ret;
225                      ret = bootdev_next_bootflow(&bflow), i++) {
226                         num_valid += bflow->state == BOOTFLOWST_READY;
227                         show_bootflow(i, bflow, errors);
228                 }
229         } else {
230                 printf("Showing all bootflows\n");
231                 show_header();
232                 for (ret = bootflow_first_glob(&bflow), i = 0;
233                      !ret;
234                      ret = bootflow_next_glob(&bflow), i++) {
235                         num_valid += bflow->state == BOOTFLOWST_READY;
236                         show_bootflow(i, bflow, errors);
237                 }
238         }
239         show_footer(i, num_valid);
240
241         return 0;
242 }
243
244 static int do_bootflow_select(struct cmd_tbl *cmdtp, int flag, int argc,
245                               char *const argv[])
246 {
247         struct bootstd_priv *std;
248         struct bootflow *bflow, *found;
249         struct udevice *dev;
250         const char *name;
251         char *endp;
252         int seq, i;
253         int ret;
254
255         ret = bootstd_get_priv(&std);
256         if (ret)
257                 return CMD_RET_FAILURE;
258 ;
259         if (argc < 2) {
260                 std->cur_bootflow = NULL;
261                 return 0;
262         }
263         dev = std->cur_bootdev;
264
265         name = argv[1];
266         seq = simple_strtol(name, &endp, 16);
267         found = NULL;
268
269         /*
270          * If we have a bootdev device, only allow selection of bootflows
271          * attached to that device
272          */
273         if (dev) {
274                 for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
275                      !ret;
276                      ret = bootdev_next_bootflow(&bflow), i++) {
277                         if (*endp ? !strcmp(bflow->name, name) : i == seq) {
278                                 found = bflow;
279                                 break;
280                         }
281                 }
282         } else {
283                 for (ret = bootflow_first_glob(&bflow), i = 0;
284                      !ret;
285                      ret = bootflow_next_glob(&bflow), i++) {
286                         if (*endp ? !strcmp(bflow->name, name) : i == seq) {
287                                 found = bflow;
288                                 break;
289                         }
290                 }
291         }
292
293         if (!found) {
294                 printf("Cannot find bootflow '%s' ", name);
295                 if (dev)
296                         printf("in bootdev '%s' ", dev->name);
297                 printf("(err=%d)\n", ret);
298                 return CMD_RET_FAILURE;
299         }
300         std->cur_bootflow = found;
301
302         return 0;
303 }
304
305 static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc,
306                             char *const argv[])
307 {
308         struct bootstd_priv *std;
309         struct bootflow *bflow;
310         bool dump = false;
311         int ret;
312
313         if (argc > 1 && *argv[1] == '-')
314                 dump = strchr(argv[1], 'd');
315
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
326         printf("Name:      %s\n", bflow->name);
327         printf("Device:    %s\n", bflow->dev->name);
328         printf("Block dev: %s\n", bflow->blk ? bflow->blk->name : "(none)");
329         printf("Method:    %s\n", bflow->method->name);
330         printf("State:     %s\n", bootflow_state_get_name(bflow->state));
331         printf("Partition: %d\n", bflow->part);
332         printf("Subdir:    %s\n", bflow->subdir ? bflow->subdir : "(none)");
333         printf("Filename:  %s\n", bflow->fname);
334         printf("Buffer:    %lx\n", (ulong)map_to_sysmem(bflow->buf));
335         printf("Size:      %x (%d bytes)\n", bflow->size, bflow->size);
336         printf("Error:     %d\n", bflow->err);
337         if (dump && bflow->buf) {
338                 /* Set some sort of maximum on the size */
339                 int size = min(bflow->size, 10 << 10);
340                 int i;
341
342                 printf("Contents:\n\n");
343                 for (i = 0; i < size; i++) {
344                         putc(bflow->buf[i]);
345                         if (!(i % 128) && ctrlc()) {
346                                 printf("...interrupted\n");
347                                 break;
348                         }
349                 }
350         }
351
352         return 0;
353 }
354
355 static int do_bootflow_boot(struct cmd_tbl *cmdtp, int flag, int argc,
356                             char *const argv[])
357 {
358         struct bootstd_priv *std;
359         struct bootflow *bflow;
360         int ret;
361
362         ret = bootstd_get_priv(&std);
363         if (ret)
364                 return CMD_RET_FAILURE;
365
366         /*
367          * Require a current bootflow. Users can use 'bootflow scan -b' to
368          * automatically scan and boot, if needed.
369          */
370         if (!std->cur_bootflow) {
371                 printf("No bootflow selected\n");
372                 return CMD_RET_FAILURE;
373         }
374         bflow = std->cur_bootflow;
375         ret = bootflow_run_boot(NULL, bflow);
376         if (ret)
377                 return CMD_RET_FAILURE;
378
379         return 0;
380 }
381 #endif /* CONFIG_CMD_BOOTFLOW_FULL */
382
383 #ifdef CONFIG_SYS_LONGHELP
384 static char bootflow_help_text[] =
385 #ifdef CONFIG_CMD_BOOTFLOW_FULL
386         "scan [-abel] [bdev]   - scan for valid bootflows (-l list, -a all, -e errors, -b boot)\n"
387         "bootflow list [-e]             - list scanned bootflows (-e errors)\n"
388         "bootflow select [<num>|<name>] - select a bootflow\n"
389         "bootflow info [-d]             - show info on current bootflow (-d dump bootflow)\n"
390         "bootflow boot                  - boot current bootflow (or first available if none selected)";
391 #else
392         "scan - boot first available bootflow\n";
393 #endif
394 #endif /* CONFIG_SYS_LONGHELP */
395
396 U_BOOT_CMD_WITH_SUBCMDS(bootflow, "Boot flows", bootflow_help_text,
397         U_BOOT_SUBCMD_MKENT(scan, 3, 1, do_bootflow_scan),
398 #ifdef CONFIG_CMD_BOOTFLOW_FULL
399         U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootflow_list),
400         U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootflow_select),
401         U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootflow_info),
402         U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot)
403 #endif
404 );