blob: af4b9c3732371a364a038c40caabef4aeca85b0f [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),
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
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;
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
201static 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
244static 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
305static 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
355static 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
384static 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
396U_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);