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