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