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