blob: fe58de5fa5908e142788c2da6338d370d37c28cd [file] [log] [blame]
Simon Glass7617f492022-04-24 23:31:11 -06001// 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 */
27static 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 */
68static 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),
Simon Glassdee4d642022-07-30 15:52:23 -060072 bflow->dev ? dev_get_uclass_name(dev_get_parent(bflow->dev)) :
73 "(none)", bflow->part, bflow->name, bflow->fname);
Simon Glass7617f492022-04-24 23:31:11 -060074 if (errors)
75 report_bootflow_err(bflow, bflow->err);
76}
77
78static void show_header(void)
79{
80 printf("Seq Method State Uclass Part Name Filename\n");
81 printf("--- ----------- ------ -------- ---- ------------------------ ----------------\n");
82}
83
84static 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
91static 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;
Simon Glass73fcf512022-07-30 15:52:25 -060098 bool all = false, boot = false, errors = false, no_global = false;
Simon Glassa21e7752023-01-17 10:48:06 -070099 bool list = false, no_hunter = false;
Simon Glass7617f492022-04-24 23:31:11 -0600100 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');
Simon Glass73fcf512022-07-30 15:52:25 -0600116 no_global = strchr(argv[1], 'G');
Simon Glass7617f492022-04-24 23:31:11 -0600117 list = strchr(argv[1], 'l');
Simon Glassa21e7752023-01-17 10:48:06 -0700118 no_hunter = strchr(argv[1], 'H');
Simon Glass7617f492022-04-24 23:31:11 -0600119 argc--;
120 argv++;
121 }
122 if (argc > 1) {
123 const char *label = argv[1];
124
Simon Glasse22fe922023-01-17 10:48:05 -0700125 if (bootdev_find_by_any(label, &dev, NULL))
Simon Glass7617f492022-04-24 23:31:11 -0600126 return CMD_RET_FAILURE;
127 }
128 } else {
129 if (has_args) {
130 printf("Flags not supported: enable CONFIG_BOOTFLOW_FULL\n");
131 return CMD_RET_USAGE;
132 }
133 boot = true;
134 }
135
136 std->cur_bootflow = NULL;
137
138 flags = 0;
139 if (list)
140 flags |= BOOTFLOWF_SHOW;
141 if (all)
142 flags |= BOOTFLOWF_ALL;
Simon Glass73fcf512022-07-30 15:52:25 -0600143 if (no_global)
144 flags |= BOOTFLOWF_SKIP_GLOBAL;
Simon Glassa21e7752023-01-17 10:48:06 -0700145 if (!no_hunter)
146 flags |= BOOTFLOWF_HUNT;
Simon Glass7617f492022-04-24 23:31:11 -0600147
148 /*
149 * If we have a device, just scan for bootflows attached to that device
150 */
151 if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL) && dev) {
152 if (list) {
153 printf("Scanning for bootflows in bootdev '%s'\n",
154 dev->name);
155 show_header();
156 }
157 bootdev_clear_bootflows(dev);
158 for (i = 0,
159 ret = bootflow_scan_bootdev(dev, &iter, flags, &bflow);
160 i < 1000 && ret != -ENODEV;
161 i++, ret = bootflow_scan_next(&iter, &bflow)) {
162 bflow.err = ret;
163 if (!ret)
164 num_valid++;
165 ret = bootdev_add_bootflow(&bflow);
166 if (ret) {
167 printf("Out of memory\n");
168 return CMD_RET_FAILURE;
169 }
170 if (list)
171 show_bootflow(i, &bflow, errors);
172 if (boot && !bflow.err)
173 bootflow_run_boot(&iter, &bflow);
174 }
175 } else {
176 if (list) {
177 printf("Scanning for bootflows in all bootdevs\n");
178 show_header();
179 }
180 bootstd_clear_glob();
181
182 for (i = 0,
183 ret = bootflow_scan_first(&iter, flags, &bflow);
184 i < 1000 && ret != -ENODEV;
185 i++, ret = bootflow_scan_next(&iter, &bflow)) {
186 bflow.err = ret;
187 if (!ret)
188 num_valid++;
189 ret = bootdev_add_bootflow(&bflow);
190 if (ret) {
191 printf("Out of memory\n");
192 return CMD_RET_FAILURE;
193 }
194 if (list)
195 show_bootflow(i, &bflow, errors);
196 if (boot && !bflow.err)
197 bootflow_run_boot(&iter, &bflow);
198 }
199 }
200 bootflow_iter_uninit(&iter);
201 if (list)
202 show_footer(i, num_valid);
203
204 return 0;
205}
206
207#ifdef CONFIG_CMD_BOOTFLOW_FULL
208static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc,
209 char *const argv[])
210{
211 struct bootstd_priv *std;
212 struct udevice *dev;
213 struct bootflow *bflow;
214 int num_valid = 0;
215 bool errors = false;
216 int ret, i;
217
218 if (argc > 1 && *argv[1] == '-')
219 errors = strchr(argv[1], 'e');
220
221 ret = bootstd_get_priv(&std);
222 if (ret)
223 return CMD_RET_FAILURE;
224 dev = std->cur_bootdev;
225
226 /* If we have a device, just list bootflows attached to that device */
227 if (dev) {
228 printf("Showing bootflows for bootdev '%s'\n", dev->name);
229 show_header();
230 for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
231 !ret;
232 ret = bootdev_next_bootflow(&bflow), i++) {
233 num_valid += bflow->state == BOOTFLOWST_READY;
234 show_bootflow(i, bflow, errors);
235 }
236 } else {
237 printf("Showing all bootflows\n");
238 show_header();
239 for (ret = bootflow_first_glob(&bflow), i = 0;
240 !ret;
241 ret = bootflow_next_glob(&bflow), i++) {
242 num_valid += bflow->state == BOOTFLOWST_READY;
243 show_bootflow(i, bflow, errors);
244 }
245 }
246 show_footer(i, num_valid);
247
248 return 0;
249}
250
251static int do_bootflow_select(struct cmd_tbl *cmdtp, int flag, int argc,
252 char *const argv[])
253{
254 struct bootstd_priv *std;
255 struct bootflow *bflow, *found;
256 struct udevice *dev;
257 const char *name;
258 char *endp;
259 int seq, i;
260 int ret;
261
262 ret = bootstd_get_priv(&std);
263 if (ret)
264 return CMD_RET_FAILURE;
265;
266 if (argc < 2) {
267 std->cur_bootflow = NULL;
268 return 0;
269 }
270 dev = std->cur_bootdev;
271
272 name = argv[1];
273 seq = simple_strtol(name, &endp, 16);
274 found = NULL;
275
276 /*
277 * If we have a bootdev device, only allow selection of bootflows
278 * attached to that device
279 */
280 if (dev) {
281 for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
282 !ret;
283 ret = bootdev_next_bootflow(&bflow), i++) {
284 if (*endp ? !strcmp(bflow->name, name) : i == seq) {
285 found = bflow;
286 break;
287 }
288 }
289 } else {
290 for (ret = bootflow_first_glob(&bflow), i = 0;
291 !ret;
292 ret = bootflow_next_glob(&bflow), i++) {
293 if (*endp ? !strcmp(bflow->name, name) : i == seq) {
294 found = bflow;
295 break;
296 }
297 }
298 }
299
300 if (!found) {
301 printf("Cannot find bootflow '%s' ", name);
302 if (dev)
303 printf("in bootdev '%s' ", dev->name);
304 printf("(err=%d)\n", ret);
305 return CMD_RET_FAILURE;
306 }
307 std->cur_bootflow = found;
308
309 return 0;
310}
311
312static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc,
313 char *const argv[])
314{
315 struct bootstd_priv *std;
316 struct bootflow *bflow;
317 bool dump = false;
318 int ret;
319
320 if (argc > 1 && *argv[1] == '-')
321 dump = strchr(argv[1], 'd');
322
323 ret = bootstd_get_priv(&std);
324 if (ret)
325 return CMD_RET_FAILURE;
326
327 if (!std->cur_bootflow) {
328 printf("No bootflow selected\n");
329 return CMD_RET_FAILURE;
330 }
331 bflow = std->cur_bootflow;
332
333 printf("Name: %s\n", bflow->name);
334 printf("Device: %s\n", bflow->dev->name);
335 printf("Block dev: %s\n", bflow->blk ? bflow->blk->name : "(none)");
336 printf("Method: %s\n", bflow->method->name);
337 printf("State: %s\n", bootflow_state_get_name(bflow->state));
338 printf("Partition: %d\n", bflow->part);
339 printf("Subdir: %s\n", bflow->subdir ? bflow->subdir : "(none)");
340 printf("Filename: %s\n", bflow->fname);
341 printf("Buffer: %lx\n", (ulong)map_to_sysmem(bflow->buf));
342 printf("Size: %x (%d bytes)\n", bflow->size, bflow->size);
Simon Glass72b7b192023-01-06 08:52:33 -0600343 printf("OS: %s\n", bflow->os_name ? bflow->os_name : "(none)");
Simon Glass612b9cc2023-01-06 08:52:34 -0600344 printf("Logo: %s\n", bflow->logo ?
345 simple_xtoa((ulong)map_to_sysmem(bflow->logo)) : "(none)");
346 if (bflow->logo) {
347 printf("Logo size: %x (%d bytes)\n", bflow->logo_size,
348 bflow->logo_size);
349 }
Simon Glass7b8c6342023-01-17 10:47:56 -0700350 printf("FDT: %s\n", bflow->fdt_fname);
351 if (bflow->fdt_fname) {
352 printf("FDT size: %x (%d bytes)\n", bflow->fdt_size,
353 bflow->fdt_size);
354 printf("FDT addr: %lx\n", bflow->fdt_addr);
355 }
Simon Glass7617f492022-04-24 23:31:11 -0600356 printf("Error: %d\n", bflow->err);
357 if (dump && bflow->buf) {
358 /* Set some sort of maximum on the size */
359 int size = min(bflow->size, 10 << 10);
360 int i;
361
362 printf("Contents:\n\n");
363 for (i = 0; i < size; i++) {
364 putc(bflow->buf[i]);
365 if (!(i % 128) && ctrlc()) {
366 printf("...interrupted\n");
367 break;
368 }
369 }
370 }
371
372 return 0;
373}
374
375static int do_bootflow_boot(struct cmd_tbl *cmdtp, int flag, int argc,
376 char *const argv[])
377{
378 struct bootstd_priv *std;
379 struct bootflow *bflow;
380 int ret;
381
382 ret = bootstd_get_priv(&std);
383 if (ret)
384 return CMD_RET_FAILURE;
385
386 /*
387 * Require a current bootflow. Users can use 'bootflow scan -b' to
388 * automatically scan and boot, if needed.
389 */
390 if (!std->cur_bootflow) {
391 printf("No bootflow selected\n");
392 return CMD_RET_FAILURE;
393 }
394 bflow = std->cur_bootflow;
395 ret = bootflow_run_boot(NULL, bflow);
396 if (ret)
397 return CMD_RET_FAILURE;
398
399 return 0;
400}
Simon Glass0a2f6a32023-01-06 08:52:40 -0600401
402static int do_bootflow_menu(struct cmd_tbl *cmdtp, int flag, int argc,
403 char *const argv[])
404{
405 struct bootstd_priv *std;
406 struct bootflow *bflow;
407 bool text_mode = false;
408 int ret;
409
410 if (argc > 1 && *argv[1] == '-')
411 text_mode = strchr(argv[1], 't');
412
413 ret = bootstd_get_priv(&std);
414 if (ret)
415 return CMD_RET_FAILURE;
416
417 ret = bootflow_menu_run(std, text_mode, &bflow);
418 if (ret) {
419 if (ret == -EAGAIN)
420 printf("Nothing chosen\n");
421 else
422 printf("Menu failed (err=%d)\n", ret);
423
424 return CMD_RET_FAILURE;
425 }
426
427 printf("Selected: %s\n", bflow->os_name ? bflow->os_name : bflow->name);
428 std->cur_bootflow = bflow;
429
430 return 0;
431}
Simon Glass7617f492022-04-24 23:31:11 -0600432#endif /* CONFIG_CMD_BOOTFLOW_FULL */
433
434#ifdef CONFIG_SYS_LONGHELP
435static char bootflow_help_text[] =
436#ifdef CONFIG_CMD_BOOTFLOW_FULL
Simon Glass73fcf512022-07-30 15:52:25 -0600437 "scan [-abeGl] [bdev] - scan for valid bootflows (-l list, -a all, -e errors, -b boot, -G no global)\n"
Simon Glass7617f492022-04-24 23:31:11 -0600438 "bootflow list [-e] - list scanned bootflows (-e errors)\n"
439 "bootflow select [<num>|<name>] - select a bootflow\n"
440 "bootflow info [-d] - show info on current bootflow (-d dump bootflow)\n"
Simon Glass0a2f6a32023-01-06 08:52:40 -0600441 "bootflow boot - boot current bootflow (or first available if none selected)\n"
442 "bootflow menu [-t] - show a menu of available bootflows";
Simon Glass7617f492022-04-24 23:31:11 -0600443#else
444 "scan - boot first available bootflow\n";
445#endif
446#endif /* CONFIG_SYS_LONGHELP */
447
448U_BOOT_CMD_WITH_SUBCMDS(bootflow, "Boot flows", bootflow_help_text,
449 U_BOOT_SUBCMD_MKENT(scan, 3, 1, do_bootflow_scan),
450#ifdef CONFIG_CMD_BOOTFLOW_FULL
451 U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootflow_list),
452 U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootflow_select),
453 U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootflow_info),
Simon Glass0a2f6a32023-01-06 08:52:40 -0600454 U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot),
455 U_BOOT_SUBCMD_MKENT(menu, 2, 1, do_bootflow_menu),
Simon Glass7617f492022-04-24 23:31:11 -0600456#endif
457);