fdt_loader_cmd.c revision 275762
1/*-
2 * Copyright (c) 2009-2010 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Semihalf under sponsorship from
6 * the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: stable/10/sys/boot/fdt/fdt_loader_cmd.c 275762 2014-12-14 15:03:11Z andrew $");
32
33#include <stand.h>
34#include <fdt.h>
35#include <libfdt.h>
36#include <sys/param.h>
37#include <sys/linker.h>
38#include <machine/elf.h>
39
40#include "bootstrap.h"
41#include "glue.h"
42
43#ifdef DEBUG
44#define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
45    printf(fmt,##args); } while (0)
46#else
47#define debugf(fmt, args...)
48#endif
49
50#define FDT_CWD_LEN	256
51#define FDT_MAX_DEPTH	6
52
53#define FDT_PROP_SEP	" = "
54
55#define STR(number) #number
56#define STRINGIFY(number) STR(number)
57
58#define COPYOUT(s,d,l)	archsw.arch_copyout(s, d, l)
59#define COPYIN(s,d,l)	archsw.arch_copyin(s, d, l)
60
61#define FDT_STATIC_DTB_SYMBOL	"fdt_static_dtb"
62
63#define	CMD_REQUIRES_BLOB	0x01
64
65/* Location of FDT yet to be loaded. */
66/* This may be in read-only memory, so can't be manipulated directly. */
67static struct fdt_header *fdt_to_load = NULL;
68/* Location of FDT on heap. */
69/* This is the copy we actually manipulate. */
70static struct fdt_header *fdtp = NULL;
71/* Size of FDT blob */
72static size_t fdtp_size = 0;
73/* Location of FDT in kernel or module. */
74/* This won't be set if FDT is loaded from disk or memory. */
75/* If it is set, we'll update it when fdt_copy() gets called. */
76static vm_offset_t fdtp_va = 0;
77
78static int fdt_load_dtb(vm_offset_t va);
79
80static int fdt_cmd_nyi(int argc, char *argv[]);
81
82static int fdt_cmd_addr(int argc, char *argv[]);
83static int fdt_cmd_mkprop(int argc, char *argv[]);
84static int fdt_cmd_cd(int argc, char *argv[]);
85static int fdt_cmd_hdr(int argc, char *argv[]);
86static int fdt_cmd_ls(int argc, char *argv[]);
87static int fdt_cmd_prop(int argc, char *argv[]);
88static int fdt_cmd_pwd(int argc, char *argv[]);
89static int fdt_cmd_rm(int argc, char *argv[]);
90static int fdt_cmd_mknode(int argc, char *argv[]);
91static int fdt_cmd_mres(int argc, char *argv[]);
92
93typedef int cmdf_t(int, char *[]);
94
95struct cmdtab {
96	const char	*name;
97	cmdf_t		*handler;
98	int		flags;
99};
100
101static const struct cmdtab commands[] = {
102	{ "addr", &fdt_cmd_addr,	0 },
103	{ "alias", &fdt_cmd_nyi,	0 },
104	{ "cd", &fdt_cmd_cd,		CMD_REQUIRES_BLOB },
105	{ "header", &fdt_cmd_hdr,	CMD_REQUIRES_BLOB },
106	{ "ls", &fdt_cmd_ls,		CMD_REQUIRES_BLOB },
107	{ "mknode", &fdt_cmd_mknode,	CMD_REQUIRES_BLOB },
108	{ "mkprop", &fdt_cmd_mkprop,	CMD_REQUIRES_BLOB },
109	{ "mres", &fdt_cmd_mres,	CMD_REQUIRES_BLOB },
110	{ "prop", &fdt_cmd_prop,	CMD_REQUIRES_BLOB },
111	{ "pwd", &fdt_cmd_pwd,		CMD_REQUIRES_BLOB },
112	{ "rm", &fdt_cmd_rm,		CMD_REQUIRES_BLOB },
113	{ NULL, NULL }
114};
115
116static char cwd[FDT_CWD_LEN] = "/";
117
118static vm_offset_t
119fdt_find_static_dtb()
120{
121	Elf_Ehdr *ehdr;
122	Elf_Shdr *shdr;
123	Elf_Sym sym;
124	vm_offset_t strtab, symtab, fdt_start;
125	uint64_t offs;
126	struct preloaded_file *kfp;
127	struct file_metadata *md;
128	char *strp;
129	int i, sym_count;
130
131	debugf("fdt_find_static_dtb()\n");
132
133	sym_count = symtab = strtab = 0;
134	strp = NULL;
135
136	offs = __elfN(relocation_offset);
137
138	kfp = file_findfile(NULL, NULL);
139	if (kfp == NULL)
140		return (0);
141
142	/* Locate the dynamic symbols and strtab. */
143	md = file_findmetadata(kfp, MODINFOMD_ELFHDR);
144	if (md == NULL)
145		return (0);
146	ehdr = (Elf_Ehdr *)md->md_data;
147
148	md = file_findmetadata(kfp, MODINFOMD_SHDR);
149	if (md == NULL)
150		return (0);
151	shdr = (Elf_Shdr *)md->md_data;
152
153	for (i = 0; i < ehdr->e_shnum; ++i) {
154		if (shdr[i].sh_type == SHT_DYNSYM && symtab == 0) {
155			symtab = shdr[i].sh_addr + offs;
156			sym_count = shdr[i].sh_size / sizeof(Elf_Sym);
157		} else if (shdr[i].sh_type == SHT_STRTAB && strtab == 0) {
158			strtab = shdr[i].sh_addr + offs;
159		}
160	}
161
162	/*
163	 * The most efficent way to find a symbol would be to calculate a
164	 * hash, find proper bucket and chain, and thus find a symbol.
165	 * However, that would involve code duplication (e.g. for hash
166	 * function). So we're using simpler and a bit slower way: we're
167	 * iterating through symbols, searching for the one which name is
168	 * 'equal' to 'fdt_static_dtb'. To speed up the process a little bit,
169	 * we are eliminating symbols type of which is not STT_NOTYPE, or(and)
170	 * those which binding attribute is not STB_GLOBAL.
171	 */
172	fdt_start = 0;
173	while (sym_count > 0 && fdt_start == 0) {
174		COPYOUT(symtab, &sym, sizeof(sym));
175		symtab += sizeof(sym);
176		--sym_count;
177		if (ELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
178		    ELF_ST_TYPE(sym.st_info) != STT_NOTYPE)
179			continue;
180		strp = strdupout(strtab + sym.st_name);
181		if (strcmp(strp, FDT_STATIC_DTB_SYMBOL) == 0)
182			fdt_start = (vm_offset_t)sym.st_value + offs;
183		free(strp);
184	}
185	return (fdt_start);
186}
187
188static int
189fdt_load_dtb(vm_offset_t va)
190{
191	struct fdt_header header;
192	int err;
193
194	debugf("fdt_load_dtb(0x%08jx)\n", (uintmax_t)va);
195
196	COPYOUT(va, &header, sizeof(header));
197	err = fdt_check_header(&header);
198	if (err < 0) {
199		if (err == -FDT_ERR_BADVERSION)
200			sprintf(command_errbuf,
201			    "incompatible blob version: %d, should be: %d",
202			    fdt_version(fdtp), FDT_LAST_SUPPORTED_VERSION);
203
204		else
205			sprintf(command_errbuf, "error validating blob: %s",
206			    fdt_strerror(err));
207		return (1);
208	}
209
210	/*
211	 * Release previous blob
212	 */
213	if (fdtp)
214		free(fdtp);
215
216	fdtp_size = fdt_totalsize(&header);
217	fdtp = malloc(fdtp_size);
218
219	if (fdtp == NULL) {
220		command_errmsg = "can't allocate memory for device tree copy";
221		return (1);
222	}
223
224	fdtp_va = va;
225	COPYOUT(va, fdtp, fdtp_size);
226	debugf("DTB blob found at 0x%jx, size: 0x%jx\n", (uintmax_t)va, (uintmax_t)fdtp_size);
227
228	return (0);
229}
230
231static int
232fdt_load_dtb_addr(struct fdt_header *header)
233{
234	int err;
235
236	debugf("fdt_load_dtb_addr(0x%p)\n", header);
237
238	fdtp_size = fdt_totalsize(header);
239	err = fdt_check_header(header);
240	if (err < 0) {
241		sprintf(command_errbuf, "error validating blob: %s",
242		    fdt_strerror(err));
243		return (err);
244	}
245	free(fdtp);
246	if ((fdtp = malloc(fdtp_size)) == NULL) {
247		command_errmsg = "can't allocate memory for device tree copy";
248		return (1);
249	}
250
251	fdtp_va = 0; // Don't write this back into module or kernel.
252	bcopy(header, fdtp, fdtp_size);
253	return (0);
254}
255
256static int
257fdt_load_dtb_file(const char * filename)
258{
259	struct preloaded_file *bfp, *oldbfp;
260	int err;
261
262	debugf("fdt_load_dtb_file(%s)\n", filename);
263
264	oldbfp = file_findfile(NULL, "dtb");
265
266	/* Attempt to load and validate a new dtb from a file. */
267	if ((bfp = file_loadraw(filename, "dtb")) == NULL) {
268		sprintf(command_errbuf, "failed to load file '%s'", filename);
269		return (1);
270	}
271	if ((err = fdt_load_dtb(bfp->f_addr)) != 0) {
272		file_discard(bfp);
273		return (err);
274	}
275
276	/* A new dtb was validated, discard any previous file. */
277	if (oldbfp)
278		file_discard(oldbfp);
279	return (0);
280}
281
282int
283fdt_setup_fdtp()
284{
285	struct preloaded_file *bfp;
286	struct fdt_header *hdr;
287	const char *s;
288	char *p;
289	vm_offset_t va;
290
291	debugf("fdt_setup_fdtp()\n");
292
293	/* If we already loaded a file, use it. */
294	if ((bfp = file_findfile(NULL, "dtb")) != NULL) {
295		if (fdt_load_dtb(bfp->f_addr) == 0) {
296			printf("Using DTB from loaded file '%s'.\n",
297			    bfp->f_name);
298			return (0);
299		}
300	}
301
302	/* If we were given the address of a valid blob in memory, use it. */
303	if (fdt_to_load != NULL) {
304		if (fdt_load_dtb_addr(fdt_to_load) == 0) {
305			printf("Using DTB from memory address 0x%08X.\n",
306			    (unsigned int)fdt_to_load);
307			return (0);
308		}
309	}
310
311	/*
312	 * If the U-boot environment contains a variable giving the address of a
313	 * valid blob in memory, use it.  The U-boot README says the right
314	 * variable for fdt data loaded into ram is fdt_addr_r, so try that
315	 * first.  Board vendors also use both fdtaddr and fdt_addr names.
316	 */
317	s = ub_env_get("fdt_addr_r");
318	if (s == NULL)
319		s = ub_env_get("fdtaddr");
320	if (s == NULL)
321		s = ub_env_get("fdt_addr");
322	if (s != NULL && *s != '\0') {
323		hdr = (struct fdt_header *)strtoul(s, &p, 16);
324		if (*p == '\0') {
325			if (fdt_load_dtb_addr(hdr) == 0) {
326				printf("Using DTB provided by U-Boot at "
327				    "address 0x%p.\n", hdr);
328				return (0);
329			}
330		}
331	}
332
333	/*
334	 * If the U-boot environment contains a variable giving the name of a
335	 * file, use it if we can load and validate it.
336	 */
337	s = ub_env_get("fdtfile");
338	if (s == NULL)
339		s = ub_env_get("fdt_file");
340	if (s != NULL && *s != '\0') {
341		if (fdt_load_dtb_file(s) == 0) {
342			printf("Loaded DTB from file '%s'.\n", s);
343			return (0);
344		}
345	}
346
347	/* If there is a dtb compiled into the kernel, use it. */
348	if ((va = fdt_find_static_dtb()) != 0) {
349		if (fdt_load_dtb(va) == 0) {
350			printf("Using DTB compiled into kernel.\n");
351			return (0);
352		}
353	}
354
355	command_errmsg = "No device tree blob found!\n";
356	return (1);
357}
358
359#define fdt_strtovect(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
360    (cellbuf), (lim), (cellsize), 0);
361
362/* Force using base 16 */
363#define fdt_strtovectx(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
364    (cellbuf), (lim), (cellsize), 16);
365
366static int
367_fdt_strtovect(const char *str, void *cellbuf, int lim, unsigned char cellsize,
368    uint8_t base)
369{
370	const char *buf = str;
371	const char *end = str + strlen(str) - 2;
372	uint32_t *u32buf = NULL;
373	uint8_t *u8buf = NULL;
374	int cnt = 0;
375
376	if (cellsize == sizeof(uint32_t))
377		u32buf = (uint32_t *)cellbuf;
378	else
379		u8buf = (uint8_t *)cellbuf;
380
381	if (lim == 0)
382		return (0);
383
384	while (buf < end) {
385
386		/* Skip white whitespace(s)/separators */
387		while (!isxdigit(*buf) && buf < end)
388			buf++;
389
390		if (u32buf != NULL)
391			u32buf[cnt] =
392			    cpu_to_fdt32((uint32_t)strtol(buf, NULL, base));
393
394		else
395			u8buf[cnt] = (uint8_t)strtol(buf, NULL, base);
396
397		if (cnt + 1 <= lim - 1)
398			cnt++;
399		else
400			break;
401		buf++;
402		/* Find another number */
403		while ((isxdigit(*buf) || *buf == 'x') && buf < end)
404			buf++;
405	}
406	return (cnt);
407}
408
409#define	TMP_MAX_ETH	8
410
411static void
412fixup_ethernet(const char *env, char *ethstr, int *eth_no, int len)
413{
414	const char *str;
415	char *end;
416	uint8_t tmp_addr[6];
417	int i, n;
418
419	/* Extract interface number */
420	i = strtol(env + 3, &end, 10);
421	if (end == (env + 3))
422		/* 'ethaddr' means interface 0 address */
423		n = 0;
424	else
425		n = i;
426
427	if (n > TMP_MAX_ETH)
428		return;
429
430	str = ub_env_get(env);
431
432	/* Convert macaddr string into a vector of uints */
433	fdt_strtovectx(str, &tmp_addr, 6, sizeof(uint8_t));
434	if (n != 0) {
435		i = strlen(env) - 7;
436		strncpy(ethstr + 8, env + 3, i);
437	}
438	/* Set actual property to a value from vect */
439	fdt_setprop(fdtp, fdt_path_offset(fdtp, ethstr),
440	    "local-mac-address", &tmp_addr, 6 * sizeof(uint8_t));
441
442	/* Clear ethernet..XXXX.. string */
443	bzero(ethstr + 8, len - 8);
444
445	if (n + 1 > *eth_no)
446		*eth_no = n + 1;
447}
448
449static void
450fixup_cpubusfreqs(unsigned long cpufreq, unsigned long busfreq)
451{
452	int lo, o = 0, o2, maxo = 0, depth;
453	const uint32_t zero = 0;
454
455	/* We want to modify every subnode of /cpus */
456	o = fdt_path_offset(fdtp, "/cpus");
457	if (o < 0)
458		return;
459
460	/* maxo should contain offset of node next to /cpus */
461	depth = 0;
462	maxo = o;
463	while (depth != -1)
464		maxo = fdt_next_node(fdtp, maxo, &depth);
465
466	/* Find CPU frequency properties */
467	o = fdt_node_offset_by_prop_value(fdtp, o, "clock-frequency",
468	    &zero, sizeof(uint32_t));
469
470	o2 = fdt_node_offset_by_prop_value(fdtp, o, "bus-frequency", &zero,
471	    sizeof(uint32_t));
472
473	lo = MIN(o, o2);
474
475	while (o != -FDT_ERR_NOTFOUND && o2 != -FDT_ERR_NOTFOUND) {
476
477		o = fdt_node_offset_by_prop_value(fdtp, lo,
478		    "clock-frequency", &zero, sizeof(uint32_t));
479
480		o2 = fdt_node_offset_by_prop_value(fdtp, lo, "bus-frequency",
481		    &zero, sizeof(uint32_t));
482
483		/* We're only interested in /cpus subnode(s) */
484		if (lo > maxo)
485			break;
486
487		fdt_setprop_inplace_cell(fdtp, lo, "clock-frequency",
488		    (uint32_t)cpufreq);
489
490		fdt_setprop_inplace_cell(fdtp, lo, "bus-frequency",
491		    (uint32_t)busfreq);
492
493		lo = MIN(o, o2);
494	}
495}
496
497static int
498fdt_reg_valid(uint32_t *reg, int len, int addr_cells, int size_cells)
499{
500	int cells_in_tuple, i, tuples, tuple_size;
501	uint32_t cur_start, cur_size;
502
503	cells_in_tuple = (addr_cells + size_cells);
504	tuple_size = cells_in_tuple * sizeof(uint32_t);
505	tuples = len / tuple_size;
506	if (tuples == 0)
507		return (EINVAL);
508
509	for (i = 0; i < tuples; i++) {
510		if (addr_cells == 2)
511			cur_start = fdt64_to_cpu(reg[i * cells_in_tuple]);
512		else
513			cur_start = fdt32_to_cpu(reg[i * cells_in_tuple]);
514
515		if (size_cells == 2)
516			cur_size = fdt64_to_cpu(reg[i * cells_in_tuple + 2]);
517		else
518			cur_size = fdt32_to_cpu(reg[i * cells_in_tuple + 1]);
519
520		if (cur_size == 0)
521			return (EINVAL);
522
523		debugf(" reg#%d (start: 0x%0x size: 0x%0x) valid!\n",
524		    i, cur_start, cur_size);
525	}
526	return (0);
527}
528
529static void
530fixup_memory(struct sys_info *si)
531{
532	struct mem_region *curmr;
533	uint32_t addr_cells, size_cells;
534	uint32_t *addr_cellsp, *reg,  *size_cellsp;
535	int err, i, len, memory, realmrno, root;
536	uint8_t *buf, *sb;
537	uint64_t rstart, rsize;
538	int reserved;
539
540	root = fdt_path_offset(fdtp, "/");
541	if (root < 0) {
542		sprintf(command_errbuf, "Could not find root node !");
543		return;
544	}
545
546	memory = fdt_path_offset(fdtp, "/memory");
547	if (memory <= 0) {
548		/* Create proper '/memory' node. */
549		memory = fdt_add_subnode(fdtp, root, "memory");
550		if (memory <= 0) {
551			sprintf(command_errbuf, "Could not fixup '/memory' "
552			    "node, error code : %d!\n", memory);
553			return;
554		}
555
556		err = fdt_setprop(fdtp, memory, "device_type", "memory",
557		    sizeof("memory"));
558
559		if (err < 0)
560			return;
561	}
562
563	addr_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#address-cells",
564	    NULL);
565	size_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#size-cells", NULL);
566
567	if (addr_cellsp == NULL || size_cellsp == NULL) {
568		sprintf(command_errbuf, "Could not fixup '/memory' node : "
569		    "%s %s property not found in root node!\n",
570		    (!addr_cellsp) ? "#address-cells" : "",
571		    (!size_cellsp) ? "#size-cells" : "");
572		return;
573	}
574
575	addr_cells = fdt32_to_cpu(*addr_cellsp);
576	size_cells = fdt32_to_cpu(*size_cellsp);
577
578	/*
579	 * Convert memreserve data to memreserve property
580	 * Check if property already exists
581	 */
582	reserved = fdt_num_mem_rsv(fdtp);
583	if (reserved &&
584	    (fdt_getprop(fdtp, root, "memreserve", NULL) == NULL)) {
585		len = (addr_cells + size_cells) * reserved * sizeof(uint32_t);
586		sb = buf = (uint8_t *)malloc(len);
587		if (!buf)
588			return;
589
590		bzero(buf, len);
591
592		for (i = 0; i < reserved; i++) {
593			curmr = &si->mr[i];
594			if (fdt_get_mem_rsv(fdtp, i, &rstart, &rsize))
595				break;
596			if (rsize) {
597				/* Ensure endianess, and put cells into a buffer */
598				if (addr_cells == 2)
599					*(uint64_t *)buf =
600					    cpu_to_fdt64(rstart);
601				else
602					*(uint32_t *)buf =
603					    cpu_to_fdt32(rstart);
604
605				buf += sizeof(uint32_t) * addr_cells;
606				if (size_cells == 2)
607					*(uint64_t *)buf =
608					    cpu_to_fdt64(rsize);
609				else
610					*(uint32_t *)buf =
611					    cpu_to_fdt32(rsize);
612
613				buf += sizeof(uint32_t) * size_cells;
614			}
615		}
616
617		/* Set property */
618		if ((err = fdt_setprop(fdtp, root, "memreserve", sb, len)) < 0)
619			printf("Could not fixup 'memreserve' property.\n");
620
621		free(sb);
622	}
623
624	/* Count valid memory regions entries in sysinfo. */
625	realmrno = si->mr_no;
626	for (i = 0; i < si->mr_no; i++)
627		if (si->mr[i].start == 0 && si->mr[i].size == 0)
628			realmrno--;
629
630	if (realmrno == 0) {
631		sprintf(command_errbuf, "Could not fixup '/memory' node : "
632		    "sysinfo doesn't contain valid memory regions info!\n");
633		return;
634	}
635
636	if ((reg = (uint32_t *)fdt_getprop(fdtp, memory, "reg",
637	    &len)) != NULL) {
638
639		if (fdt_reg_valid(reg, len, addr_cells, size_cells) == 0)
640			/*
641			 * Do not apply fixup if existing 'reg' property
642			 * seems to be valid.
643			 */
644			return;
645	}
646
647	len = (addr_cells + size_cells) * realmrno * sizeof(uint32_t);
648	sb = buf = (uint8_t *)malloc(len);
649	if (!buf)
650		return;
651
652	bzero(buf, len);
653
654	for (i = 0; i < si->mr_no; i++) {
655		curmr = &si->mr[i];
656		if (curmr->size != 0) {
657			/* Ensure endianess, and put cells into a buffer */
658			if (addr_cells == 2)
659				*(uint64_t *)buf =
660				    cpu_to_fdt64(curmr->start);
661			else
662				*(uint32_t *)buf =
663				    cpu_to_fdt32(curmr->start);
664
665			buf += sizeof(uint32_t) * addr_cells;
666			if (size_cells == 2)
667				*(uint64_t *)buf =
668				    cpu_to_fdt64(curmr->size);
669			else
670				*(uint32_t *)buf =
671				    cpu_to_fdt32(curmr->size);
672
673			buf += sizeof(uint32_t) * size_cells;
674		}
675	}
676
677	/* Set property */
678	if ((err = fdt_setprop(fdtp, memory, "reg", sb, len)) < 0)
679		sprintf(command_errbuf, "Could not fixup '/memory' node.\n");
680
681	free(sb);
682}
683
684static void
685fixup_stdout(const char *env)
686{
687	const char *str;
688	char *ptr;
689	int serialno;
690	int len, no, sero;
691	const struct fdt_property *prop;
692	char *tmp[10];
693
694	str = ub_env_get(env);
695	ptr = (char *)str + strlen(str) - 1;
696	while (ptr > str && isdigit(*(str - 1)))
697		str--;
698
699	if (ptr == str)
700		return;
701
702	serialno = (int)strtol(ptr, NULL, 0);
703	no = fdt_path_offset(fdtp, "/chosen");
704	if (no < 0)
705		return;
706
707	prop = fdt_get_property(fdtp, no, "stdout", &len);
708
709	/* If /chosen/stdout does not extist, create it */
710	if (prop == NULL || (prop != NULL && len == 0)) {
711
712		bzero(tmp, 10 * sizeof(char));
713		strcpy((char *)&tmp, "serial");
714		if (strlen(ptr) > 3)
715			/* Serial number too long */
716			return;
717
718		strncpy((char *)tmp + 6, ptr, 3);
719		sero = fdt_path_offset(fdtp, (const char *)tmp);
720		if (sero < 0)
721			/*
722			 * If serial device we're trying to assign
723			 * stdout to doesn't exist in DT -- return.
724			 */
725			return;
726
727		fdt_setprop(fdtp, no, "stdout", &tmp,
728		    strlen((char *)&tmp) + 1);
729		fdt_setprop(fdtp, no, "stdin", &tmp,
730		    strlen((char *)&tmp) + 1);
731	}
732}
733
734/*
735 * Locate the blob, fix it up and return its location.
736 */
737static int
738fdt_fixup(void)
739{
740	const char *env;
741	char *ethstr;
742	int chosen, eth_no, len;
743	struct sys_info *si;
744
745	env = NULL;
746	eth_no = 0;
747	ethstr = NULL;
748	len = 0;
749
750	debugf("fdt_fixup()\n");
751
752	if (fdtp == NULL && fdt_setup_fdtp() != 0)
753		return (0);
754
755	/* Create /chosen node (if not exists) */
756	if ((chosen = fdt_subnode_offset(fdtp, 0, "chosen")) ==
757	    -FDT_ERR_NOTFOUND)
758		chosen = fdt_add_subnode(fdtp, 0, "chosen");
759
760	/* Value assigned to fixup-applied does not matter. */
761	if (fdt_getprop(fdtp, chosen, "fixup-applied", NULL))
762		return (1);
763
764	/* Acquire sys_info */
765	si = ub_get_sys_info();
766
767	while ((env = ub_env_enum(env)) != NULL) {
768		if (strncmp(env, "eth", 3) == 0 &&
769		    strncmp(env + (strlen(env) - 4), "addr", 4) == 0) {
770			/*
771			 * Handle Ethernet addrs: parse uboot env eth%daddr
772			 */
773
774			if (!eth_no) {
775				/*
776				 * Check how many chars we will need to store
777				 * maximal eth iface number.
778				 */
779				len = strlen(STRINGIFY(TMP_MAX_ETH)) +
780				    strlen("ethernet");
781
782				/*
783				 * Reserve mem for string "ethernet" and len
784				 * chars for iface no.
785				 */
786				ethstr = (char *)malloc(len * sizeof(char));
787				bzero(ethstr, len * sizeof(char));
788				strcpy(ethstr, "ethernet0");
789			}
790
791			/* Modify blob */
792			fixup_ethernet(env, ethstr, &eth_no, len);
793
794		} else if (strcmp(env, "consoledev") == 0)
795			fixup_stdout(env);
796	}
797
798	/* Modify cpu(s) and bus clock frequenties in /cpus node [Hz] */
799	fixup_cpubusfreqs(si->clk_cpu, si->clk_bus);
800
801	/* Fixup memory regions */
802	fixup_memory(si);
803
804	fdt_setprop(fdtp, chosen, "fixup-applied", NULL, 0);
805	return (1);
806}
807
808/*
809 * Copy DTB blob to specified location and return size
810 */
811int
812fdt_copy(vm_offset_t va)
813{
814	int err;
815	debugf("fdt_copy va 0x%08x\n", va);
816	if (fdtp == NULL) {
817		err = fdt_setup_fdtp();
818		if (err) {
819			printf("No valid device tree blob found!\n");
820			return (0);
821		}
822	}
823
824	if (fdt_fixup() == 0)
825		return (0);
826
827	if (fdtp_va != 0) {
828		/* Overwrite the FDT with the fixed version. */
829		/* XXX Is this really appropriate? */
830		COPYIN(fdtp, fdtp_va, fdtp_size);
831	}
832	COPYIN(fdtp, va, fdtp_size);
833	return (fdtp_size);
834}
835
836
837
838int
839command_fdt_internal(int argc, char *argv[])
840{
841	cmdf_t *cmdh;
842	int flags;
843	char *cmd;
844	int i, err;
845
846	if (argc < 2) {
847		command_errmsg = "usage is 'fdt <command> [<args>]";
848		return (CMD_ERROR);
849	}
850
851	/*
852	 * Validate fdt <command>.
853	 */
854	cmd = strdup(argv[1]);
855	i = 0;
856	cmdh = NULL;
857	while (!(commands[i].name == NULL)) {
858		if (strcmp(cmd, commands[i].name) == 0) {
859			/* found it */
860			cmdh = commands[i].handler;
861			flags = commands[i].flags;
862			break;
863		}
864		i++;
865	}
866	if (cmdh == NULL) {
867		command_errmsg = "unknown command";
868		return (CMD_ERROR);
869	}
870
871	if (flags & CMD_REQUIRES_BLOB) {
872		/*
873		 * Check if uboot env vars were parsed already. If not, do it now.
874		 */
875		if (fdt_fixup() == 0)
876			return (CMD_ERROR);
877	}
878
879	/*
880	 * Call command handler.
881	 */
882	err = (*cmdh)(argc, argv);
883
884	return (err);
885}
886
887static int
888fdt_cmd_addr(int argc, char *argv[])
889{
890	struct preloaded_file *fp;
891	struct fdt_header *hdr;
892	const char *addr;
893	char *cp;
894
895	fdt_to_load = NULL;
896
897	if (argc > 2)
898		addr = argv[2];
899	else {
900		sprintf(command_errbuf, "no address specified");
901		return (CMD_ERROR);
902	}
903
904	hdr = (struct fdt_header *)strtoul(addr, &cp, 16);
905	if (cp == addr) {
906		sprintf(command_errbuf, "Invalid address: %s", addr);
907		return (CMD_ERROR);
908	}
909
910	while ((fp = file_findfile(NULL, "dtb")) != NULL) {
911		file_discard(fp);
912	}
913
914	fdt_to_load = hdr;
915	return (CMD_OK);
916}
917
918static int
919fdt_cmd_cd(int argc, char *argv[])
920{
921	char *path;
922	char tmp[FDT_CWD_LEN];
923	int len, o;
924
925	path = (argc > 2) ? argv[2] : "/";
926
927	if (path[0] == '/') {
928		len = strlen(path);
929		if (len >= FDT_CWD_LEN)
930			goto fail;
931	} else {
932		/* Handle path specification relative to cwd */
933		len = strlen(cwd) + strlen(path) + 1;
934		if (len >= FDT_CWD_LEN)
935			goto fail;
936
937		strcpy(tmp, cwd);
938		strcat(tmp, "/");
939		strcat(tmp, path);
940		path = tmp;
941	}
942
943	o = fdt_path_offset(fdtp, path);
944	if (o < 0) {
945		sprintf(command_errbuf, "could not find node: '%s'", path);
946		return (CMD_ERROR);
947	}
948
949	strcpy(cwd, path);
950	return (CMD_OK);
951
952fail:
953	sprintf(command_errbuf, "path too long: %d, max allowed: %d",
954	    len, FDT_CWD_LEN - 1);
955	return (CMD_ERROR);
956}
957
958static int
959fdt_cmd_hdr(int argc __unused, char *argv[] __unused)
960{
961	char line[80];
962	int ver;
963
964	if (fdtp == NULL) {
965		command_errmsg = "no device tree blob pointer?!";
966		return (CMD_ERROR);
967	}
968
969	ver = fdt_version(fdtp);
970	pager_open();
971	sprintf(line, "\nFlattened device tree header (%p):\n", fdtp);
972	pager_output(line);
973	sprintf(line, " magic                   = 0x%08x\n", fdt_magic(fdtp));
974	pager_output(line);
975	sprintf(line, " size                    = %d\n", fdt_totalsize(fdtp));
976	pager_output(line);
977	sprintf(line, " off_dt_struct           = 0x%08x\n",
978	    fdt_off_dt_struct(fdtp));
979	pager_output(line);
980	sprintf(line, " off_dt_strings          = 0x%08x\n",
981	    fdt_off_dt_strings(fdtp));
982	pager_output(line);
983	sprintf(line, " off_mem_rsvmap          = 0x%08x\n",
984	    fdt_off_mem_rsvmap(fdtp));
985	pager_output(line);
986	sprintf(line, " version                 = %d\n", ver);
987	pager_output(line);
988	sprintf(line, " last compatible version = %d\n",
989	    fdt_last_comp_version(fdtp));
990	pager_output(line);
991	if (ver >= 2) {
992		sprintf(line, " boot_cpuid              = %d\n",
993		    fdt_boot_cpuid_phys(fdtp));
994		pager_output(line);
995	}
996	if (ver >= 3) {
997		sprintf(line, " size_dt_strings         = %d\n",
998		    fdt_size_dt_strings(fdtp));
999		pager_output(line);
1000	}
1001	if (ver >= 17) {
1002		sprintf(line, " size_dt_struct          = %d\n",
1003		    fdt_size_dt_struct(fdtp));
1004		pager_output(line);
1005	}
1006	pager_close();
1007
1008	return (CMD_OK);
1009}
1010
1011static int
1012fdt_cmd_ls(int argc, char *argv[])
1013{
1014	const char *prevname[FDT_MAX_DEPTH] = { NULL };
1015	const char *name;
1016	char *path;
1017	int i, o, depth, len;
1018
1019	path = (argc > 2) ? argv[2] : NULL;
1020	if (path == NULL)
1021		path = cwd;
1022
1023	o = fdt_path_offset(fdtp, path);
1024	if (o < 0) {
1025		sprintf(command_errbuf, "could not find node: '%s'", path);
1026		return (CMD_ERROR);
1027	}
1028
1029	for (depth = 0;
1030	    (o >= 0) && (depth >= 0);
1031	    o = fdt_next_node(fdtp, o, &depth)) {
1032
1033		name = fdt_get_name(fdtp, o, &len);
1034
1035		if (depth > FDT_MAX_DEPTH) {
1036			printf("max depth exceeded: %d\n", depth);
1037			continue;
1038		}
1039
1040		prevname[depth] = name;
1041
1042		/* Skip root (i = 1) when printing devices */
1043		for (i = 1; i <= depth; i++) {
1044			if (prevname[i] == NULL)
1045				break;
1046
1047			if (strcmp(cwd, "/") == 0)
1048				printf("/");
1049			printf("%s", prevname[i]);
1050		}
1051		printf("\n");
1052	}
1053
1054	return (CMD_OK);
1055}
1056
1057static __inline int
1058isprint(int c)
1059{
1060
1061	return (c >= ' ' && c <= 0x7e);
1062}
1063
1064static int
1065fdt_isprint(const void *data, int len, int *count)
1066{
1067	const char *d;
1068	char ch;
1069	int yesno, i;
1070
1071	if (len == 0)
1072		return (0);
1073
1074	d = (const char *)data;
1075	if (d[len - 1] != '\0')
1076		return (0);
1077
1078	*count = 0;
1079	yesno = 1;
1080	for (i = 0; i < len; i++) {
1081		ch = *(d + i);
1082		if (isprint(ch) || (ch == '\0' && i > 0)) {
1083			/* Count strings */
1084			if (ch == '\0')
1085				(*count)++;
1086			continue;
1087		}
1088
1089		yesno = 0;
1090		break;
1091	}
1092
1093	return (yesno);
1094}
1095
1096static int
1097fdt_data_str(const void *data, int len, int count, char **buf)
1098{
1099	char *b, *tmp;
1100	const char *d;
1101	int buf_len, i, l;
1102
1103	/*
1104	 * Calculate the length for the string and allocate memory.
1105	 *
1106	 * Note that 'len' already includes at least one terminator.
1107	 */
1108	buf_len = len;
1109	if (count > 1) {
1110		/*
1111		 * Each token had already a terminator buried in 'len', but we
1112		 * only need one eventually, don't count space for these.
1113		 */
1114		buf_len -= count - 1;
1115
1116		/* Each consecutive token requires a ", " separator. */
1117		buf_len += count * 2;
1118	}
1119
1120	/* Add some space for surrounding double quotes. */
1121	buf_len += count * 2;
1122
1123	/* Note that string being put in 'tmp' may be as big as 'buf_len'. */
1124	b = (char *)malloc(buf_len);
1125	tmp = (char *)malloc(buf_len);
1126	if (b == NULL)
1127		goto error;
1128
1129	if (tmp == NULL) {
1130		free(b);
1131		goto error;
1132	}
1133
1134	b[0] = '\0';
1135
1136	/*
1137	 * Now that we have space, format the string.
1138	 */
1139	i = 0;
1140	do {
1141		d = (const char *)data + i;
1142		l = strlen(d) + 1;
1143
1144		sprintf(tmp, "\"%s\"%s", d,
1145		    (i + l) < len ?  ", " : "");
1146		strcat(b, tmp);
1147
1148		i += l;
1149
1150	} while (i < len);
1151	*buf = b;
1152
1153	free(tmp);
1154
1155	return (0);
1156error:
1157	return (1);
1158}
1159
1160static int
1161fdt_data_cell(const void *data, int len, char **buf)
1162{
1163	char *b, *tmp;
1164	const uint32_t *c;
1165	int count, i, l;
1166
1167	/* Number of cells */
1168	count = len / 4;
1169
1170	/*
1171	 * Calculate the length for the string and allocate memory.
1172	 */
1173
1174	/* Each byte translates to 2 output characters */
1175	l = len * 2;
1176	if (count > 1) {
1177		/* Each consecutive cell requires a " " separator. */
1178		l += (count - 1) * 1;
1179	}
1180	/* Each cell will have a "0x" prefix */
1181	l += count * 2;
1182	/* Space for surrounding <> and terminator */
1183	l += 3;
1184
1185	b = (char *)malloc(l);
1186	tmp = (char *)malloc(l);
1187	if (b == NULL)
1188		goto error;
1189
1190	if (tmp == NULL) {
1191		free(b);
1192		goto error;
1193	}
1194
1195	b[0] = '\0';
1196	strcat(b, "<");
1197
1198	for (i = 0; i < len; i += 4) {
1199		c = (const uint32_t *)((const uint8_t *)data + i);
1200		sprintf(tmp, "0x%08x%s", fdt32_to_cpu(*c),
1201		    i < (len - 4) ? " " : "");
1202		strcat(b, tmp);
1203	}
1204	strcat(b, ">");
1205	*buf = b;
1206
1207	free(tmp);
1208
1209	return (0);
1210error:
1211	return (1);
1212}
1213
1214static int
1215fdt_data_bytes(const void *data, int len, char **buf)
1216{
1217	char *b, *tmp;
1218	const char *d;
1219	int i, l;
1220
1221	/*
1222	 * Calculate the length for the string and allocate memory.
1223	 */
1224
1225	/* Each byte translates to 2 output characters */
1226	l = len * 2;
1227	if (len > 1)
1228		/* Each consecutive byte requires a " " separator. */
1229		l += (len - 1) * 1;
1230	/* Each byte will have a "0x" prefix */
1231	l += len * 2;
1232	/* Space for surrounding [] and terminator. */
1233	l += 3;
1234
1235	b = (char *)malloc(l);
1236	tmp = (char *)malloc(l);
1237	if (b == NULL)
1238		goto error;
1239
1240	if (tmp == NULL) {
1241		free(b);
1242		goto error;
1243	}
1244
1245	b[0] = '\0';
1246	strcat(b, "[");
1247
1248	for (i = 0, d = data; i < len; i++) {
1249		sprintf(tmp, "0x%02x%s", d[i], i < len - 1 ? " " : "");
1250		strcat(b, tmp);
1251	}
1252	strcat(b, "]");
1253	*buf = b;
1254
1255	free(tmp);
1256
1257	return (0);
1258error:
1259	return (1);
1260}
1261
1262static int
1263fdt_data_fmt(const void *data, int len, char **buf)
1264{
1265	int count;
1266
1267	if (len == 0) {
1268		*buf = NULL;
1269		return (1);
1270	}
1271
1272	if (fdt_isprint(data, len, &count))
1273		return (fdt_data_str(data, len, count, buf));
1274
1275	else if ((len % 4) == 0)
1276		return (fdt_data_cell(data, len, buf));
1277
1278	else
1279		return (fdt_data_bytes(data, len, buf));
1280}
1281
1282static int
1283fdt_prop(int offset)
1284{
1285	char *line, *buf;
1286	const struct fdt_property *prop;
1287	const char *name;
1288	const void *data;
1289	int len, rv;
1290
1291	line = NULL;
1292	prop = fdt_offset_ptr(fdtp, offset, sizeof(*prop));
1293	if (prop == NULL)
1294		return (1);
1295
1296	name = fdt_string(fdtp, fdt32_to_cpu(prop->nameoff));
1297	len = fdt32_to_cpu(prop->len);
1298
1299	rv = 0;
1300	buf = NULL;
1301	if (len == 0) {
1302		/* Property without value */
1303		line = (char *)malloc(strlen(name) + 2);
1304		if (line == NULL) {
1305			rv = 2;
1306			goto out2;
1307		}
1308		sprintf(line, "%s\n", name);
1309		goto out1;
1310	}
1311
1312	/*
1313	 * Process property with value
1314	 */
1315	data = prop->data;
1316
1317	if (fdt_data_fmt(data, len, &buf) != 0) {
1318		rv = 3;
1319		goto out2;
1320	}
1321
1322	line = (char *)malloc(strlen(name) + strlen(FDT_PROP_SEP) +
1323	    strlen(buf) + 2);
1324	if (line == NULL) {
1325		sprintf(command_errbuf, "could not allocate space for string");
1326		rv = 4;
1327		goto out2;
1328	}
1329
1330	sprintf(line, "%s" FDT_PROP_SEP "%s\n", name, buf);
1331
1332out1:
1333	pager_open();
1334	pager_output(line);
1335	pager_close();
1336
1337out2:
1338	if (buf)
1339		free(buf);
1340
1341	if (line)
1342		free(line);
1343
1344	return (rv);
1345}
1346
1347static int
1348fdt_modprop(int nodeoff, char *propname, void *value, char mode)
1349{
1350	uint32_t cells[100];
1351	const char *buf;
1352	int len, rv;
1353	const struct fdt_property *p;
1354
1355	p = fdt_get_property(fdtp, nodeoff, propname, NULL);
1356
1357	if (p != NULL) {
1358		if (mode == 1) {
1359			 /* Adding inexistant value in mode 1 is forbidden */
1360			sprintf(command_errbuf, "property already exists!");
1361			return (CMD_ERROR);
1362		}
1363	} else if (mode == 0) {
1364		sprintf(command_errbuf, "property does not exist!");
1365		return (CMD_ERROR);
1366	}
1367	len = strlen(value);
1368	rv = 0;
1369	buf = value;
1370
1371	switch (*buf) {
1372	case '&':
1373		/* phandles */
1374		break;
1375	case '<':
1376		/* Data cells */
1377		len = fdt_strtovect(buf, (void *)&cells, 100,
1378		    sizeof(uint32_t));
1379
1380		rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
1381		    len * sizeof(uint32_t));
1382		break;
1383	case '[':
1384		/* Data bytes */
1385		len = fdt_strtovect(buf, (void *)&cells, 100,
1386		    sizeof(uint8_t));
1387
1388		rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
1389		    len * sizeof(uint8_t));
1390		break;
1391	case '"':
1392	default:
1393		/* Default -- string */
1394		rv = fdt_setprop_string(fdtp, nodeoff, propname, value);
1395		break;
1396	}
1397
1398	if (rv != 0) {
1399		if (rv == -FDT_ERR_NOSPACE)
1400			sprintf(command_errbuf,
1401			    "Device tree blob is too small!\n");
1402		else
1403			sprintf(command_errbuf,
1404			    "Could not add/modify property!\n");
1405	}
1406	return (rv);
1407}
1408
1409/* Merge strings from argv into a single string */
1410static int
1411fdt_merge_strings(int argc, char *argv[], int start, char **buffer)
1412{
1413	char *buf;
1414	int i, idx, sz;
1415
1416	*buffer = NULL;
1417	sz = 0;
1418
1419	for (i = start; i < argc; i++)
1420		sz += strlen(argv[i]);
1421
1422	/* Additional bytes for whitespaces between args */
1423	sz += argc - start;
1424
1425	buf = (char *)malloc(sizeof(char) * sz);
1426	bzero(buf, sizeof(char) * sz);
1427
1428	if (buf == NULL) {
1429		sprintf(command_errbuf, "could not allocate space "
1430		    "for string");
1431		return (1);
1432	}
1433
1434	idx = 0;
1435	for (i = start, idx = 0; i < argc; i++) {
1436		strcpy(buf + idx, argv[i]);
1437		idx += strlen(argv[i]);
1438		buf[idx] = ' ';
1439		idx++;
1440	}
1441	buf[sz - 1] = '\0';
1442	*buffer = buf;
1443	return (0);
1444}
1445
1446/* Extract offset and name of node/property from a given path */
1447static int
1448fdt_extract_nameloc(char **pathp, char **namep, int *nodeoff)
1449{
1450	int o;
1451	char *path = *pathp, *name = NULL, *subpath = NULL;
1452
1453	subpath = strrchr(path, '/');
1454	if (subpath == NULL) {
1455		o = fdt_path_offset(fdtp, cwd);
1456		name = path;
1457		path = (char *)&cwd;
1458	} else {
1459		*subpath = '\0';
1460		if (strlen(path) == 0)
1461			path = cwd;
1462
1463		name = subpath + 1;
1464		o = fdt_path_offset(fdtp, path);
1465	}
1466
1467	if (strlen(name) == 0) {
1468		sprintf(command_errbuf, "name not specified");
1469		return (1);
1470	}
1471	if (o < 0) {
1472		sprintf(command_errbuf, "could not find node: '%s'", path);
1473		return (1);
1474	}
1475	*namep = name;
1476	*nodeoff = o;
1477	*pathp = path;
1478	return (0);
1479}
1480
1481static int
1482fdt_cmd_prop(int argc, char *argv[])
1483{
1484	char *path, *propname, *value;
1485	int o, next, depth, rv;
1486	uint32_t tag;
1487
1488	path = (argc > 2) ? argv[2] : NULL;
1489
1490	value = NULL;
1491
1492	if (argc > 3) {
1493		/* Merge property value strings into one */
1494		if (fdt_merge_strings(argc, argv, 3, &value) != 0)
1495			return (CMD_ERROR);
1496	} else
1497		value = NULL;
1498
1499	if (path == NULL)
1500		path = cwd;
1501
1502	rv = CMD_OK;
1503
1504	if (value) {
1505		/* If value is specified -- try to modify prop. */
1506		if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1507			return (CMD_ERROR);
1508
1509		rv = fdt_modprop(o, propname, value, 0);
1510		if (rv)
1511			return (CMD_ERROR);
1512		return (CMD_OK);
1513
1514	}
1515	/* User wants to display properties */
1516	o = fdt_path_offset(fdtp, path);
1517
1518	if (o < 0) {
1519		sprintf(command_errbuf, "could not find node: '%s'", path);
1520		rv = CMD_ERROR;
1521		goto out;
1522	}
1523
1524	depth = 0;
1525	while (depth >= 0) {
1526		tag = fdt_next_tag(fdtp, o, &next);
1527		switch (tag) {
1528		case FDT_NOP:
1529			break;
1530		case FDT_PROP:
1531			if (depth > 1)
1532				/* Don't process properties of nested nodes */
1533				break;
1534
1535			if (fdt_prop(o) != 0) {
1536				sprintf(command_errbuf, "could not process "
1537				    "property");
1538				rv = CMD_ERROR;
1539				goto out;
1540			}
1541			break;
1542		case FDT_BEGIN_NODE:
1543			depth++;
1544			if (depth > FDT_MAX_DEPTH) {
1545				printf("warning: nesting too deep: %d\n",
1546				    depth);
1547				goto out;
1548			}
1549			break;
1550		case FDT_END_NODE:
1551			depth--;
1552			if (depth == 0)
1553				/*
1554				 * This is the end of our starting node, force
1555				 * the loop finish.
1556				 */
1557				depth--;
1558			break;
1559		}
1560		o = next;
1561	}
1562out:
1563	return (rv);
1564}
1565
1566static int
1567fdt_cmd_mkprop(int argc, char *argv[])
1568{
1569	int o;
1570	char *path, *propname, *value;
1571
1572	path = (argc > 2) ? argv[2] : NULL;
1573
1574	value = NULL;
1575
1576	if (argc > 3) {
1577		/* Merge property value strings into one */
1578		if (fdt_merge_strings(argc, argv, 3, &value) != 0)
1579			return (CMD_ERROR);
1580	} else
1581		value = NULL;
1582
1583	if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1584		return (CMD_ERROR);
1585
1586	if (fdt_modprop(o, propname, value, 1))
1587		return (CMD_ERROR);
1588
1589	return (CMD_OK);
1590}
1591
1592static int
1593fdt_cmd_rm(int argc, char *argv[])
1594{
1595	int o, rv;
1596	char *path = NULL, *propname;
1597
1598	if (argc > 2)
1599		path = argv[2];
1600	else {
1601		sprintf(command_errbuf, "no node/property name specified");
1602		return (CMD_ERROR);
1603	}
1604
1605	o = fdt_path_offset(fdtp, path);
1606	if (o < 0) {
1607		/* If node not found -- try to find & delete property */
1608		if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1609			return (CMD_ERROR);
1610
1611		if ((rv = fdt_delprop(fdtp, o, propname)) != 0) {
1612			sprintf(command_errbuf, "could not delete"
1613			    "%s\n", (rv == -FDT_ERR_NOTFOUND) ?
1614			    "(property/node does not exist)" : "");
1615			return (CMD_ERROR);
1616
1617		} else
1618			return (CMD_OK);
1619	}
1620	/* If node exists -- remove node */
1621	rv = fdt_del_node(fdtp, o);
1622	if (rv) {
1623		sprintf(command_errbuf, "could not delete node");
1624		return (CMD_ERROR);
1625	}
1626	return (CMD_OK);
1627}
1628
1629static int
1630fdt_cmd_mknode(int argc, char *argv[])
1631{
1632	int o, rv;
1633	char *path = NULL, *nodename = NULL;
1634
1635	if (argc > 2)
1636		path = argv[2];
1637	else {
1638		sprintf(command_errbuf, "no node name specified");
1639		return (CMD_ERROR);
1640	}
1641
1642	if (fdt_extract_nameloc(&path, &nodename, &o) != 0)
1643		return (CMD_ERROR);
1644
1645	rv = fdt_add_subnode(fdtp, o, nodename);
1646
1647	if (rv < 0) {
1648		if (rv == -FDT_ERR_NOSPACE)
1649			sprintf(command_errbuf,
1650			    "Device tree blob is too small!\n");
1651		else
1652			sprintf(command_errbuf,
1653			    "Could not add node!\n");
1654		return (CMD_ERROR);
1655	}
1656	return (CMD_OK);
1657}
1658
1659static int
1660fdt_cmd_pwd(int argc, char *argv[])
1661{
1662	char line[FDT_CWD_LEN];
1663
1664	pager_open();
1665	sprintf(line, "%s\n", cwd);
1666	pager_output(line);
1667	pager_close();
1668	return (CMD_OK);
1669}
1670
1671static int
1672fdt_cmd_mres(int argc, char *argv[])
1673{
1674	uint64_t start, size;
1675	int i, total;
1676	char line[80];
1677
1678	pager_open();
1679	total = fdt_num_mem_rsv(fdtp);
1680	if (total > 0) {
1681		pager_output("Reserved memory regions:\n");
1682		for (i = 0; i < total; i++) {
1683			fdt_get_mem_rsv(fdtp, i, &start, &size);
1684			sprintf(line, "reg#%d: (start: 0x%jx, size: 0x%jx)\n",
1685			    i, start, size);
1686			pager_output(line);
1687		}
1688	} else
1689		pager_output("No reserved memory regions\n");
1690	pager_close();
1691
1692	return (CMD_OK);
1693}
1694
1695static int
1696fdt_cmd_nyi(int argc, char *argv[])
1697{
1698
1699	printf("command not yet implemented\n");
1700	return (CMD_ERROR);
1701}
1702