1 /*-
2  * Copyright (c) 2000 Benno Rice <benno@jeamland.net>
3  * Copyright (c) 2000 Stephane Potvin <sepotvin@videotron.ca>
4  * Copyright (c) 2007-2008 Semihalf, Rafal Jaworowski <raj@semihalf.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD: stable/10/sys/boot/uboot/common/main.c 294346 2016-01-19 21:42:19Z ian $");
31 #include <sys/param.h>
32 
33 #include <stand.h>
34 
35 #include "api_public.h"
36 #include "bootstrap.h"
37 #include "glue.h"
38 #include "libuboot.h"
39 
40 #ifndef nitems
41 #define	nitems(x)	(sizeof((x)) / sizeof((x)[0]))
42 #endif
43 
44 struct uboot_devdesc currdev;
45 struct arch_switch archsw;		/* MI/MD interface boundary */
46 int devs_no;
47 
48 uintptr_t uboot_heap_start;
49 uintptr_t uboot_heap_end;
50 
51 struct device_type {
52 	const char *name;
53 	int type;
54 } device_types[] = {
55 	{ "disk", DEV_TYP_STOR },
56 	{ "ide",  DEV_TYP_STOR | DT_STOR_IDE },
57 	{ "mmc",  DEV_TYP_STOR | DT_STOR_MMC },
58 	{ "sata", DEV_TYP_STOR | DT_STOR_SATA },
59 	{ "scsi", DEV_TYP_STOR | DT_STOR_SCSI },
60 	{ "usb",  DEV_TYP_STOR | DT_STOR_USB },
61 	{ "net",  DEV_TYP_NET }
62 };
63 
64 extern char end[];
65 extern char bootprog_name[];
66 extern char bootprog_rev[];
67 extern char bootprog_date[];
68 extern char bootprog_maker[];
69 
70 extern unsigned char _etext[];
71 extern unsigned char _edata[];
72 extern unsigned char __bss_start[];
73 extern unsigned char __sbss_start[];
74 extern unsigned char __sbss_end[];
75 extern unsigned char _end[];
76 
77 #ifdef LOADER_FDT_SUPPORT
78 extern int command_fdt_internal(int argc, char *argv[]);
79 #endif
80 
81 static void
dump_sig(struct api_signature * sig)82 dump_sig(struct api_signature *sig)
83 {
84 #ifdef DEBUG
85 	printf("signature:\n");
86 	printf("  version\t= %d\n", sig->version);
87 	printf("  checksum\t= 0x%08x\n", sig->checksum);
88 	printf("  sc entry\t= 0x%08x\n", sig->syscall);
89 #endif
90 }
91 
92 static void
dump_addr_info(void)93 dump_addr_info(void)
94 {
95 #ifdef DEBUG
96 	printf("\naddresses info:\n");
97 	printf(" _etext (sdata) = 0x%08x\n", (uint32_t)_etext);
98 	printf(" _edata         = 0x%08x\n", (uint32_t)_edata);
99 	printf(" __sbss_start   = 0x%08x\n", (uint32_t)__sbss_start);
100 	printf(" __sbss_end     = 0x%08x\n", (uint32_t)__sbss_end);
101 	printf(" __sbss_start   = 0x%08x\n", (uint32_t)__bss_start);
102 	printf(" _end           = 0x%08x\n", (uint32_t)_end);
103 	printf(" syscall entry  = 0x%08x\n", (uint32_t)syscall_ptr);
104 #endif
105 }
106 
107 static uint64_t
memsize(struct sys_info * si,int flags)108 memsize(struct sys_info *si, int flags)
109 {
110 	uint64_t size;
111 	int i;
112 
113 	size = 0;
114 	for (i = 0; i < si->mr_no; i++)
115 		if (si->mr[i].flags == flags && si->mr[i].size)
116 			size += (si->mr[i].size);
117 
118 	return (size);
119 }
120 
121 static void
meminfo(void)122 meminfo(void)
123 {
124 	uint64_t size;
125 	struct sys_info *si;
126 	int t[3] = { MR_ATTR_DRAM, MR_ATTR_FLASH, MR_ATTR_SRAM };
127 	int i;
128 
129 	if ((si = ub_get_sys_info()) == NULL)
130 		panic("could not retrieve system info");
131 
132 	for (i = 0; i < 3; i++) {
133 		size = memsize(si, t[i]);
134 		if (size > 0)
135 			printf("%s: %lldMB\n", ub_mem_type(t[i]),
136 			    size / 1024 / 1024);
137 	}
138 }
139 
140 static const char *
get_device_type(const char * devstr,int * devtype)141 get_device_type(const char *devstr, int *devtype)
142 {
143 	int i;
144 	int namelen;
145 	struct device_type *dt;
146 
147 	if (devstr) {
148 		for (i = 0; i < nitems(device_types); i++) {
149 			dt = &device_types[i];
150 			namelen = strlen(dt->name);
151 			if (strncmp(dt->name, devstr, namelen) == 0) {
152 				*devtype = dt->type;
153 				return (devstr + namelen);
154 			}
155 		}
156 		printf("Unknown device type '%s'\n", devstr);
157 	}
158 
159 	*devtype = -1;
160 	return (NULL);
161 }
162 
163 static const char *
device_typename(int type)164 device_typename(int type)
165 {
166 	int i;
167 
168 	for (i = 0; i < nitems(device_types); i++)
169 		if (device_types[i].type == type)
170 			return (device_types[i].name);
171 
172 	return ("<unknown>");
173 }
174 
175 /*
176  * Parse a device string into type, unit, slice and partition numbers. A
177  * returned value of -1 for type indicates a search should be done for the
178  * first loadable device, otherwise a returned value of -1 for unit
179  * indicates a search should be done for the first loadable device of the
180  * given type.
181  *
182  * The returned values for slice and partition are interpreted by
183  * disk_open().
184  *
185  * Valid device strings:                     For device types:
186  *
187  * <type_name>                               DEV_TYP_STOR, DEV_TYP_NET
188  * <type_name><unit>                         DEV_TYP_STOR, DEV_TYP_NET
189  * <type_name><unit>:                        DEV_TYP_STOR, DEV_TYP_NET
190  * <type_name><unit>:<slice>                 DEV_TYP_STOR
191  * <type_name><unit>:<slice>.                DEV_TYP_STOR
192  * <type_name><unit>:<slice>.<partition>     DEV_TYP_STOR
193  *
194  * For valid type names, see the device_types array, above.
195  *
196  * Slice numbers are 1-based.  0 is a wildcard.
197  */
198 static void
get_load_device(int * type,int * unit,int * slice,int * partition)199 get_load_device(int *type, int *unit, int *slice, int *partition)
200 {
201 	char *devstr;
202 	const char *p;
203 	char *endp;
204 
205 	*type = -1;
206 	*unit = -1;
207 	*slice = 0;
208 	*partition = -1;
209 
210 	devstr = ub_env_get("loaderdev");
211 	if (devstr == NULL) {
212 		printf("U-Boot env: loaderdev not set, will probe all devices.\n");
213 		return;
214 	}
215 	printf("U-Boot env: loaderdev='%s'\n", devstr);
216 
217 	p = get_device_type(devstr, type);
218 
219 	/* Ignore optional spaces after the device name. */
220 	while (*p == ' ')
221 		p++;
222 
223 	/* Unknown device name, or a known name without unit number.  */
224 	if ((*type == -1) || (*p == '\0')) {
225 		return;
226 	}
227 
228 	/* Malformed unit number. */
229 	if (!isdigit(*p)) {
230 		*type = -1;
231 		return;
232 	}
233 
234 	/* Guaranteed to extract a number from the string, as *p is a digit. */
235 	*unit = strtol(p, &endp, 10);
236 	p = endp;
237 
238 	/* Known device name with unit number and nothing else. */
239 	if (*p == '\0') {
240 		return;
241 	}
242 
243 	/* Device string is malformed beyond unit number. */
244 	if (*p != ':') {
245 		*type = -1;
246 		*unit = -1;
247 		return;
248 	}
249 
250 	p++;
251 
252 	/* No slice and partition specification. */
253 	if ('\0' == *p )
254 		return;
255 
256 	/* Only DEV_TYP_STOR devices can have a slice specification. */
257 	if (!(*type & DEV_TYP_STOR)) {
258 		*type = -1;
259 		*unit = -1;
260 		return;
261 	}
262 
263 	*slice = strtoul(p, &endp, 10);
264 
265 	/* Malformed slice number. */
266 	if (p == endp) {
267 		*type = -1;
268 		*unit = -1;
269 		*slice = 0;
270 		return;
271 	}
272 
273 	p = endp;
274 
275 	/* No partition specification. */
276 	if (*p == '\0')
277 		return;
278 
279 	/* Device string is malformed beyond slice number. */
280 	if (*p != '.') {
281 		*type = -1;
282 		*unit = -1;
283 		*slice = 0;
284 		return;
285 	}
286 
287 	p++;
288 
289 	/* No partition specification. */
290 	if (*p == '\0')
291 		return;
292 
293 	*partition = strtol(p, &endp, 10);
294 	p = endp;
295 
296 	/*  Full, valid device string. */
297 	if (*endp == '\0')
298 		return;
299 
300 	/* Junk beyond partition number. */
301 	*type = -1;
302 	*unit = -1;
303 	*slice = 0;
304 	*partition = -1;
305 }
306 
307 static void
print_disk_probe_info()308 print_disk_probe_info()
309 {
310 	char slice[32];
311 	char partition[32];
312 
313 	if (currdev.d_disk.slice > 0)
314 		sprintf(slice, "%d", currdev.d_disk.slice);
315 	else
316 		strcpy(slice, "<auto>");
317 
318 	if (currdev.d_disk.partition >= 0)
319 		sprintf(partition, "%d", currdev.d_disk.partition);
320 	else
321 		strcpy(partition, "<auto>");
322 
323 	printf("  Checking unit=%d slice=%s partition=%s...",
324 	    currdev.d_unit, slice, partition);
325 
326 }
327 
328 static int
probe_disks(int devidx,int load_type,int load_unit,int load_slice,int load_partition)329 probe_disks(int devidx, int load_type, int load_unit, int load_slice,
330     int load_partition)
331 {
332 	int open_result, unit;
333 	struct open_file f;
334 
335 	currdev.d_disk.slice = load_slice;
336 	currdev.d_disk.partition = load_partition;
337 
338 	f.f_devdata = &currdev;
339 	open_result = -1;
340 
341 	if (load_type == -1) {
342 		printf("  Probing all disk devices...\n");
343 		/* Try each disk in succession until one works.  */
344 		for (currdev.d_unit = 0; currdev.d_unit < UB_MAX_DEV;
345 		     currdev.d_unit++) {
346 			print_disk_probe_info();
347 			open_result = devsw[devidx]->dv_open(&f, &currdev);
348 			if (open_result == 0) {
349 				printf(" good.\n");
350 				return (0);
351 			}
352 			printf("\n");
353 		}
354 		return (-1);
355 	}
356 
357 	if (load_unit == -1) {
358 		printf("  Probing all %s devices...\n", device_typename(load_type));
359 		/* Try each disk of given type in succession until one works. */
360 		for (unit = 0; unit < UB_MAX_DEV; unit++) {
361 			currdev.d_unit = uboot_diskgetunit(load_type, unit);
362 			if (currdev.d_unit == -1)
363 				break;
364 			print_disk_probe_info();
365 			open_result = devsw[devidx]->dv_open(&f, &currdev);
366 			if (open_result == 0) {
367 				printf(" good.\n");
368 				return (0);
369 			}
370 			printf("\n");
371 		}
372 		return (-1);
373 	}
374 
375 	if ((currdev.d_unit = uboot_diskgetunit(load_type, load_unit)) != -1) {
376 		print_disk_probe_info();
377 		open_result = devsw[devidx]->dv_open(&f,&currdev);
378 		if (open_result == 0) {
379 			printf(" good.\n");
380 			return (0);
381 		}
382 		printf("\n");
383 	}
384 
385 	printf("  Requested disk type/unit/slice/partition not found\n");
386 	return (-1);
387 }
388 
389 int
main(void)390 main(void)
391 {
392 	struct api_signature *sig = NULL;
393 	int load_type, load_unit, load_slice, load_partition;
394 	int i;
395 	const char *ldev;
396 
397 	/*
398 	 * If we can't find the magic signature and related info, exit with a
399 	 * unique error code that U-Boot reports as "## Application terminated,
400 	 * rc = 0xnnbadab1". Hopefully 'badab1' looks enough like "bad api" to
401 	 * provide a clue. It's better than 0xffffffff anyway.
402 	 */
403 	if (!api_search_sig(&sig))
404 		return (0x01badab1);
405 
406 	syscall_ptr = sig->syscall;
407 	if (syscall_ptr == NULL)
408 		return (0x02badab1);
409 
410 	if (sig->version > API_SIG_VERSION)
411 		return (0x03badab1);
412 
413         /* Clear BSS sections */
414 	bzero(__sbss_start, __sbss_end - __sbss_start);
415 	bzero(__bss_start, _end - __bss_start);
416 
417 	/*
418 	 * Initialise the heap as early as possible.  Once this is done,
419 	 * alloc() is usable. The stack is buried inside us, so this is safe.
420 	 */
421 	uboot_heap_start = round_page((uintptr_t)end);
422 	uboot_heap_end   = uboot_heap_start + 512 * 1024;
423 	setheap((void *)uboot_heap_start, (void *)uboot_heap_end);
424 
425 	/*
426 	 * Set up console.
427 	 */
428 	cons_probe();
429 	printf("Compatible U-Boot API signature found @%x\n", (uint32_t)sig);
430 
431 	printf("\n");
432 	printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
433 	printf("(%s, %s)\n", bootprog_maker, bootprog_date);
434 	printf("\n");
435 
436 	dump_sig(sig);
437 	dump_addr_info();
438 
439 	meminfo();
440 
441 	/*
442 	 * Enumerate U-Boot devices
443 	 */
444 	if ((devs_no = ub_dev_enum()) == 0)
445 		panic("no U-Boot devices found");
446 	printf("Number of U-Boot devices: %d\n", devs_no);
447 
448 	get_load_device(&load_type, &load_unit, &load_slice, &load_partition);
449 
450 	/*
451 	 * March through the device switch probing for things.
452 	 */
453 	for (i = 0; devsw[i] != NULL; i++) {
454 
455 		if (devsw[i]->dv_init == NULL)
456 			continue;
457 		if ((devsw[i]->dv_init)() != 0)
458 			continue;
459 
460 		printf("Found U-Boot device: %s\n", devsw[i]->dv_name);
461 
462 		currdev.d_dev = devsw[i];
463 		currdev.d_type = currdev.d_dev->dv_type;
464 		currdev.d_unit = 0;
465 
466 		if ((load_type == -1 || (load_type & DEV_TYP_STOR)) &&
467 		    strcmp(devsw[i]->dv_name, "disk") == 0) {
468 			if (probe_disks(i, load_type, load_unit, load_slice,
469 			    load_partition) == 0)
470 				break;
471 		}
472 
473 		if ((load_type == -1 || (load_type & DEV_TYP_NET)) &&
474 		    strcmp(devsw[i]->dv_name, "net") == 0)
475 			break;
476 	}
477 
478 	/*
479 	 * If we couldn't find a boot device, return an error to u-boot.
480 	 * U-boot may be running a boot script that can try something different
481 	 * so returning an error is better than forcing a reboot.
482 	 */
483 	if (devsw[i] == NULL) {
484 		printf("No boot device found!\n");
485 		return (0xbadef1ce);
486 	}
487 
488 	ldev = uboot_fmtdev(&currdev);
489 	env_setenv("currdev", EV_VOLATILE, ldev, uboot_setcurrdev, env_nounset);
490 	env_setenv("loaddev", EV_VOLATILE, ldev, env_noset, env_nounset);
491 	printf("Booting from %s\n", ldev);
492 
493 	setenv("LINES", "24", 1);		/* optional */
494 	setenv("prompt", "loader>", 1);
495 
496 	archsw.arch_loadaddr = uboot_loadaddr;
497 	archsw.arch_getdev = uboot_getdev;
498 	archsw.arch_copyin = uboot_copyin;
499 	archsw.arch_copyout = uboot_copyout;
500 	archsw.arch_readin = uboot_readin;
501 	archsw.arch_autoload = uboot_autoload;
502 
503 	interact();				/* doesn't return */
504 
505 	return (0);
506 }
507 
508 
509 COMMAND_SET(heap, "heap", "show heap usage", command_heap);
510 static int
command_heap(int argc,char * argv[])511 command_heap(int argc, char *argv[])
512 {
513 
514 	printf("heap base at %p, top at %p, used %d\n", end, sbrk(0),
515 	    sbrk(0) - end);
516 
517 	return (CMD_OK);
518 }
519 
520 COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
521 static int
command_reboot(int argc,char * argv[])522 command_reboot(int argc, char *argv[])
523 {
524 
525 	printf("Resetting...\n");
526 	ub_reset();
527 
528 	printf("Reset failed!\n");
529 	while(1);
530 }
531 
532 COMMAND_SET(devinfo, "devinfo", "show U-Boot devices", command_devinfo);
533 static int
command_devinfo(int argc,char * argv[])534 command_devinfo(int argc, char *argv[])
535 {
536 	int i;
537 
538 	if ((devs_no = ub_dev_enum()) == 0) {
539 		command_errmsg = "no U-Boot devices found!?";
540 		return (CMD_ERROR);
541 	}
542 
543 	printf("U-Boot devices:\n");
544 	for (i = 0; i < devs_no; i++) {
545 		ub_dump_di(i);
546 		printf("\n");
547 	}
548 	return (CMD_OK);
549 }
550 
551 COMMAND_SET(sysinfo, "sysinfo", "show U-Boot system info", command_sysinfo);
552 static int
command_sysinfo(int argc,char * argv[])553 command_sysinfo(int argc, char *argv[])
554 {
555 	struct sys_info *si;
556 
557 	if ((si = ub_get_sys_info()) == NULL) {
558 		command_errmsg = "could not retrieve U-Boot sys info!?";
559 		return (CMD_ERROR);
560 	}
561 
562 	printf("U-Boot system info:\n");
563 	ub_dump_si(si);
564 	return (CMD_OK);
565 }
566 
567 enum ubenv_action {
568 	UBENV_UNKNOWN,
569 	UBENV_SHOW,
570 	UBENV_IMPORT
571 };
572 
573 static void
handle_uboot_env_var(enum ubenv_action action,const char * var)574 handle_uboot_env_var(enum ubenv_action action, const char * var)
575 {
576 	char ldvar[128];
577 	const char *val;
578 	char *wrk;
579 	int len;
580 
581 	/*
582 	 * On an import with the variable name formatted as ldname=ubname,
583 	 * import the uboot variable ubname into the loader variable ldname,
584 	 * otherwise the historical behavior is to import to uboot.ubname.
585 	 */
586 	if (action == UBENV_IMPORT) {
587 		len = strcspn(var, "=");
588 		if (len == 0) {
589 			printf("name cannot start with '=': '%s'\n", var);
590 			return;
591 		}
592 		if (var[len] == 0) {
593 			strcpy(ldvar, "uboot.");
594 			strncat(ldvar, var, sizeof(ldvar) - 7);
595 		} else {
596 			len = MIN(len, sizeof(ldvar) - 1);
597 			strncpy(ldvar, var, len);
598 			ldvar[len] = 0;
599 			var = &var[len + 1];
600 		}
601 	}
602 
603 	/*
604 	 * If the user prepended "uboot." (which is how they usually see these
605 	 * names) strip it off as a convenience.
606 	 */
607 	if (strncmp(var, "uboot.", 6) == 0) {
608 		var = &var[6];
609 	}
610 
611 	/* If there is no variable name left, punt. */
612 	if (var[0] == 0) {
613 		printf("empty variable name\n");
614 		return;
615 	}
616 
617 	val = ub_env_get(var);
618 	if (action == UBENV_SHOW) {
619 		if (val == NULL)
620 			printf("uboot.%s is not set\n", var);
621 		else
622 			printf("uboot.%s=%s\n", var, val);
623 	} else if (action == UBENV_IMPORT) {
624 		if (val != NULL) {
625 			setenv(ldvar, val, 1);
626 		}
627 	}
628 }
629 
630 static int
command_ubenv(int argc,char * argv[])631 command_ubenv(int argc, char *argv[])
632 {
633 	enum ubenv_action action;
634 	const char *var;
635 	int i;
636 
637 	action = UBENV_UNKNOWN;
638 	if (argc > 1) {
639 		if (strcasecmp(argv[1], "import") == 0)
640 			action = UBENV_IMPORT;
641 		else if (strcasecmp(argv[1], "show") == 0)
642 			action = UBENV_SHOW;
643 	}
644 	if (action == UBENV_UNKNOWN) {
645 		command_errmsg = "usage: 'ubenv <import|show> [var ...]";
646 		return (CMD_ERROR);
647 	}
648 
649 	if (argc > 2) {
650 		for (i = 2; i < argc; i++)
651 			handle_uboot_env_var(action, argv[i]);
652 	} else {
653 		var = NULL;
654 		for (;;) {
655 			if ((var = ub_env_enum(var)) == NULL)
656 				break;
657 			handle_uboot_env_var(action, var);
658 		}
659 	}
660 
661 	return (CMD_OK);
662 }
663 COMMAND_SET(ubenv, "ubenv", "show or import U-Boot env vars", command_ubenv);
664 
665 #ifdef LOADER_FDT_SUPPORT
666 /*
667  * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
668  * and declaring it as extern is in contradiction with COMMAND_SET() macro
669  * (which uses static pointer), we're defining wrapper function, which
670  * calls the proper fdt handling routine.
671  */
672 static int
command_fdt(int argc,char * argv[])673 command_fdt(int argc, char *argv[])
674 {
675 
676 	return (command_fdt_internal(argc, argv));
677 }
678 
679 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
680 #endif
681