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