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