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/11/stand/uboot/common/main.c 346483 2019-04-21 04:35:49Z kevans $");
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#ifndef HEAP_SIZE
45#define	HEAP_SIZE	(2 * 1024 * 1024)
46#endif
47
48struct uboot_devdesc currdev;
49struct arch_switch archsw;		/* MI/MD interface boundary */
50int devs_no;
51
52uintptr_t uboot_heap_start;
53uintptr_t uboot_heap_end;
54
55struct device_type {
56	const char *name;
57	int type;
58} device_types[] = {
59	{ "disk", DEV_TYP_STOR },
60	{ "ide",  DEV_TYP_STOR | DT_STOR_IDE },
61	{ "mmc",  DEV_TYP_STOR | DT_STOR_MMC },
62	{ "sata", DEV_TYP_STOR | DT_STOR_SATA },
63	{ "scsi", DEV_TYP_STOR | DT_STOR_SCSI },
64	{ "usb",  DEV_TYP_STOR | DT_STOR_USB },
65	{ "net",  DEV_TYP_NET }
66};
67
68extern char end[];
69
70extern unsigned char _etext[];
71extern unsigned char _edata[];
72extern unsigned char __bss_start[];
73extern unsigned char __sbss_start[];
74extern unsigned char __sbss_end[];
75extern unsigned char _end[];
76
77#ifdef LOADER_FDT_SUPPORT
78extern int command_fdt_internal(int argc, char *argv[]);
79#endif
80
81static void
82dump_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
92static void
93dump_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
107static uint64_t
108memsize(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
121static void
122meminfo(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: %juMB\n", ub_mem_type(t[i]),
136			    (uintmax_t)(size / 1024 / 1024));
137	}
138}
139
140static const char *
141get_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 = DEV_TYP_NONE;
160	return (NULL);
161}
162
163static const char *
164device_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 * The device string can be a standard loader(8) disk specifier:
186 *
187 * disk<unit>s<slice>              disk0s1
188 * disk<unit>s<slice><partition>   disk1s2a
189 * disk<unit>p<partition>          disk0p4
190 *
191 * or one of the following formats:
192 *
193 * Valid device strings:                     For device types:
194 *
195 * <type_name>                               DEV_TYP_STOR, DEV_TYP_NET
196 * <type_name><unit>                         DEV_TYP_STOR, DEV_TYP_NET
197 * <type_name><unit>:                        DEV_TYP_STOR, DEV_TYP_NET
198 * <type_name><unit>:<slice>                 DEV_TYP_STOR
199 * <type_name><unit>:<slice>.                DEV_TYP_STOR
200 * <type_name><unit>:<slice>.<partition>     DEV_TYP_STOR
201 *
202 * For valid type names, see the device_types array, above.
203 *
204 * Slice numbers are 1-based.  0 is a wildcard.
205 */
206static void
207get_load_device(int *type, int *unit, int *slice, int *partition)
208{
209	struct disk_devdesc dev;
210	char *devstr;
211	const char *p;
212	char *endp;
213
214	*type = DEV_TYP_NONE;
215	*unit = -1;
216	*slice = 0;
217	*partition = -1;
218
219	devstr = ub_env_get("loaderdev");
220	if (devstr == NULL) {
221		printf("U-Boot env: loaderdev not set, will probe all devices.\n");
222		return;
223	}
224	printf("U-Boot env: loaderdev='%s'\n", devstr);
225
226	p = get_device_type(devstr, type);
227
228	/*
229	 * If type is DEV_TYP_STOR we have a disk-like device.  If the remainder
230	 * of the string contains spaces, dots, or a colon in any location other
231	 * than the last char, it's legacy format.  Otherwise it might be
232	 * standard loader(8) format (e.g., disk0s2a or mmc1p12), so try to
233	 * parse the remainder of the string as such, and if it works, return
234	 * those results. Otherwise we'll fall through to the code that parses
235	 * the legacy format.
236	 */
237	if (*type & DEV_TYP_STOR) {
238		size_t len = strlen(p);
239		if (strcspn(p, " .") == len && strcspn(p, ":") >= len - 1 &&
240		    disk_parsedev(&dev, p, NULL) == 0) {
241			*unit = dev.dd.d_unit;
242			*slice = dev.d_slice;
243			*partition = dev.d_partition;
244			return;
245		}
246	}
247
248	/* Ignore optional spaces after the device name. */
249	while (*p == ' ')
250		p++;
251
252	/* Unknown device name, or a known name without unit number.  */
253	if ((*type == DEV_TYP_NONE) || (*p == '\0')) {
254		return;
255	}
256
257	/* Malformed unit number. */
258	if (!isdigit(*p)) {
259		*type = DEV_TYP_NONE;
260		return;
261	}
262
263	/* Guaranteed to extract a number from the string, as *p is a digit. */
264	*unit = strtol(p, &endp, 10);
265	p = endp;
266
267	/* Known device name with unit number and nothing else. */
268	if (*p == '\0') {
269		return;
270	}
271
272	/* Device string is malformed beyond unit number. */
273	if (*p != ':') {
274		*type = DEV_TYP_NONE;
275		*unit = -1;
276		return;
277	}
278
279	p++;
280
281	/* No slice and partition specification. */
282	if ('\0' == *p )
283		return;
284
285	/* Only DEV_TYP_STOR devices can have a slice specification. */
286	if (!(*type & DEV_TYP_STOR)) {
287		*type = DEV_TYP_NONE;
288		*unit = -1;
289		return;
290	}
291
292	*slice = strtoul(p, &endp, 10);
293
294	/* Malformed slice number. */
295	if (p == endp) {
296		*type = DEV_TYP_NONE;
297		*unit = -1;
298		*slice = 0;
299		return;
300	}
301
302	p = endp;
303
304	/* No partition specification. */
305	if (*p == '\0')
306		return;
307
308	/* Device string is malformed beyond slice number. */
309	if (*p != '.') {
310		*type = DEV_TYP_NONE;
311		*unit = -1;
312		*slice = 0;
313		return;
314	}
315
316	p++;
317
318	/* No partition specification. */
319	if (*p == '\0')
320		return;
321
322	*partition = strtol(p, &endp, 10);
323	p = endp;
324
325	/*  Full, valid device string. */
326	if (*endp == '\0')
327		return;
328
329	/* Junk beyond partition number. */
330	*type = DEV_TYP_NONE;
331	*unit = -1;
332	*slice = 0;
333	*partition = -1;
334}
335
336static void
337print_disk_probe_info()
338{
339	char slice[32];
340	char partition[32];
341
342	if (currdev.d_disk.d_slice > 0)
343		sprintf(slice, "%d", currdev.d_disk.d_slice);
344	else
345		strcpy(slice, "<auto>");
346
347	if (currdev.d_disk.d_partition >= 0)
348		sprintf(partition, "%d", currdev.d_disk.d_partition);
349	else
350		strcpy(partition, "<auto>");
351
352	printf("  Checking unit=%d slice=%s partition=%s...",
353	    currdev.dd.d_unit, slice, partition);
354
355}
356
357static int
358probe_disks(int devidx, int load_type, int load_unit, int load_slice,
359    int load_partition)
360{
361	int open_result, unit;
362	struct open_file f;
363
364	currdev.d_disk.d_slice = load_slice;
365	currdev.d_disk.d_partition = load_partition;
366
367	f.f_devdata = &currdev;
368	open_result = -1;
369
370	if (load_type == -1) {
371		printf("  Probing all disk devices...\n");
372		/* Try each disk in succession until one works.  */
373		for (currdev.dd.d_unit = 0; currdev.dd.d_unit < UB_MAX_DEV;
374		     currdev.dd.d_unit++) {
375			print_disk_probe_info();
376			open_result = devsw[devidx]->dv_open(&f, &currdev);
377			if (open_result == 0) {
378				printf(" good.\n");
379				return (0);
380			}
381			printf("\n");
382		}
383		return (-1);
384	}
385
386	if (load_unit == -1) {
387		printf("  Probing all %s devices...\n", device_typename(load_type));
388		/* Try each disk of given type in succession until one works. */
389		for (unit = 0; unit < UB_MAX_DEV; unit++) {
390			currdev.dd.d_unit = uboot_diskgetunit(load_type, unit);
391			if (currdev.dd.d_unit == -1)
392				break;
393			print_disk_probe_info();
394			open_result = devsw[devidx]->dv_open(&f, &currdev);
395			if (open_result == 0) {
396				printf(" good.\n");
397				return (0);
398			}
399			printf("\n");
400		}
401		return (-1);
402	}
403
404	if ((currdev.dd.d_unit = uboot_diskgetunit(load_type, load_unit)) != -1) {
405		print_disk_probe_info();
406		open_result = devsw[devidx]->dv_open(&f,&currdev);
407		if (open_result == 0) {
408			printf(" good.\n");
409			return (0);
410		}
411		printf("\n");
412	}
413
414	printf("  Requested disk type/unit/slice/partition not found\n");
415	return (-1);
416}
417
418int
419main(int argc, char **argv)
420{
421	struct api_signature *sig = NULL;
422	int load_type, load_unit, load_slice, load_partition;
423	int i;
424	const char *ldev;
425
426	/*
427	 * We first check if a command line argument was passed to us containing
428	 * API's signature address. If it wasn't then we try to search for the
429	 * API signature via the usual hinted address.
430	 * If we can't find the magic signature and related info, exit with a
431	 * unique error code that U-Boot reports as "## Application terminated,
432	 * rc = 0xnnbadab1". Hopefully 'badab1' looks enough like "bad api" to
433	 * provide a clue. It's better than 0xffffffff anyway.
434	 */
435	if (!api_parse_cmdline_sig(argc, argv, &sig) && !api_search_sig(&sig))
436		return (0x01badab1);
437
438	syscall_ptr = sig->syscall;
439	if (syscall_ptr == NULL)
440		return (0x02badab1);
441
442	if (sig->version > API_SIG_VERSION)
443		return (0x03badab1);
444
445        /* Clear BSS sections */
446	bzero(__sbss_start, __sbss_end - __sbss_start);
447	bzero(__bss_start, _end - __bss_start);
448
449	/*
450	 * Initialise the heap as early as possible.  Once this is done,
451	 * alloc() is usable.  We are using the stack u-boot set up near the top
452	 * of physical ram; hopefully there is sufficient space between the end
453	 * of our bss and the bottom of the u-boot stack to avoid overlap.
454	 */
455	uboot_heap_start = round_page((uintptr_t)end);
456	uboot_heap_end   = uboot_heap_start + HEAP_SIZE;
457	setheap((void *)uboot_heap_start, (void *)uboot_heap_end);
458
459	/*
460	 * Set up console.
461	 */
462	cons_probe();
463	printf("Compatible U-Boot API signature found @%p\n", sig);
464
465	printf("\n%s", bootprog_info);
466	printf("\n");
467
468	dump_sig(sig);
469	dump_addr_info();
470
471	meminfo();
472
473	/*
474	 * Enumerate U-Boot devices
475	 */
476	if ((devs_no = ub_dev_enum()) == 0) {
477		printf("no U-Boot devices found");
478		goto do_interact;
479	}
480	printf("Number of U-Boot devices: %d\n", devs_no);
481
482	get_load_device(&load_type, &load_unit, &load_slice, &load_partition);
483
484	/*
485	 * March through the device switch probing for things.
486	 */
487	for (i = 0; devsw[i] != NULL; i++) {
488
489		if (devsw[i]->dv_init == NULL)
490			continue;
491		if ((devsw[i]->dv_init)() != 0)
492			continue;
493
494		printf("Found U-Boot device: %s\n", devsw[i]->dv_name);
495
496		currdev.dd.d_dev = devsw[i];
497		currdev.dd.d_unit = 0;
498
499		if ((load_type == DEV_TYP_NONE || (load_type & DEV_TYP_STOR)) &&
500		    strcmp(devsw[i]->dv_name, "disk") == 0) {
501			if (probe_disks(i, load_type, load_unit, load_slice,
502			    load_partition) == 0)
503				break;
504		}
505
506		if ((load_type == DEV_TYP_NONE || (load_type & DEV_TYP_NET)) &&
507		    strcmp(devsw[i]->dv_name, "net") == 0)
508			break;
509	}
510
511	/*
512	 * If we couldn't find a boot device, return an error to u-boot.
513	 * U-boot may be running a boot script that can try something different
514	 * so returning an error is better than forcing a reboot.
515	 */
516	if (devsw[i] == NULL) {
517		printf("No boot device found!\n");
518		return (0xbadef1ce);
519	}
520
521	ldev = uboot_fmtdev(&currdev);
522	env_setenv("currdev", EV_VOLATILE, ldev, uboot_setcurrdev, env_nounset);
523	env_setenv("loaddev", EV_VOLATILE, ldev, env_noset, env_nounset);
524	printf("Booting from %s\n", ldev);
525
526do_interact:
527	setenv("LINES", "24", 1);		/* optional */
528	setenv("prompt", "loader>", 1);
529
530	archsw.arch_loadaddr = uboot_loadaddr;
531	archsw.arch_getdev = uboot_getdev;
532	archsw.arch_copyin = uboot_copyin;
533	archsw.arch_copyout = uboot_copyout;
534	archsw.arch_readin = uboot_readin;
535	archsw.arch_autoload = uboot_autoload;
536
537	interact();				/* doesn't return */
538
539	return (0);
540}
541
542
543COMMAND_SET(heap, "heap", "show heap usage", command_heap);
544static int
545command_heap(int argc, char *argv[])
546{
547
548	printf("heap base at %p, top at %p, used %td\n", end, sbrk(0),
549	    sbrk(0) - end);
550
551	return (CMD_OK);
552}
553
554COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
555static int
556command_reboot(int argc, char *argv[])
557{
558
559	printf("Resetting...\n");
560	ub_reset();
561
562	printf("Reset failed!\n");
563	while (1);
564	__unreachable();
565}
566
567COMMAND_SET(devinfo, "devinfo", "show U-Boot devices", command_devinfo);
568static int
569command_devinfo(int argc, char *argv[])
570{
571	int i;
572
573	if ((devs_no = ub_dev_enum()) == 0) {
574		command_errmsg = "no U-Boot devices found!?";
575		return (CMD_ERROR);
576	}
577
578	printf("U-Boot devices:\n");
579	for (i = 0; i < devs_no; i++) {
580		ub_dump_di(i);
581		printf("\n");
582	}
583	return (CMD_OK);
584}
585
586COMMAND_SET(sysinfo, "sysinfo", "show U-Boot system info", command_sysinfo);
587static int
588command_sysinfo(int argc, char *argv[])
589{
590	struct sys_info *si;
591
592	if ((si = ub_get_sys_info()) == NULL) {
593		command_errmsg = "could not retrieve U-Boot sys info!?";
594		return (CMD_ERROR);
595	}
596
597	printf("U-Boot system info:\n");
598	ub_dump_si(si);
599	return (CMD_OK);
600}
601
602enum ubenv_action {
603	UBENV_UNKNOWN,
604	UBENV_SHOW,
605	UBENV_IMPORT
606};
607
608static void
609handle_uboot_env_var(enum ubenv_action action, const char * var)
610{
611	char ldvar[128];
612	const char *val;
613	char *wrk;
614	int len;
615
616	/*
617	 * On an import with the variable name formatted as ldname=ubname,
618	 * import the uboot variable ubname into the loader variable ldname,
619	 * otherwise the historical behavior is to import to uboot.ubname.
620	 */
621	if (action == UBENV_IMPORT) {
622		len = strcspn(var, "=");
623		if (len == 0) {
624			printf("name cannot start with '=': '%s'\n", var);
625			return;
626		}
627		if (var[len] == 0) {
628			strcpy(ldvar, "uboot.");
629			strncat(ldvar, var, sizeof(ldvar) - 7);
630		} else {
631			len = MIN(len, sizeof(ldvar) - 1);
632			strncpy(ldvar, var, len);
633			ldvar[len] = 0;
634			var = &var[len + 1];
635		}
636	}
637
638	/*
639	 * If the user prepended "uboot." (which is how they usually see these
640	 * names) strip it off as a convenience.
641	 */
642	if (strncmp(var, "uboot.", 6) == 0) {
643		var = &var[6];
644	}
645
646	/* If there is no variable name left, punt. */
647	if (var[0] == 0) {
648		printf("empty variable name\n");
649		return;
650	}
651
652	val = ub_env_get(var);
653	if (action == UBENV_SHOW) {
654		if (val == NULL)
655			printf("uboot.%s is not set\n", var);
656		else
657			printf("uboot.%s=%s\n", var, val);
658	} else if (action == UBENV_IMPORT) {
659		if (val != NULL) {
660			setenv(ldvar, val, 1);
661		}
662	}
663}
664
665static int
666command_ubenv(int argc, char *argv[])
667{
668	enum ubenv_action action;
669	const char *var;
670	int i;
671
672	action = UBENV_UNKNOWN;
673	if (argc > 1) {
674		if (strcasecmp(argv[1], "import") == 0)
675			action = UBENV_IMPORT;
676		else if (strcasecmp(argv[1], "show") == 0)
677			action = UBENV_SHOW;
678	}
679	if (action == UBENV_UNKNOWN) {
680		command_errmsg = "usage: 'ubenv <import|show> [var ...]";
681		return (CMD_ERROR);
682	}
683
684	if (argc > 2) {
685		for (i = 2; i < argc; i++)
686			handle_uboot_env_var(action, argv[i]);
687	} else {
688		var = NULL;
689		for (;;) {
690			if ((var = ub_env_enum(var)) == NULL)
691				break;
692			handle_uboot_env_var(action, var);
693		}
694	}
695
696	return (CMD_OK);
697}
698COMMAND_SET(ubenv, "ubenv", "show or import U-Boot env vars", command_ubenv);
699
700#ifdef LOADER_FDT_SUPPORT
701/*
702 * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
703 * and declaring it as extern is in contradiction with COMMAND_SET() macro
704 * (which uses static pointer), we're defining wrapper function, which
705 * calls the proper fdt handling routine.
706 */
707static int
708command_fdt(int argc, char *argv[])
709{
710
711	return (command_fdt_internal(argc, argv));
712}
713
714COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
715#endif
716