1176348Smarcel/*-
2176348Smarcel * Copyright (c) 2000 Benno Rice <benno@jeamland.net>
3176348Smarcel * Copyright (c) 2000 Stephane Potvin <sepotvin@videotron.ca>
4182732Sraj * Copyright (c) 2007-2008 Semihalf, Rafal Jaworowski <raj@semihalf.com>
5176348Smarcel * All rights reserved.
6176348Smarcel *
7176348Smarcel * Redistribution and use in source and binary forms, with or without
8176348Smarcel * modification, are permitted provided that the following conditions
9176348Smarcel * are met:
10176348Smarcel * 1. Redistributions of source code must retain the above copyright
11176348Smarcel *    notice, this list of conditions and the following disclaimer.
12176348Smarcel * 2. Redistributions in binary form must reproduce the above copyright
13176348Smarcel *    notice, this list of conditions and the following disclaimer in the
14176348Smarcel *    documentation and/or other materials provided with the distribution.
15176348Smarcel *
16176348Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17176348Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18176348Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19176348Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20176348Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21176348Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22176348Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23176348Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24176348Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25176348Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26176348Smarcel * SUCH DAMAGE.
27176348Smarcel */
28176348Smarcel
29176348Smarcel#include <sys/cdefs.h>
30176348Smarcel__FBSDID("$FreeBSD$");
31283505Sian#include <sys/param.h>
32176348Smarcel
33176348Smarcel#include <stand.h>
34176482Smarcel
35176482Smarcel#include "api_public.h"
36176348Smarcel#include "bootstrap.h"
37176482Smarcel#include "glue.h"
38176348Smarcel#include "libuboot.h"
39176348Smarcel
40265071Sian#ifndef nitems
41265071Sian#define	nitems(x)	(sizeof((x)) / sizeof((x)[0]))
42265071Sian#endif
43265071Sian
44182732Srajstruct uboot_devdesc currdev;
45182732Srajstruct arch_switch archsw;		/* MI/MD interface boundary */
46182732Srajint devs_no;
47176348Smarcel
48283505Sianuintptr_t uboot_heap_start;
49283505Sianuintptr_t uboot_heap_end;
50283505Sian
51265071Sianstruct device_type {
52265071Sian	const char *name;
53265071Sian	int type;
54265071Sian} device_types[] = {
55265071Sian	{ "disk", DEV_TYP_STOR },
56265071Sian	{ "ide",  DEV_TYP_STOR | DT_STOR_IDE },
57265071Sian	{ "mmc",  DEV_TYP_STOR | DT_STOR_MMC },
58265071Sian	{ "sata", DEV_TYP_STOR | DT_STOR_SATA },
59265071Sian	{ "scsi", DEV_TYP_STOR | DT_STOR_SCSI },
60265071Sian	{ "usb",  DEV_TYP_STOR | DT_STOR_USB },
61265071Sian	{ "net",  DEV_TYP_NET }
62265071Sian};
63265071Sian
64176348Smarcelextern char end[];
65176348Smarcelextern char bootprog_name[];
66176348Smarcelextern char bootprog_rev[];
67176348Smarcelextern char bootprog_date[];
68176348Smarcelextern char bootprog_maker[];
69176348Smarcel
70176348Smarcelextern unsigned char _etext[];
71176348Smarcelextern unsigned char _edata[];
72176348Smarcelextern unsigned char __bss_start[];
73176348Smarcelextern unsigned char __sbss_start[];
74176348Smarcelextern unsigned char __sbss_end[];
75176348Smarcelextern unsigned char _end[];
76176348Smarcel
77208538Sraj#ifdef LOADER_FDT_SUPPORT
78208538Srajextern int command_fdt_internal(int argc, char *argv[]);
79208538Sraj#endif
80208538Sraj
81182732Srajstatic void
82182732Srajdump_sig(struct api_signature *sig)
83176348Smarcel{
84176348Smarcel#ifdef DEBUG
85176348Smarcel	printf("signature:\n");
86176348Smarcel	printf("  version\t= %d\n", sig->version);
87176348Smarcel	printf("  checksum\t= 0x%08x\n", sig->checksum);
88176348Smarcel	printf("  sc entry\t= 0x%08x\n", sig->syscall);
89176348Smarcel#endif
90176348Smarcel}
91182732Sraj
92176348Smarcelstatic void
93176348Smarceldump_addr_info(void)
94176348Smarcel{
95176348Smarcel#ifdef DEBUG
96176348Smarcel	printf("\naddresses info:\n");
97182732Sraj	printf(" _etext (sdata) = 0x%08x\n", (uint32_t)_etext);
98182732Sraj	printf(" _edata         = 0x%08x\n", (uint32_t)_edata);
99182732Sraj	printf(" __sbss_start   = 0x%08x\n", (uint32_t)__sbss_start);
100182732Sraj	printf(" __sbss_end     = 0x%08x\n", (uint32_t)__sbss_end);
101182732Sraj	printf(" __sbss_start   = 0x%08x\n", (uint32_t)__bss_start);
102182732Sraj	printf(" _end           = 0x%08x\n", (uint32_t)_end);
103182732Sraj	printf(" syscall entry  = 0x%08x\n", (uint32_t)syscall_ptr);
104176348Smarcel#endif
105176348Smarcel}
106176348Smarcel
107176348Smarcelstatic uint64_t
108182732Srajmemsize(struct sys_info *si, int flags)
109176348Smarcel{
110182732Sraj	uint64_t size;
111182732Sraj	int i;
112176348Smarcel
113176482Smarcel	size = 0;
114176348Smarcel	for (i = 0; i < si->mr_no; i++)
115176348Smarcel		if (si->mr[i].flags == flags && si->mr[i].size)
116176482Smarcel			size += (si->mr[i].size);
117176348Smarcel
118176482Smarcel	return (size);
119176348Smarcel}
120176348Smarcel
121182732Srajstatic void
122182732Srajmeminfo(void)
123182732Sraj{
124182732Sraj	uint64_t size;
125182732Sraj	struct sys_info *si;
126182732Sraj	int t[3] = { MR_ATTR_DRAM, MR_ATTR_FLASH, MR_ATTR_SRAM };
127182732Sraj	int i;
128182732Sraj
129182732Sraj	if ((si = ub_get_sys_info()) == NULL)
130182732Sraj		panic("could not retrieve system info");
131182732Sraj
132182732Sraj	for (i = 0; i < 3; i++) {
133182732Sraj		size = memsize(si, t[i]);
134182732Sraj		if (size > 0)
135265071Sian			printf("%s: %lldMB\n", ub_mem_type(t[i]),
136182732Sraj			    size / 1024 / 1024);
137182732Sraj	}
138182732Sraj}
139182732Sraj
140265071Sianstatic const char *
141265071Sianget_device_type(const char *devstr, int *devtype)
142265071Sian{
143265071Sian	int i;
144265071Sian	int namelen;
145265071Sian	struct device_type *dt;
146265071Sian
147265071Sian	if (devstr) {
148265071Sian		for (i = 0; i < nitems(device_types); i++) {
149265071Sian			dt = &device_types[i];
150265071Sian			namelen = strlen(dt->name);
151265071Sian			if (strncmp(dt->name, devstr, namelen) == 0) {
152265071Sian				*devtype = dt->type;
153265071Sian				return (devstr + namelen);
154265071Sian			}
155265071Sian		}
156265071Sian		printf("Unknown device type '%s'\n", devstr);
157265071Sian	}
158265071Sian
159265071Sian	*devtype = -1;
160265071Sian	return (NULL);
161265071Sian}
162265071Sian
163265071Sianstatic const char *
164265071Siandevice_typename(int type)
165265071Sian{
166265071Sian	int i;
167265071Sian
168265071Sian	for (i = 0; i < nitems(device_types); i++)
169265071Sian		if (device_types[i].type == type)
170265071Sian			return (device_types[i].name);
171265071Sian
172265071Sian	return ("<unknown>");
173265071Sian}
174265071Sian
175265071Sian/*
176265071Sian * Parse a device string into type, unit, slice and partition numbers. A
177265071Sian * returned value of -1 for type indicates a search should be done for the
178265071Sian * first loadable device, otherwise a returned value of -1 for unit
179265071Sian * indicates a search should be done for the first loadable device of the
180265071Sian * given type.
181265071Sian *
182265071Sian * The returned values for slice and partition are interpreted by
183265071Sian * disk_open().
184265071Sian *
185265071Sian * Valid device strings:                     For device types:
186265071Sian *
187265071Sian * <type_name>                               DEV_TYP_STOR, DEV_TYP_NET
188265071Sian * <type_name><unit>                         DEV_TYP_STOR, DEV_TYP_NET
189265071Sian * <type_name><unit>:                        DEV_TYP_STOR, DEV_TYP_NET
190265071Sian * <type_name><unit>:<slice>                 DEV_TYP_STOR
191265071Sian * <type_name><unit>:<slice>.                DEV_TYP_STOR
192265071Sian * <type_name><unit>:<slice>.<partition>     DEV_TYP_STOR
193265071Sian *
194265071Sian * For valid type names, see the device_types array, above.
195265071Sian *
196265071Sian * Slice numbers are 1-based.  0 is a wildcard.
197265071Sian */
198265071Sianstatic void
199265071Sianget_load_device(int *type, int *unit, int *slice, int *partition)
200265071Sian{
201265071Sian	char *devstr;
202265071Sian	const char *p;
203265071Sian	char *endp;
204265071Sian
205265071Sian	*type = -1;
206265071Sian	*unit = -1;
207265071Sian	*slice = 0;
208265071Sian	*partition = -1;
209265071Sian
210265071Sian	devstr = ub_env_get("loaderdev");
211265071Sian	if (devstr == NULL) {
212265071Sian		printf("U-Boot env: loaderdev not set, will probe all devices.\n");
213265071Sian		return;
214265071Sian	}
215265071Sian	printf("U-Boot env: loaderdev='%s'\n", devstr);
216265071Sian
217265071Sian	p = get_device_type(devstr, type);
218265071Sian
219276289Sian	/* Ignore optional spaces after the device name. */
220276289Sian	while (*p == ' ')
221276289Sian		p++;
222276289Sian
223276289Sian	/* Unknown device name, or a known name without unit number.  */
224265071Sian	if ((*type == -1) || (*p == '\0')) {
225265071Sian		return;
226265071Sian	}
227265071Sian
228265071Sian	/* Malformed unit number. */
229265071Sian	if (!isdigit(*p)) {
230265071Sian		*type = -1;
231265071Sian		return;
232265071Sian	}
233265071Sian
234265071Sian	/* Guaranteed to extract a number from the string, as *p is a digit. */
235265071Sian	*unit = strtol(p, &endp, 10);
236265071Sian	p = endp;
237265071Sian
238265071Sian	/* Known device name with unit number and nothing else. */
239265071Sian	if (*p == '\0') {
240265071Sian		return;
241265071Sian	}
242265071Sian
243265071Sian	/* Device string is malformed beyond unit number. */
244265071Sian	if (*p != ':') {
245265071Sian		*type = -1;
246265071Sian		*unit = -1;
247265071Sian		return;
248265071Sian	}
249265071Sian
250265071Sian	p++;
251265071Sian
252265071Sian	/* No slice and partition specification. */
253265071Sian	if ('\0' == *p )
254265071Sian		return;
255265071Sian
256265071Sian	/* Only DEV_TYP_STOR devices can have a slice specification. */
257265071Sian	if (!(*type & DEV_TYP_STOR)) {
258265071Sian		*type = -1;
259265071Sian		*unit = -1;
260265071Sian		return;
261265071Sian	}
262265071Sian
263265071Sian	*slice = strtoul(p, &endp, 10);
264265071Sian
265265071Sian	/* Malformed slice number. */
266265071Sian	if (p == endp) {
267265071Sian		*type = -1;
268265071Sian		*unit = -1;
269265071Sian		*slice = 0;
270265071Sian		return;
271265071Sian	}
272265071Sian
273265071Sian	p = endp;
274265071Sian
275265071Sian	/* No partition specification. */
276265071Sian	if (*p == '\0')
277265071Sian		return;
278265071Sian
279265071Sian	/* Device string is malformed beyond slice number. */
280265071Sian	if (*p != '.') {
281265071Sian		*type = -1;
282265071Sian		*unit = -1;
283265071Sian		*slice = 0;
284265071Sian		return;
285265071Sian	}
286265071Sian
287265071Sian	p++;
288265071Sian
289265071Sian	/* No partition specification. */
290265071Sian	if (*p == '\0')
291265071Sian		return;
292265071Sian
293265071Sian	*partition = strtol(p, &endp, 10);
294265071Sian	p = endp;
295265071Sian
296265071Sian	/*  Full, valid device string. */
297265071Sian	if (*endp == '\0')
298265071Sian		return;
299265071Sian
300265071Sian	/* Junk beyond partition number. */
301265071Sian	*type = -1;
302265071Sian	*unit = -1;
303265071Sian	*slice = 0;
304265071Sian	*partition = -1;
305265071Sian}
306265071Sian
307265071Sianstatic void
308265071Sianprint_disk_probe_info()
309265071Sian{
310265071Sian	char slice[32];
311265071Sian	char partition[32];
312265071Sian
313265071Sian	if (currdev.d_disk.slice > 0)
314265071Sian		sprintf(slice, "%d", currdev.d_disk.slice);
315265071Sian	else
316265071Sian		strcpy(slice, "<auto>");
317265071Sian
318294341Sian	if (currdev.d_disk.partition >= 0)
319265071Sian		sprintf(partition, "%d", currdev.d_disk.partition);
320265071Sian	else
321265071Sian		strcpy(partition, "<auto>");
322265071Sian
323265071Sian	printf("  Checking unit=%d slice=%s partition=%s...",
324265071Sian	    currdev.d_unit, slice, partition);
325265071Sian
326265071Sian}
327265071Sian
328265071Sianstatic int
329265071Sianprobe_disks(int devidx, int load_type, int load_unit, int load_slice,
330265071Sian    int load_partition)
331265071Sian{
332265071Sian	int open_result, unit;
333265071Sian	struct open_file f;
334265071Sian
335265071Sian	currdev.d_disk.slice = load_slice;
336265071Sian	currdev.d_disk.partition = load_partition;
337265071Sian
338265071Sian	f.f_devdata = &currdev;
339265071Sian	open_result = -1;
340265071Sian
341265071Sian	if (load_type == -1) {
342265071Sian		printf("  Probing all disk devices...\n");
343265071Sian		/* Try each disk in succession until one works.  */
344265071Sian		for (currdev.d_unit = 0; currdev.d_unit < UB_MAX_DEV;
345265071Sian		     currdev.d_unit++) {
346265071Sian			print_disk_probe_info();
347265071Sian			open_result = devsw[devidx]->dv_open(&f, &currdev);
348265071Sian			if (open_result == 0) {
349265071Sian				printf(" good.\n");
350265071Sian				return (0);
351265071Sian			}
352265071Sian			printf("\n");
353265071Sian		}
354265071Sian		return (-1);
355265071Sian	}
356265071Sian
357265071Sian	if (load_unit == -1) {
358265071Sian		printf("  Probing all %s devices...\n", device_typename(load_type));
359265071Sian		/* Try each disk of given type in succession until one works. */
360265071Sian		for (unit = 0; unit < UB_MAX_DEV; unit++) {
361265071Sian			currdev.d_unit = uboot_diskgetunit(load_type, unit);
362265071Sian			if (currdev.d_unit == -1)
363265071Sian				break;
364265071Sian			print_disk_probe_info();
365265071Sian			open_result = devsw[devidx]->dv_open(&f, &currdev);
366265071Sian			if (open_result == 0) {
367265071Sian				printf(" good.\n");
368265071Sian				return (0);
369265071Sian			}
370265071Sian			printf("\n");
371265071Sian		}
372265071Sian		return (-1);
373265071Sian	}
374265071Sian
375265071Sian	if ((currdev.d_unit = uboot_diskgetunit(load_type, load_unit)) != -1) {
376265071Sian		print_disk_probe_info();
377265071Sian		open_result = devsw[devidx]->dv_open(&f,&currdev);
378265071Sian		if (open_result == 0) {
379265071Sian			printf(" good.\n");
380265071Sian			return (0);
381265071Sian		}
382265071Sian		printf("\n");
383265071Sian	}
384265071Sian
385294341Sian	printf("  Requested disk type/unit/slice/partition not found\n");
386265071Sian	return (-1);
387265071Sian}
388265071Sian
389176348Smarcelint
390176348Smarcelmain(void)
391176348Smarcel{
392176348Smarcel	struct api_signature *sig = NULL;
393265071Sian	int load_type, load_unit, load_slice, load_partition;
394265071Sian	int i;
395294341Sian	const char *ldev;
396176348Smarcel
397265070Sian	/*
398265070Sian	 * If we can't find the magic signature and related info, exit with a
399265070Sian	 * unique error code that U-Boot reports as "## Application terminated,
400265070Sian	 * rc = 0xnnbadab1". Hopefully 'badab1' looks enough like "bad api" to
401265070Sian	 * provide a clue. It's better than 0xffffffff anyway.
402265070Sian	 */
403176348Smarcel	if (!api_search_sig(&sig))
404265070Sian		return (0x01badab1);
405176348Smarcel
406176348Smarcel	syscall_ptr = sig->syscall;
407176348Smarcel	if (syscall_ptr == NULL)
408265070Sian		return (0x02badab1);
409176348Smarcel
410176348Smarcel	if (sig->version > API_SIG_VERSION)
411265070Sian		return (0x03badab1);
412176348Smarcel
413176348Smarcel        /* Clear BSS sections */
414176348Smarcel	bzero(__sbss_start, __sbss_end - __sbss_start);
415176348Smarcel	bzero(__bss_start, _end - __bss_start);
416176348Smarcel
417176348Smarcel	/*
418265071Sian	 * Initialise the heap as early as possible.  Once this is done,
419265071Sian	 * alloc() is usable. The stack is buried inside us, so this is safe.
420265071Sian	 */
421283505Sian	uboot_heap_start = round_page((uintptr_t)end);
422283505Sian	uboot_heap_end   = uboot_heap_start + 512 * 1024;
423283505Sian	setheap((void *)uboot_heap_start, (void *)uboot_heap_end);
424265071Sian
425265071Sian	/*
426265071Sian	 * Set up console.
427265071Sian	 */
428176348Smarcel	cons_probe();
429265071Sian	printf("Compatible U-Boot API signature found @%x\n", (uint32_t)sig);
430176348Smarcel
431265071Sian	printf("\n");
432265071Sian	printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
433265071Sian	printf("(%s, %s)\n", bootprog_maker, bootprog_date);
434265071Sian	printf("\n");
435176348Smarcel
436176348Smarcel	dump_sig(sig);
437176348Smarcel	dump_addr_info();
438176348Smarcel
439265071Sian	meminfo();
440176348Smarcel
441176348Smarcel	/*
442176348Smarcel	 * Enumerate U-Boot devices
443176348Smarcel	 */
444176348Smarcel	if ((devs_no = ub_dev_enum()) == 0)
445182732Sraj		panic("no U-Boot devices found");
446182732Sraj	printf("Number of U-Boot devices: %d\n", devs_no);
447176348Smarcel
448265071Sian	get_load_device(&load_type, &load_unit, &load_slice, &load_partition);
449176348Smarcel
450208534Sraj	/*
451265071Sian	 * March through the device switch probing for things.
452208534Sraj	 */
453204316Sraj	for (i = 0; devsw[i] != NULL; i++) {
454204316Sraj
455208534Sraj		if (devsw[i]->dv_init == NULL)
456208534Sraj			continue;
457208534Sraj		if ((devsw[i]->dv_init)() != 0)
458208534Sraj			continue;
459208534Sraj
460265071Sian		printf("Found U-Boot device: %s\n", devsw[i]->dv_name);
461208534Sraj
462265071Sian		currdev.d_dev = devsw[i];
463265071Sian		currdev.d_type = currdev.d_dev->dv_type;
464265071Sian		currdev.d_unit = 0;
465265071Sian
466265071Sian		if ((load_type == -1 || (load_type & DEV_TYP_STOR)) &&
467265071Sian		    strcmp(devsw[i]->dv_name, "disk") == 0) {
468265071Sian			if (probe_disks(i, load_type, load_unit, load_slice,
469265071Sian			    load_partition) == 0)
470265071Sian				break;
471204316Sraj		}
472265071Sian
473265071Sian		if ((load_type == -1 || (load_type & DEV_TYP_NET)) &&
474265071Sian		    strcmp(devsw[i]->dv_name, "net") == 0)
475265071Sian			break;
476204316Sraj	}
477176348Smarcel
478265071Sian	/*
479265071Sian	 * If we couldn't find a boot device, return an error to u-boot.
480265071Sian	 * U-boot may be running a boot script that can try something different
481265071Sian	 * so returning an error is better than forcing a reboot.
482265071Sian	 */
483265071Sian	if (devsw[i] == NULL) {
484265071Sian		printf("No boot device found!\n");
485265071Sian		return (0xbadef1ce);
486265071Sian	}
487176348Smarcel
488294341Sian	ldev = uboot_fmtdev(&currdev);
489294341Sian	env_setenv("currdev", EV_VOLATILE, ldev, uboot_setcurrdev, env_nounset);
490294341Sian	env_setenv("loaddev", EV_VOLATILE, ldev, env_noset, env_nounset);
491294341Sian	printf("Booting from %s\n", ldev);
492176348Smarcel
493176348Smarcel	setenv("LINES", "24", 1);		/* optional */
494176348Smarcel	setenv("prompt", "loader>", 1);
495176348Smarcel
496283505Sian	archsw.arch_loadaddr = uboot_loadaddr;
497176348Smarcel	archsw.arch_getdev = uboot_getdev;
498176348Smarcel	archsw.arch_copyin = uboot_copyin;
499176348Smarcel	archsw.arch_copyout = uboot_copyout;
500176348Smarcel	archsw.arch_readin = uboot_readin;
501176348Smarcel	archsw.arch_autoload = uboot_autoload;
502176348Smarcel
503176348Smarcel	interact();				/* doesn't return */
504176348Smarcel
505182732Sraj	return (0);
506176348Smarcel}
507176348Smarcel
508176348Smarcel
509176348SmarcelCOMMAND_SET(heap, "heap", "show heap usage", command_heap);
510176348Smarcelstatic int
511176348Smarcelcommand_heap(int argc, char *argv[])
512176348Smarcel{
513182723Sraj
514182723Sraj	printf("heap base at %p, top at %p, used %d\n", end, sbrk(0),
515177152Sobrien	    sbrk(0) - end);
516176348Smarcel
517182732Sraj	return (CMD_OK);
518176348Smarcel}
519176348Smarcel
520176348SmarcelCOMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
521176348Smarcelstatic int
522176348Smarcelcommand_reboot(int argc, char *argv[])
523176348Smarcel{
524186231Sraj
525176348Smarcel	printf("Resetting...\n");
526176348Smarcel	ub_reset();
527176348Smarcel
528176348Smarcel	printf("Reset failed!\n");
529176348Smarcel	while(1);
530176348Smarcel}
531182732Sraj
532182732SrajCOMMAND_SET(devinfo, "devinfo", "show U-Boot devices", command_devinfo);
533182732Srajstatic int
534182732Srajcommand_devinfo(int argc, char *argv[])
535182732Sraj{
536182732Sraj	int i;
537182732Sraj
538182732Sraj	if ((devs_no = ub_dev_enum()) == 0) {
539182732Sraj		command_errmsg = "no U-Boot devices found!?";
540182732Sraj		return (CMD_ERROR);
541182732Sraj	}
542182732Sraj
543182732Sraj	printf("U-Boot devices:\n");
544182732Sraj	for (i = 0; i < devs_no; i++) {
545182732Sraj		ub_dump_di(i);
546182732Sraj		printf("\n");
547182732Sraj	}
548182732Sraj	return (CMD_OK);
549182732Sraj}
550182732Sraj
551182732SrajCOMMAND_SET(sysinfo, "sysinfo", "show U-Boot system info", command_sysinfo);
552182732Srajstatic int
553182732Srajcommand_sysinfo(int argc, char *argv[])
554182732Sraj{
555182732Sraj	struct sys_info *si;
556182732Sraj
557182732Sraj	if ((si = ub_get_sys_info()) == NULL) {
558182732Sraj		command_errmsg = "could not retrieve U-Boot sys info!?";
559182732Sraj		return (CMD_ERROR);
560182732Sraj	}
561182732Sraj
562182732Sraj	printf("U-Boot system info:\n");
563182732Sraj	ub_dump_si(si);
564182732Sraj	return (CMD_OK);
565182732Sraj}
566208538Sraj
567273665Sianenum ubenv_action {
568273665Sian	UBENV_UNKNOWN,
569273665Sian	UBENV_SHOW,
570273665Sian	UBENV_IMPORT
571273665Sian};
572273665Sian
573273665Sianstatic void
574273665Sianhandle_uboot_env_var(enum ubenv_action action, const char * var)
575273665Sian{
576294341Sian	char ldvar[128];
577294341Sian	const char *val;
578294341Sian	char *wrk;
579294341Sian	int len;
580273665Sian
581273665Sian	/*
582294341Sian	 * On an import with the variable name formatted as ldname=ubname,
583294341Sian	 * import the uboot variable ubname into the loader variable ldname,
584294341Sian	 * otherwise the historical behavior is to import to uboot.ubname.
585294341Sian	 */
586294341Sian	if (action == UBENV_IMPORT) {
587294341Sian		len = strcspn(var, "=");
588294346Sian		if (len == 0) {
589294346Sian			printf("name cannot start with '=': '%s'\n", var);
590294346Sian			return;
591294346Sian		}
592294341Sian		if (var[len] == 0) {
593294341Sian			strcpy(ldvar, "uboot.");
594294341Sian			strncat(ldvar, var, sizeof(ldvar) - 7);
595294341Sian		} else {
596294341Sian			len = MIN(len, sizeof(ldvar) - 1);
597294341Sian			strncpy(ldvar, var, len);
598294341Sian			ldvar[len] = 0;
599294341Sian			var = &var[len + 1];
600294341Sian		}
601294341Sian	}
602294341Sian
603294341Sian	/*
604273665Sian	 * If the user prepended "uboot." (which is how they usually see these
605273665Sian	 * names) strip it off as a convenience.
606273665Sian	 */
607273665Sian	if (strncmp(var, "uboot.", 6) == 0) {
608294341Sian		var = &var[6];
609273665Sian	}
610294341Sian
611294346Sian	/* If there is no variable name left, punt. */
612294346Sian	if (var[0] == 0) {
613294346Sian		printf("empty variable name\n");
614294341Sian		return;
615294346Sian	}
616294341Sian
617273665Sian	val = ub_env_get(var);
618273665Sian	if (action == UBENV_SHOW) {
619273665Sian		if (val == NULL)
620273665Sian			printf("uboot.%s is not set\n", var);
621273665Sian		else
622273665Sian			printf("uboot.%s=%s\n", var, val);
623273665Sian	} else if (action == UBENV_IMPORT) {
624273665Sian		if (val != NULL) {
625294341Sian			setenv(ldvar, val, 1);
626273665Sian		}
627273665Sian	}
628273665Sian}
629273665Sian
630273665Sianstatic int
631273665Siancommand_ubenv(int argc, char *argv[])
632273665Sian{
633273665Sian	enum ubenv_action action;
634273665Sian	const char *var;
635273665Sian	int i;
636273665Sian
637273665Sian	action = UBENV_UNKNOWN;
638273665Sian	if (argc > 1) {
639273665Sian		if (strcasecmp(argv[1], "import") == 0)
640273665Sian			action = UBENV_IMPORT;
641273665Sian		else if (strcasecmp(argv[1], "show") == 0)
642273665Sian			action = UBENV_SHOW;
643273665Sian	}
644273665Sian	if (action == UBENV_UNKNOWN) {
645273665Sian		command_errmsg = "usage: 'ubenv <import|show> [var ...]";
646273665Sian		return (CMD_ERROR);
647273665Sian	}
648273665Sian
649273665Sian	if (argc > 2) {
650273665Sian		for (i = 2; i < argc; i++)
651273665Sian			handle_uboot_env_var(action, argv[i]);
652273665Sian	} else {
653273665Sian		var = NULL;
654273665Sian		for (;;) {
655273665Sian			if ((var = ub_env_enum(var)) == NULL)
656273665Sian				break;
657273665Sian			handle_uboot_env_var(action, var);
658273665Sian		}
659273665Sian	}
660273665Sian
661273665Sian	return (CMD_OK);
662273665Sian}
663273665SianCOMMAND_SET(ubenv, "ubenv", "show or import U-Boot env vars", command_ubenv);
664273665Sian
665208538Sraj#ifdef LOADER_FDT_SUPPORT
666208538Sraj/*
667208538Sraj * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
668208538Sraj * and declaring it as extern is in contradiction with COMMAND_SET() macro
669208538Sraj * (which uses static pointer), we're defining wrapper function, which
670208538Sraj * calls the proper fdt handling routine.
671208538Sraj */
672208538Srajstatic int
673208538Srajcommand_fdt(int argc, char *argv[])
674208538Sraj{
675208538Sraj
676208538Sraj	return (command_fdt_internal(argc, argv));
677208538Sraj}
678208538Sraj
679208538SrajCOMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
680208538Sraj#endif
681