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