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