cmd: bootefi: allocate device-tree copy from high memory
[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 = 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_BOOTFLOW_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         return 0;
185 }
186
187 #ifdef CONFIG_CMD_BOOTFLOW_FULL
188 static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc,
189                             char *const argv[])
190 {
191         struct bootstd_priv *std;
192         struct udevice *dev;
193         struct bootflow *bflow;
194         int num_valid = 0;
195         bool errors = false;
196         int ret, i;
197
198         if (argc > 1 && *argv[1] == '-')
199                 errors = strchr(argv[1], 'e');
200
201         ret = bootstd_get_priv(&std);
202         if (ret)
203                 return CMD_RET_FAILURE;
204         dev = std->cur_bootdev;
205
206         /* If we have a device, just list bootflows attached to that device */
207         if (dev) {
208                 printf("Showing bootflows for bootdev '%s'\n", dev->name);
209                 show_header();
210                 for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
211                      !ret;
212                      ret = bootdev_next_bootflow(&bflow), i++) {
213                         num_valid += bflow->state == BOOTFLOWST_READY;
214                         show_bootflow(i, bflow, errors);
215                 }
216         } else {
217                 printf("Showing all bootflows\n");
218                 show_header();
219                 for (ret = bootflow_first_glob(&bflow), i = 0;
220                      !ret;
221                      ret = bootflow_next_glob(&bflow), i++) {
222                         num_valid += bflow->state == BOOTFLOWST_READY;
223                         show_bootflow(i, bflow, errors);
224                 }
225         }
226         show_footer(i, num_valid);
227
228         return 0;
229 }
230
231 static int do_bootflow_select(struct cmd_tbl *cmdtp, int flag, int argc,
232                               char *const argv[])
233 {
234         struct bootstd_priv *std;
235         struct bootflow *bflow, *found;
236         struct udevice *dev;
237         const char *name;
238         char *endp;
239         int seq, i;
240         int ret;
241
242         ret = bootstd_get_priv(&std);
243         if (ret)
244                 return CMD_RET_FAILURE;
245 ;
246         if (argc < 2) {
247                 std->cur_bootflow = NULL;
248                 return 0;
249         }
250         dev = std->cur_bootdev;
251
252         name = argv[1];
253         seq = simple_strtol(name, &endp, 16);
254         found = NULL;
255
256         /*
257          * If we have a bootdev device, only allow selection of bootflows
258          * attached to that device
259          */
260         if (dev) {
261                 for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
262                      !ret;
263                      ret = bootdev_next_bootflow(&bflow), i++) {
264                         if (*endp ? !strcmp(bflow->name, name) : i == seq) {
265                                 found = bflow;
266                                 break;
267                         }
268                 }
269         } else {
270                 for (ret = bootflow_first_glob(&bflow), i = 0;
271                      !ret;
272                      ret = bootflow_next_glob(&bflow), i++) {
273                         if (*endp ? !strcmp(bflow->name, name) : i == seq) {
274                                 found = bflow;
275                                 break;
276                         }
277                 }
278         }
279
280         if (!found) {
281                 printf("Cannot find bootflow '%s' ", name);
282                 if (dev)
283                         printf("in bootdev '%s' ", dev->name);
284                 printf("(err=%d)\n", ret);
285                 return CMD_RET_FAILURE;
286         }
287         std->cur_bootflow = found;
288
289         return 0;
290 }
291
292 static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc,
293                             char *const argv[])
294 {
295         struct bootstd_priv *std;
296         struct bootflow *bflow;
297         bool dump = false;
298         int ret;
299
300         if (argc > 1 && *argv[1] == '-')
301                 dump = strchr(argv[1], 'd');
302
303         ret = bootstd_get_priv(&std);
304         if (ret)
305                 return CMD_RET_FAILURE;
306
307         if (!std->cur_bootflow) {
308                 printf("No bootflow selected\n");
309                 return CMD_RET_FAILURE;
310         }
311         bflow = std->cur_bootflow;
312
313         printf("Name:      %s\n", bflow->name);
314         printf("Device:    %s\n", bflow->dev->name);
315         printf("Block dev: %s\n", bflow->blk ? bflow->blk->name : "(none)");
316         printf("Method:    %s\n", bflow->method->name);
317         printf("State:     %s\n", bootflow_state_get_name(bflow->state));
318         printf("Partition: %d\n", bflow->part);
319         printf("Subdir:    %s\n", bflow->subdir ? bflow->subdir : "(none)");
320         printf("Filename:  %s\n", bflow->fname);
321         printf("Buffer:    %lx\n", (ulong)map_to_sysmem(bflow->buf));
322         printf("Size:      %x (%d bytes)\n", bflow->size, bflow->size);
323         printf("OS:        %s\n", bflow->os_name ? bflow->os_name : "(none)");
324         printf("Logo:      %s\n", bflow->logo ?
325                simple_xtoa((ulong)map_to_sysmem(bflow->logo)) : "(none)");
326         if (bflow->logo) {
327                 printf("Logo size: %x (%d bytes)\n", bflow->logo_size,
328                        bflow->logo_size);
329         }
330         printf("FDT:       %s\n", bflow->fdt_fname);
331         if (bflow->fdt_fname) {
332                 printf("FDT size:  %x (%d bytes)\n", bflow->fdt_size,
333                        bflow->fdt_size);
334                 printf("FDT addr:  %lx\n", bflow->fdt_addr);
335         }
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
382 static int do_bootflow_menu(struct cmd_tbl *cmdtp, int flag, int argc,
383                             char *const argv[])
384 {
385         struct bootstd_priv *std;
386         struct bootflow *bflow;
387         bool text_mode = false;
388         int ret;
389
390         if (argc > 1 && *argv[1] == '-')
391                 text_mode = strchr(argv[1], 't');
392
393         ret = bootstd_get_priv(&std);
394         if (ret)
395                 return CMD_RET_FAILURE;
396
397         if (IS_ENABLED(CONFIG_EXPO)) {
398                 ret = bootflow_menu_run(std, text_mode, &bflow);
399                 if (ret) {
400                         if (ret == -EAGAIN)
401                                 printf("Nothing chosen\n");
402                         else
403                                 printf("Menu failed (err=%d)\n", ret);
404                 }
405         } else {
406                 printf("Menu not supported\n");
407                 ret = -ENOSYS;
408         }
409         if (ret)
410                 return CMD_RET_FAILURE;
411
412         printf("Selected: %s\n", bflow->os_name ? bflow->os_name : bflow->name);
413         std->cur_bootflow = bflow;
414
415         return 0;
416 }
417 #endif /* CONFIG_CMD_BOOTFLOW_FULL */
418
419 #ifdef CONFIG_SYS_LONGHELP
420 static char bootflow_help_text[] =
421 #ifdef CONFIG_CMD_BOOTFLOW_FULL
422         "scan [-abeGl] [bdev]  - scan for valid bootflows (-l list, -a all, -e errors, -b boot, -G no global)\n"
423         "bootflow list [-e]             - list scanned bootflows (-e errors)\n"
424         "bootflow select [<num>|<name>] - select a bootflow\n"
425         "bootflow info [-d]             - show info on current bootflow (-d dump bootflow)\n"
426         "bootflow boot                  - boot current bootflow (or first available if none selected)\n"
427         "bootflow menu [-t]             - show a menu of available bootflows";
428 #else
429         "scan - boot first available bootflow\n";
430 #endif
431 #endif /* CONFIG_SYS_LONGHELP */
432
433 U_BOOT_CMD_WITH_SUBCMDS(bootflow, "Boot flows", bootflow_help_text,
434         U_BOOT_SUBCMD_MKENT(scan, 3, 1, do_bootflow_scan),
435 #ifdef CONFIG_CMD_BOOTFLOW_FULL
436         U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootflow_list),
437         U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootflow_select),
438         U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootflow_info),
439         U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot),
440         U_BOOT_SUBCMD_MKENT(menu, 2, 1, do_bootflow_menu),
441 #endif
442 );