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