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