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