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  */
report_bootflow_err(struct bootflow * bflow,int err)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=%dE\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  */
show_bootflow(int index,struct bootflow * bflow,bool errors)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 
show_header(void)78 static void show_header(void)
79 {
80 	printf("Seq  Method       State   Uclass    Part  Name                      Filename\n");
81 	printf("---  -----------  ------  --------  ----  ------------------------  ----------------\n");
82 }
83 
show_footer(int count,int num_valid)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 
do_bootflow_scan(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])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_BOOTSTD_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 	if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL) && !num_valid && !list)
185 		printf("No bootflows found; try again with -l\n");
186 
187 	return 0;
188 }
189 
190 #ifdef CONFIG_CMD_BOOTFLOW_FULL
do_bootflow_list(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])191 static 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 
do_bootflow_select(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])234 static 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 
do_bootflow_info(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])295 static 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);
326 	printf("OS:        %s\n", bflow->os_name ? bflow->os_name : "(none)");
327 	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 	}
333 	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 	}
339 	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 
do_bootflow_boot(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])358 static 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 }
384 
do_bootflow_menu(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])385 static 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 
393 	if (!IS_ENABLED(CONFIG_EXPO)) {
394 		printf("Menu not supported\n");
395 		return CMD_RET_FAILURE;
396 	}
397 
398 	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 
405 	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;
412 		}
413 	}
414 
415 	printf("Selected: %s\n", bflow->os_name ? bflow->os_name : bflow->name);
416 	std->cur_bootflow = bflow;
417 
418 	return 0;
419 }
420 #endif /* CONFIG_CMD_BOOTFLOW_FULL */
421 
422 #ifdef CONFIG_SYS_LONGHELP
423 static char bootflow_help_text[] =
424 #ifdef CONFIG_CMD_BOOTFLOW_FULL
425 	"scan [-abeGl] [bdev]  - scan for valid bootflows (-l list, -a all, -e errors, -b boot, -G no global)\n"
426 	"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"
429 	"bootflow boot                  - boot current bootflow (or first available if none selected)\n"
430 	"bootflow menu [-t]             - show a menu of available bootflows";
431 #else
432 	"scan - boot first available bootflow\n";
433 #endif
434 #endif /* CONFIG_SYS_LONGHELP */
435 
436 U_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),
442 	U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot),
443 	U_BOOT_SUBCMD_MKENT(menu, 2, 1, do_bootflow_menu),
444 #endif
445 );
446