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