blob: cfe3422698968bb049492c53580fb06ac7b9a74d [file] [log] [blame]
Simon Glass2d653f62022-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 Glasseccb25c2022-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 Glass2d653f62022-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;
Simon Glass91943ff2023-01-17 10:48:15 -070096 struct udevice *dev = NULL;
Simon Glass2d653f62022-04-24 23:31:11 -060097 struct bootflow bflow;
Simon Glass2b80bc12022-07-30 15:52:25 -060098 bool all = false, boot = false, errors = false, no_global = false;
Simon Glassd73420e2023-01-17 10:48:06 -070099 bool list = false, no_hunter = false;
Simon Glass2d653f62022-04-24 23:31:11 -0600100 int num_valid = 0;
Simon Glass91943ff2023-01-17 10:48:15 -0700101 const char *label = NULL;
Simon Glass2d653f62022-04-24 23:31:11 -0600102 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;
Simon Glass2d653f62022-04-24 23:31:11 -0600109
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 Glass2b80bc12022-07-30 15:52:25 -0600116 no_global = strchr(argv[1], 'G');
Simon Glass2d653f62022-04-24 23:31:11 -0600117 list = strchr(argv[1], 'l');
Simon Glassd73420e2023-01-17 10:48:06 -0700118 no_hunter = strchr(argv[1], 'H');
Simon Glass2d653f62022-04-24 23:31:11 -0600119 argc--;
120 argv++;
121 }
Simon Glass91943ff2023-01-17 10:48:15 -0700122 if (argc > 1)
123 label = argv[1];
124 if (!label)
125 dev = std->cur_bootdev;
Simon Glass2d653f62022-04-24 23:31:11 -0600126 } 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)
Simon Glass4f806f32023-02-22 12:17:03 -0700138 flags |= BOOTFLOWIF_SHOW;
Simon Glass2d653f62022-04-24 23:31:11 -0600139 if (all)
Simon Glass4f806f32023-02-22 12:17:03 -0700140 flags |= BOOTFLOWIF_ALL;
Simon Glass2b80bc12022-07-30 15:52:25 -0600141 if (no_global)
Simon Glass4f806f32023-02-22 12:17:03 -0700142 flags |= BOOTFLOWIF_SKIP_GLOBAL;
Simon Glassd73420e2023-01-17 10:48:06 -0700143 if (!no_hunter)
Simon Glass4f806f32023-02-22 12:17:03 -0700144 flags |= BOOTFLOWIF_HUNT;
Simon Glass2d653f62022-04-24 23:31:11 -0600145
146 /*
147 * If we have a device, just scan for bootflows attached to that device
148 */
Simon Glass91943ff2023-01-17 10:48:15 -0700149 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)
Simon Glass2d653f62022-04-24 23:31:11 -0600160 bootdev_clear_bootflows(dev);
Simon Glass91943ff2023-01-17 10:48:15 -0700161 else
Simon Glass2d653f62022-04-24 23:31:11 -0600162 bootstd_clear_glob();
Simon Glass91943ff2023-01-17 10:48:15 -0700163 for (i = 0,
Simon Glass4b7cb052023-01-17 10:48:16 -0700164 ret = bootflow_scan_first(dev, label, &iter, flags, &bflow);
Simon Glass91943ff2023-01-17 10:48:15 -0700165 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;
Simon Glass2d653f62022-04-24 23:31:11 -0600174 }
Simon Glass91943ff2023-01-17 10:48:15 -0700175 if (list)
176 show_bootflow(i, &bflow, errors);
177 if (boot && !bflow.err)
178 bootflow_run_boot(&iter, &bflow);
Simon Glass2d653f62022-04-24 23:31:11 -0600179 }
180 bootflow_iter_uninit(&iter);
181 if (list)
182 show_footer(i, num_valid);
183
Simon Glassf9fb57c2023-04-24 13:49:48 +1200184 if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL) && !num_valid && !list)
185 printf("No bootflows found; try again with -l\n");
186
Simon Glass2d653f62022-04-24 23:31:11 -0600187 return 0;
188}
189
190#ifdef CONFIG_CMD_BOOTFLOW_FULL
191static 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
234static 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
292 return 0;
293}
294
295static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc,
296 char *const argv[])
297{
298 struct bootstd_priv *std;
299 struct bootflow *bflow;
300 bool dump = false;
301 int ret;
302
303 if (argc > 1 && *argv[1] == '-')
304 dump = strchr(argv[1], 'd');
305
306 ret = bootstd_get_priv(&std);
307 if (ret)
308 return CMD_RET_FAILURE;
309
310 if (!std->cur_bootflow) {
311 printf("No bootflow selected\n");
312 return CMD_RET_FAILURE;
313 }
314 bflow = std->cur_bootflow;
315
316 printf("Name: %s\n", bflow->name);
317 printf("Device: %s\n", bflow->dev->name);
318 printf("Block dev: %s\n", bflow->blk ? bflow->blk->name : "(none)");
319 printf("Method: %s\n", bflow->method->name);
320 printf("State: %s\n", bootflow_state_get_name(bflow->state));
321 printf("Partition: %d\n", bflow->part);
322 printf("Subdir: %s\n", bflow->subdir ? bflow->subdir : "(none)");
323 printf("Filename: %s\n", bflow->fname);
324 printf("Buffer: %lx\n", (ulong)map_to_sysmem(bflow->buf));
325 printf("Size: %x (%d bytes)\n", bflow->size, bflow->size);
Simon Glass2175e762023-01-06 08:52:33 -0600326 printf("OS: %s\n", bflow->os_name ? bflow->os_name : "(none)");
Simon Glass24d8e1b2023-01-06 08:52:34 -0600327 printf("Logo: %s\n", bflow->logo ?
328 simple_xtoa((ulong)map_to_sysmem(bflow->logo)) : "(none)");
329 if (bflow->logo) {
330 printf("Logo size: %x (%d bytes)\n", bflow->logo_size,
331 bflow->logo_size);
332 }
Simon Glass7638c852023-01-17 10:47:56 -0700333 printf("FDT: %s\n", bflow->fdt_fname);
334 if (bflow->fdt_fname) {
335 printf("FDT size: %x (%d bytes)\n", bflow->fdt_size,
336 bflow->fdt_size);
337 printf("FDT addr: %lx\n", bflow->fdt_addr);
338 }
Simon Glass2d653f62022-04-24 23:31:11 -0600339 printf("Error: %d\n", bflow->err);
340 if (dump && bflow->buf) {
341 /* Set some sort of maximum on the size */
342 int size = min(bflow->size, 10 << 10);
343 int i;
344
345 printf("Contents:\n\n");
346 for (i = 0; i < size; i++) {
347 putc(bflow->buf[i]);
348 if (!(i % 128) && ctrlc()) {
349 printf("...interrupted\n");
350 break;
351 }
352 }
353 }
354
355 return 0;
356}
357
358static int do_bootflow_boot(struct cmd_tbl *cmdtp, int flag, int argc,
359 char *const argv[])
360{
361 struct bootstd_priv *std;
362 struct bootflow *bflow;
363 int ret;
364
365 ret = bootstd_get_priv(&std);
366 if (ret)
367 return CMD_RET_FAILURE;
368
369 /*
370 * Require a current bootflow. Users can use 'bootflow scan -b' to
371 * automatically scan and boot, if needed.
372 */
373 if (!std->cur_bootflow) {
374 printf("No bootflow selected\n");
375 return CMD_RET_FAILURE;
376 }
377 bflow = std->cur_bootflow;
378 ret = bootflow_run_boot(NULL, bflow);
379 if (ret)
380 return CMD_RET_FAILURE;
381
382 return 0;
383}
Simon Glass02d929b2023-01-06 08:52:40 -0600384
385static int do_bootflow_menu(struct cmd_tbl *cmdtp, int flag, int argc,
386 char *const argv[])
387{
388 struct bootstd_priv *std;
389 struct bootflow *bflow;
390 bool text_mode = false;
391 int ret;
392
Tom Rinie0dda262023-04-06 10:03:33 -0400393 if (!IS_ENABLED(CONFIG_EXPO)) {
394 printf("Menu not supported\n");
395 return CMD_RET_FAILURE;
396 }
397
Simon Glass02d929b2023-01-06 08:52:40 -0600398 if (argc > 1 && *argv[1] == '-')
399 text_mode = strchr(argv[1], 't');
400
401 ret = bootstd_get_priv(&std);
402 if (ret)
403 return CMD_RET_FAILURE;
404
Tom Rinie0dda262023-04-06 10:03:33 -0400405 ret = bootflow_menu_run(std, text_mode, &bflow);
406 if (ret) {
407 if (ret == -EAGAIN)
408 printf("Nothing chosen\n");
409 else {
410 printf("Menu failed (err=%d)\n", ret);
411 return CMD_RET_FAILURE;
Simon Glass0041b1c2023-01-28 15:00:18 -0700412 }
Simon Glass02d929b2023-01-06 08:52:40 -0600413 }
414
415 printf("Selected: %s\n", bflow->os_name ? bflow->os_name : bflow->name);
416 std->cur_bootflow = bflow;
417
418 return 0;
419}
Simon Glass2d653f62022-04-24 23:31:11 -0600420#endif /* CONFIG_CMD_BOOTFLOW_FULL */
421
422#ifdef CONFIG_SYS_LONGHELP
423static char bootflow_help_text[] =
424#ifdef CONFIG_CMD_BOOTFLOW_FULL
Simon Glass2b80bc12022-07-30 15:52:25 -0600425 "scan [-abeGl] [bdev] - scan for valid bootflows (-l list, -a all, -e errors, -b boot, -G no global)\n"
Simon Glass2d653f62022-04-24 23:31:11 -0600426 "bootflow list [-e] - list scanned bootflows (-e errors)\n"
427 "bootflow select [<num>|<name>] - select a bootflow\n"
428 "bootflow info [-d] - show info on current bootflow (-d dump bootflow)\n"
Simon Glass02d929b2023-01-06 08:52:40 -0600429 "bootflow boot - boot current bootflow (or first available if none selected)\n"
430 "bootflow menu [-t] - show a menu of available bootflows";
Simon Glass2d653f62022-04-24 23:31:11 -0600431#else
432 "scan - boot first available bootflow\n";
433#endif
434#endif /* CONFIG_SYS_LONGHELP */
435
436U_BOOT_CMD_WITH_SUBCMDS(bootflow, "Boot flows", bootflow_help_text,
437 U_BOOT_SUBCMD_MKENT(scan, 3, 1, do_bootflow_scan),
438#ifdef CONFIG_CMD_BOOTFLOW_FULL
439 U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootflow_list),
440 U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootflow_select),
441 U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootflow_info),
Simon Glass02d929b2023-01-06 08:52:40 -0600442 U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot),
443 U_BOOT_SUBCMD_MKENT(menu, 2, 1, do_bootflow_menu),
Simon Glass2d653f62022-04-24 23:31:11 -0600444#endif
445);