fdt_loader_cmd.c revision 247045
1246586Sdelphij/*-
2246586Sdelphij * Copyright (c) 2009-2010 The FreeBSD Foundation
3246586Sdelphij * All rights reserved.
4246586Sdelphij *
5246586Sdelphij * This software was developed by Semihalf under sponsorship from
6246586Sdelphij * the FreeBSD Foundation.
7246586Sdelphij *
8246586Sdelphij * Redistribution and use in source and binary forms, with or without
9246586Sdelphij * modification, are permitted provided that the following conditions
10246586Sdelphij * are met:
11246586Sdelphij * 1. Redistributions of source code must retain the above copyright
12246586Sdelphij *    notice, this list of conditions and the following disclaimer.
13246586Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
14246586Sdelphij *    notice, this list of conditions and the following disclaimer in the
15246586Sdelphij *    documentation and/or other materials provided with the distribution.
16246586Sdelphij *
17246586Sdelphij * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18246586Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19246586Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20246586Sdelphij * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21246586Sdelphij * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22246586Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23246586Sdelphij * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24246586Sdelphij * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25246586Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26246586Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27246586Sdelphij * SUCH DAMAGE.
28246586Sdelphij */
29246586Sdelphij
30246586Sdelphij#include <sys/cdefs.h>
31246586Sdelphij__FBSDID("$FreeBSD: head/sys/boot/fdt/fdt_loader_cmd.c 247045 2013-02-20 16:32:38Z kientzle $");
32246586Sdelphij
33246586Sdelphij#include <stand.h>
34246586Sdelphij#include <fdt.h>
35246586Sdelphij#include <libfdt.h>
36246586Sdelphij#include <sys/param.h>
37246586Sdelphij#include <sys/linker.h>
38246586Sdelphij#include <machine/elf.h>
39246586Sdelphij
40246586Sdelphij#include "bootstrap.h"
41246586Sdelphij#include "glue.h"
42246586Sdelphij
43246586Sdelphij#ifdef DEBUG
44246586Sdelphij#define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
45246586Sdelphij    printf(fmt,##args); } while (0)
46246586Sdelphij#else
47246586Sdelphij#define debugf(fmt, args...)
48246586Sdelphij#endif
49246586Sdelphij
50246586Sdelphij#define FDT_CWD_LEN	256
51246586Sdelphij#define FDT_MAX_DEPTH	6
52246586Sdelphij
53246586Sdelphij#define FDT_PROP_SEP	" = "
54246586Sdelphij
55246586Sdelphij#define STR(number) #number
56246586Sdelphij#define STRINGIFY(number) STR(number)
57246586Sdelphij
58246586Sdelphij#define COPYOUT(s,d,l)	archsw.arch_copyout(s, d, l)
59246586Sdelphij#define COPYIN(s,d,l)	archsw.arch_copyin(s, d, l)
60246586Sdelphij
61246586Sdelphij#define FDT_STATIC_DTB_SYMBOL	"fdt_static_dtb"
62246586Sdelphij
63246586Sdelphij#define	CMD_REQUIRES_BLOB	0x01
64246586Sdelphij
65246586Sdelphij/* Local copy of FDT */
66246586Sdelphijstatic struct fdt_header *fdtp = NULL;
67246586Sdelphij/* Size of FDT blob */
68246586Sdelphijstatic size_t fdtp_size = 0;
69246586Sdelphij/* Location of FDT in kernel or module */
70246586Sdelphijstatic vm_offset_t fdtp_va = 0;
71246586Sdelphij
72246586Sdelphijstatic int fdt_load_dtb(vm_offset_t va);
73246586Sdelphij
74246586Sdelphijstatic int fdt_cmd_nyi(int argc, char *argv[]);
75246586Sdelphij
76246586Sdelphijstatic int fdt_cmd_addr(int argc, char *argv[]);
77246586Sdelphijstatic int fdt_cmd_mkprop(int argc, char *argv[]);
78246586Sdelphijstatic int fdt_cmd_cd(int argc, char *argv[]);
79246586Sdelphijstatic int fdt_cmd_hdr(int argc, char *argv[]);
80246586Sdelphijstatic int fdt_cmd_ls(int argc, char *argv[]);
81246586Sdelphijstatic int fdt_cmd_prop(int argc, char *argv[]);
82246586Sdelphijstatic int fdt_cmd_pwd(int argc, char *argv[]);
83246586Sdelphijstatic int fdt_cmd_rm(int argc, char *argv[]);
84246586Sdelphijstatic int fdt_cmd_mknode(int argc, char *argv[]);
85246586Sdelphijstatic int fdt_cmd_mres(int argc, char *argv[]);
86246586Sdelphij
87246586Sdelphijtypedef int cmdf_t(int, char *[]);
88246586Sdelphij
89246586Sdelphijstruct cmdtab {
90246586Sdelphij	char	*name;
91246586Sdelphij	cmdf_t	*handler;
92246586Sdelphij	int	flags;
93246586Sdelphij};
94246586Sdelphij
95246586Sdelphijstatic const struct cmdtab commands[] = {
96246586Sdelphij	{ "addr", &fdt_cmd_addr,	0 },
97246586Sdelphij	{ "alias", &fdt_cmd_nyi,	0 },
98246586Sdelphij	{ "cd", &fdt_cmd_cd,		CMD_REQUIRES_BLOB },
99246586Sdelphij	{ "header", &fdt_cmd_hdr,	CMD_REQUIRES_BLOB },
100246586Sdelphij	{ "ls", &fdt_cmd_ls,		CMD_REQUIRES_BLOB },
101246586Sdelphij	{ "mknode", &fdt_cmd_mknode,	CMD_REQUIRES_BLOB },
102246586Sdelphij	{ "mkprop", &fdt_cmd_mkprop,	CMD_REQUIRES_BLOB },
103246586Sdelphij	{ "mres", &fdt_cmd_mres,	CMD_REQUIRES_BLOB },
104246586Sdelphij	{ "prop", &fdt_cmd_prop,	CMD_REQUIRES_BLOB },
105246586Sdelphij	{ "pwd", &fdt_cmd_pwd,		CMD_REQUIRES_BLOB },
106246586Sdelphij	{ "rm", &fdt_cmd_rm,		CMD_REQUIRES_BLOB },
107246586Sdelphij	{ NULL, NULL }
108246586Sdelphij};
109246586Sdelphij
110246586Sdelphijstatic char cwd[FDT_CWD_LEN] = "/";
111246586Sdelphij
112246586Sdelphijstatic vm_offset_t
113246586Sdelphijfdt_find_static_dtb()
114246586Sdelphij{
115246586Sdelphij	Elf_Dyn dyn;
116246586Sdelphij	Elf_Sym sym;
117246586Sdelphij	vm_offset_t dyntab, esym, strtab, symtab, fdt_start;
118246586Sdelphij	uint64_t offs;
119246586Sdelphij	struct preloaded_file *kfp;
120246586Sdelphij	struct file_metadata *md;
121246586Sdelphij	char *strp;
122246586Sdelphij	int sym_count;
123246586Sdelphij
124246586Sdelphij	symtab = strtab = dyntab = esym = 0;
125246586Sdelphij	strp = NULL;
126246586Sdelphij
127246586Sdelphij	offs = __elfN(relocation_offset);
128246586Sdelphij
129246586Sdelphij	kfp = file_findfile(NULL, NULL);
130246586Sdelphij	if (kfp == NULL)
131246586Sdelphij		return (0);
132246586Sdelphij
133246586Sdelphij	md = file_findmetadata(kfp, MODINFOMD_ESYM);
134246586Sdelphij	if (md == NULL)
135246586Sdelphij		return (0);
136246586Sdelphij	bcopy(md->md_data, &esym, sizeof(esym));
137246586Sdelphij	/* esym is already offset */
138246586Sdelphij
139246586Sdelphij	md = file_findmetadata(kfp, MODINFOMD_DYNAMIC);
140246586Sdelphij	if (md == NULL)
141246586Sdelphij		return (0);
142246586Sdelphij	bcopy(md->md_data, &dyntab, sizeof(dyntab));
143246586Sdelphij	dyntab += offs;
144246586Sdelphij
145246586Sdelphij	/* Locate STRTAB and DYNTAB */
146246586Sdelphij	for (;;) {
147246586Sdelphij		COPYOUT(dyntab, &dyn, sizeof(dyn));
148246586Sdelphij		if (dyn.d_tag == DT_STRTAB) {
149246586Sdelphij			strtab = (vm_offset_t)(dyn.d_un.d_ptr) + offs;
150246586Sdelphij		} else if (dyn.d_tag == DT_SYMTAB) {
151246586Sdelphij			symtab = (vm_offset_t)(dyn.d_un.d_ptr) + offs;
152246586Sdelphij		} else if (dyn.d_tag == DT_NULL) {
153246586Sdelphij			break;
154246586Sdelphij		}
155246586Sdelphij		dyntab += sizeof(dyn);
156246586Sdelphij	}
157246586Sdelphij
158246586Sdelphij	if (symtab == 0 || strtab == 0) {
159246586Sdelphij		/*
160246586Sdelphij		 * No symtab? No strtab? That should not happen here,
161246586Sdelphij		 * and should have been verified during __elfN(loadimage).
162246586Sdelphij		 * This must be some kind of a bug.
163246586Sdelphij		 */
164246586Sdelphij		return (0);
165246586Sdelphij	}
166246586Sdelphij
167246586Sdelphij	sym_count = (int)(esym - symtab) / sizeof(Elf_Sym);
168246586Sdelphij
169246586Sdelphij	/*
170246586Sdelphij	 * The most efficent way to find a symbol would be to calculate a
171246586Sdelphij	 * hash, find proper bucket and chain, and thus find a symbol.
172246586Sdelphij	 * However, that would involve code duplication (e.g. for hash
173246586Sdelphij	 * function). So we're using simpler and a bit slower way: we're
174246586Sdelphij	 * iterating through symbols, searching for the one which name is
175246586Sdelphij	 * 'equal' to 'fdt_static_dtb'. To speed up the process a little bit,
176246586Sdelphij	 * we are eliminating symbols type of which is not STT_NOTYPE, or(and)
177246586Sdelphij	 * those which binding attribute is not STB_GLOBAL.
178246586Sdelphij	 */
179246586Sdelphij	fdt_start = 0;
180246586Sdelphij	while (sym_count > 0 && fdt_start == 0) {
181246586Sdelphij		COPYOUT(symtab, &sym, sizeof(sym));
182246586Sdelphij		symtab += sizeof(sym);
183246586Sdelphij		--sym_count;
184246586Sdelphij		if (ELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
185246586Sdelphij		    ELF_ST_TYPE(sym.st_info) != STT_NOTYPE)
186246586Sdelphij			continue;
187246586Sdelphij		strp = strdupout(strtab + sym.st_name);
188246586Sdelphij		if (strcmp(strp, FDT_STATIC_DTB_SYMBOL) == 0)
189246586Sdelphij			fdt_start = (vm_offset_t)sym.st_value + offs;
190246586Sdelphij		free(strp);
191246586Sdelphij	}
192246586Sdelphij	printf("fdt_start: 0x%08jX\n", (intmax_t)fdt_start);
193246586Sdelphij	return (fdt_start);
194246586Sdelphij}
195246586Sdelphij
196246586Sdelphijstatic int
197246586Sdelphijfdt_load_dtb(vm_offset_t va)
198246586Sdelphij{
199246586Sdelphij	struct fdt_header header;
200246586Sdelphij	int err;
201246586Sdelphij
202246586Sdelphij	COPYOUT(va, &header, sizeof(header));
203246586Sdelphij	err = fdt_check_header(&header);
204246586Sdelphij	if (err < 0) {
205246586Sdelphij		if (err == -FDT_ERR_BADVERSION)
206246586Sdelphij			sprintf(command_errbuf,
207246586Sdelphij			    "incompatible blob version: %d, should be: %d",
208246586Sdelphij			    fdt_version(fdtp), FDT_LAST_SUPPORTED_VERSION);
209246586Sdelphij
210246586Sdelphij		else
211246586Sdelphij			sprintf(command_errbuf, "error validating blob: %s",
212246586Sdelphij			    fdt_strerror(err));
213246586Sdelphij		return (1);
214246586Sdelphij	}
215246586Sdelphij
216246586Sdelphij	/*
217246586Sdelphij	 * Release previous blob
218246586Sdelphij	 */
219246586Sdelphij	if (fdtp)
220246586Sdelphij		free(fdtp);
221246586Sdelphij
222246586Sdelphij	fdtp_size = fdt_totalsize(&header);
223246586Sdelphij	fdtp = malloc(fdtp_size);
224246586Sdelphij
225246586Sdelphij	if (fdtp == NULL) {
226246586Sdelphij		command_errmsg = "can't allocate memory for device tree copy";
227246586Sdelphij		return (1);
228246586Sdelphij	}
229246586Sdelphij
230246586Sdelphij	fdtp_va = va;
231246586Sdelphij	COPYOUT(va, fdtp, fdtp_size);
232246586Sdelphij	debugf("DTB blob found at 0x%jx, size: 0x%jx\n", (uintmax_t)va, (uintmax_t)fdtp_size);
233246586Sdelphij
234246586Sdelphij	return (0);
235246586Sdelphij}
236246586Sdelphij
237246586Sdelphijstatic int
238246586Sdelphijfdt_load_dtb_addr(struct fdt_header *header)
239246586Sdelphij{
240246586Sdelphij	struct preloaded_file *bfp;
241246586Sdelphij
242246586Sdelphij	bfp = mem_load_raw("dtb", "memory.dtb", header, fdt_totalsize(header));
243246586Sdelphij	if (bfp == NULL) {
244246586Sdelphij		command_errmsg = "unable to copy DTB into module directory";
245246586Sdelphij		return (1);
246246586Sdelphij	}
247246586Sdelphij	return fdt_load_dtb(bfp->f_addr);
248246586Sdelphij}
249246586Sdelphij
250246586Sdelphijstatic int
251246586Sdelphijfdt_setup_fdtp()
252246586Sdelphij{
253246586Sdelphij  struct preloaded_file *bfp;
254246586Sdelphij  struct fdt_header *hdr;
255246586Sdelphij  const char *s, *p;
256246586Sdelphij  vm_offset_t va;
257246586Sdelphij
258246586Sdelphij  if ((bfp = file_findfile(NULL, "dtb")) != NULL) {
259246586Sdelphij	  printf("Using DTB from loaded file.\n");
260246586Sdelphij	  return fdt_load_dtb(bfp->f_addr);
261246586Sdelphij  }
262246586Sdelphij
263246586Sdelphij  s = ub_env_get("fdtaddr");
264246586Sdelphij  if (s != NULL && *s != '\0') {
265246586Sdelphij	  hdr = (struct fdt_header *)strtoul(s, &p, 16);
266246586Sdelphij	  if (*p == '\0') {
267246586Sdelphij		  printf("Using DTB provided by U-Boot.\n");
268246586Sdelphij		  return fdt_load_dtb_addr(hdr);
269246586Sdelphij	  }
270246586Sdelphij  }
271246586Sdelphij
272246586Sdelphij  if ((va = fdt_find_static_dtb()) != 0) {
273246586Sdelphij	  printf("Using DTB compiled into kernel.\n");
274246586Sdelphij	  return (fdt_load_dtb(va));
275246586Sdelphij  }
276246586Sdelphij
277246586Sdelphij  command_errmsg = "no device tree blob found!";
278246586Sdelphij  return (1);
279246586Sdelphij}
280246586Sdelphij
281246586Sdelphij#define fdt_strtovect(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
282246586Sdelphij    (cellbuf), (lim), (cellsize), 0);
283246586Sdelphij
284246586Sdelphij/* Force using base 16 */
285246586Sdelphij#define fdt_strtovectx(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
286246586Sdelphij    (cellbuf), (lim), (cellsize), 16);
287246586Sdelphij
288246586Sdelphijstatic int
289246586Sdelphij_fdt_strtovect(char *str, void *cellbuf, int lim, unsigned char cellsize,
290246586Sdelphij    uint8_t base)
291246586Sdelphij{
292246586Sdelphij	char *buf = str;
293246586Sdelphij	char *end = str + strlen(str) - 2;
294246586Sdelphij	uint32_t *u32buf = NULL;
295246586Sdelphij	uint8_t *u8buf = NULL;
296246586Sdelphij	int cnt = 0;
297246586Sdelphij
298246586Sdelphij	if (cellsize == sizeof(uint32_t))
299246586Sdelphij		u32buf = (uint32_t *)cellbuf;
300246586Sdelphij	else
301246586Sdelphij		u8buf = (uint8_t *)cellbuf;
302246586Sdelphij
303246586Sdelphij	if (lim == 0)
304246586Sdelphij		return (0);
305246586Sdelphij
306246586Sdelphij	while (buf < end) {
307246586Sdelphij
308246586Sdelphij		/* Skip white whitespace(s)/separators */
309		while (!isxdigit(*buf) && buf < end)
310			buf++;
311
312		if (u32buf != NULL)
313			u32buf[cnt] =
314			    cpu_to_fdt32((uint32_t)strtol(buf, NULL, base));
315
316		else
317			u8buf[cnt] = (uint8_t)strtol(buf, NULL, base);
318
319		if (cnt + 1 <= lim - 1)
320			cnt++;
321		else
322			break;
323		buf++;
324		/* Find another number */
325		while ((isxdigit(*buf) || *buf == 'x') && buf < end)
326			buf++;
327	}
328	return (cnt);
329}
330
331#define	TMP_MAX_ETH	8
332
333void
334fixup_ethernet(const char *env, char *ethstr, int *eth_no, int len)
335{
336	char *end, *str;
337	uint8_t tmp_addr[6];
338	int i, n;
339
340	/* Extract interface number */
341	i = strtol(env + 3, &end, 10);
342	if (end == (env + 3))
343		/* 'ethaddr' means interface 0 address */
344		n = 0;
345	else
346		n = i;
347
348	if (n > TMP_MAX_ETH)
349		return;
350
351	str = ub_env_get(env);
352
353	/* Convert macaddr string into a vector of uints */
354	fdt_strtovectx(str, &tmp_addr, 6, sizeof(uint8_t));
355	if (n != 0) {
356		i = strlen(env) - 7;
357		strncpy(ethstr + 8, env + 3, i);
358	}
359	/* Set actual property to a value from vect */
360	fdt_setprop(fdtp, fdt_path_offset(fdtp, ethstr),
361	    "local-mac-address", &tmp_addr, 6 * sizeof(uint8_t));
362
363	/* Clear ethernet..XXXX.. string */
364	bzero(ethstr + 8, len - 8);
365
366	if (n + 1 > *eth_no)
367		*eth_no = n + 1;
368}
369
370void
371fixup_cpubusfreqs(unsigned long cpufreq, unsigned long busfreq)
372{
373	int lo, o = 0, o2, maxo = 0, depth;
374	const uint32_t zero = 0;
375
376	/* We want to modify every subnode of /cpus */
377	o = fdt_path_offset(fdtp, "/cpus");
378	if (o < 0)
379		return;
380
381	/* maxo should contain offset of node next to /cpus */
382	depth = 0;
383	maxo = o;
384	while (depth != -1)
385		maxo = fdt_next_node(fdtp, maxo, &depth);
386
387	/* Find CPU frequency properties */
388	o = fdt_node_offset_by_prop_value(fdtp, o, "clock-frequency",
389	    &zero, sizeof(uint32_t));
390
391	o2 = fdt_node_offset_by_prop_value(fdtp, o, "bus-frequency", &zero,
392	    sizeof(uint32_t));
393
394	lo = MIN(o, o2);
395
396	while (o != -FDT_ERR_NOTFOUND && o2 != -FDT_ERR_NOTFOUND) {
397
398		o = fdt_node_offset_by_prop_value(fdtp, lo,
399		    "clock-frequency", &zero, sizeof(uint32_t));
400
401		o2 = fdt_node_offset_by_prop_value(fdtp, lo, "bus-frequency",
402		    &zero, sizeof(uint32_t));
403
404		/* We're only interested in /cpus subnode(s) */
405		if (lo > maxo)
406			break;
407
408		fdt_setprop_inplace_cell(fdtp, lo, "clock-frequency",
409		    (uint32_t)cpufreq);
410
411		fdt_setprop_inplace_cell(fdtp, lo, "bus-frequency",
412		    (uint32_t)busfreq);
413
414		lo = MIN(o, o2);
415	}
416}
417
418int
419fdt_reg_valid(uint32_t *reg, int len, int addr_cells, int size_cells)
420{
421	int cells_in_tuple, i, tuples, tuple_size;
422	uint32_t cur_start, cur_size;
423
424	cells_in_tuple = (addr_cells + size_cells);
425	tuple_size = cells_in_tuple * sizeof(uint32_t);
426	tuples = len / tuple_size;
427	if (tuples == 0)
428		return (EINVAL);
429
430	for (i = 0; i < tuples; i++) {
431		if (addr_cells == 2)
432			cur_start = fdt64_to_cpu(reg[i * cells_in_tuple]);
433		else
434			cur_start = fdt32_to_cpu(reg[i * cells_in_tuple]);
435
436		if (size_cells == 2)
437			cur_size = fdt64_to_cpu(reg[i * cells_in_tuple + 2]);
438		else
439			cur_size = fdt32_to_cpu(reg[i * cells_in_tuple + 1]);
440
441		if (cur_size == 0)
442			return (EINVAL);
443
444		debugf(" reg#%d (start: 0x%0x size: 0x%0x) valid!\n",
445		    i, cur_start, cur_size);
446	}
447	return (0);
448}
449
450void
451fixup_memory(struct sys_info *si)
452{
453	struct mem_region *curmr;
454	uint32_t addr_cells, size_cells;
455	uint32_t *addr_cellsp, *reg,  *size_cellsp;
456	int err, i, len, memory, realmrno, root;
457	uint8_t *buf, *sb;
458	uint64_t rstart, rsize;
459	int reserved;
460
461	root = fdt_path_offset(fdtp, "/");
462	if (root < 0) {
463		sprintf(command_errbuf, "Could not find root node !");
464		return;
465	}
466
467	memory = fdt_path_offset(fdtp, "/memory");
468	if (memory <= 0) {
469		/* Create proper '/memory' node. */
470		memory = fdt_add_subnode(fdtp, root, "memory");
471		if (memory <= 0) {
472			sprintf(command_errbuf, "Could not fixup '/memory' "
473			    "node, error code : %d!\n", memory);
474			return;
475		}
476
477		err = fdt_setprop(fdtp, memory, "device_type", "memory",
478		    sizeof("memory"));
479
480		if (err < 0)
481			return;
482	}
483
484	addr_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#address-cells",
485	    NULL);
486	size_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#size-cells", NULL);
487
488	if (addr_cellsp == NULL || size_cellsp == NULL) {
489		sprintf(command_errbuf, "Could not fixup '/memory' node : "
490		    "%s %s property not found in root node!\n",
491		    (!addr_cellsp) ? "#address-cells" : "",
492		    (!size_cellsp) ? "#size-cells" : "");
493		return;
494	}
495
496	addr_cells = fdt32_to_cpu(*addr_cellsp);
497	size_cells = fdt32_to_cpu(*size_cellsp);
498
499	/*
500	 * Convert memreserve data to memreserve property
501	 * Check if property already exists
502	 */
503	reserved = fdt_num_mem_rsv(fdtp);
504	if (reserved &&
505	    (fdt_getprop(fdtp, root, "memreserve", NULL) == NULL)) {
506		len = (addr_cells + size_cells) * reserved * sizeof(uint32_t);
507		sb = buf = (uint8_t *)malloc(len);
508		if (!buf)
509			return;
510
511		bzero(buf, len);
512
513		for (i = 0; i < reserved; i++) {
514			curmr = &si->mr[i];
515			if (fdt_get_mem_rsv(fdtp, i, &rstart, &rsize))
516				break;
517			if (rsize) {
518				/* Ensure endianess, and put cells into a buffer */
519				if (addr_cells == 2)
520					*(uint64_t *)buf =
521					    cpu_to_fdt64(rstart);
522				else
523					*(uint32_t *)buf =
524					    cpu_to_fdt32(rstart);
525
526				buf += sizeof(uint32_t) * addr_cells;
527				if (size_cells == 2)
528					*(uint64_t *)buf =
529					    cpu_to_fdt64(rsize);
530				else
531					*(uint32_t *)buf =
532					    cpu_to_fdt32(rsize);
533
534				buf += sizeof(uint32_t) * size_cells;
535			}
536		}
537
538		/* Set property */
539		if ((err = fdt_setprop(fdtp, root, "memreserve", sb, len)) < 0)
540			printf("Could not fixup 'memreserve' property.\n");
541
542		free(sb);
543	}
544
545	/* Count valid memory regions entries in sysinfo. */
546	realmrno = si->mr_no;
547	for (i = 0; i < si->mr_no; i++)
548		if (si->mr[i].start == 0 && si->mr[i].size == 0)
549			realmrno--;
550
551	if (realmrno == 0) {
552		sprintf(command_errbuf, "Could not fixup '/memory' node : "
553		    "sysinfo doesn't contain valid memory regions info!\n");
554		return;
555	}
556
557	if ((reg = (uint32_t *)fdt_getprop(fdtp, memory, "reg",
558	    &len)) != NULL) {
559
560		if (fdt_reg_valid(reg, len, addr_cells, size_cells) == 0)
561			/*
562			 * Do not apply fixup if existing 'reg' property
563			 * seems to be valid.
564			 */
565			return;
566	}
567
568	len = (addr_cells + size_cells) * realmrno * sizeof(uint32_t);
569	sb = buf = (uint8_t *)malloc(len);
570	if (!buf)
571		return;
572
573	bzero(buf, len);
574
575	for (i = 0; i < si->mr_no; i++) {
576		curmr = &si->mr[i];
577		if (curmr->size != 0) {
578			/* Ensure endianess, and put cells into a buffer */
579			if (addr_cells == 2)
580				*(uint64_t *)buf =
581				    cpu_to_fdt64(curmr->start);
582			else
583				*(uint32_t *)buf =
584				    cpu_to_fdt32(curmr->start);
585
586			buf += sizeof(uint32_t) * addr_cells;
587			if (size_cells == 2)
588				*(uint64_t *)buf =
589				    cpu_to_fdt64(curmr->size);
590			else
591				*(uint32_t *)buf =
592				    cpu_to_fdt32(curmr->size);
593
594			buf += sizeof(uint32_t) * size_cells;
595		}
596	}
597
598	/* Set property */
599	if ((err = fdt_setprop(fdtp, memory, "reg", sb, len)) < 0)
600		sprintf(command_errbuf, "Could not fixup '/memory' node.\n");
601
602	free(sb);
603}
604
605void
606fixup_stdout(const char *env)
607{
608	const char *str;
609	char *ptr;
610	int serialno;
611	int len, no, sero;
612	const struct fdt_property *prop;
613	char *tmp[10];
614
615	str = ub_env_get(env);
616	ptr = (char *)str + strlen(str) - 1;
617	while (ptr > str && isdigit(*(str - 1)))
618		str--;
619
620	if (ptr == str)
621		return;
622
623	serialno = (int)strtol(ptr, NULL, 0);
624	no = fdt_path_offset(fdtp, "/chosen");
625	if (no < 0)
626		return;
627
628	prop = fdt_get_property(fdtp, no, "stdout", &len);
629
630	/* If /chosen/stdout does not extist, create it */
631	if (prop == NULL || (prop != NULL && len == 0)) {
632
633		bzero(tmp, 10 * sizeof(char));
634		strcpy((char *)&tmp, "serial");
635		if (strlen(ptr) > 3)
636			/* Serial number too long */
637			return;
638
639		strncpy((char *)tmp + 6, ptr, 3);
640		sero = fdt_path_offset(fdtp, (const char *)tmp);
641		if (sero < 0)
642			/*
643			 * If serial device we're trying to assign
644			 * stdout to doesn't exist in DT -- return.
645			 */
646			return;
647
648		fdt_setprop(fdtp, no, "stdout", &tmp,
649		    strlen((char *)&tmp) + 1);
650		fdt_setprop(fdtp, no, "stdin", &tmp,
651		    strlen((char *)&tmp) + 1);
652	}
653}
654
655/*
656 * Locate the blob, fix it up and return its location.
657 */
658static vm_offset_t
659fdt_fixup(void)
660{
661	const char *env;
662	char *ethstr;
663	int chosen, err, eth_no, len;
664	struct sys_info *si;
665
666	env = NULL;
667	eth_no = 0;
668	ethstr = NULL;
669	len = 0;
670
671	if (fdtp == NULL) {
672		err = fdt_setup_fdtp();
673		if (err) {
674			sprintf(command_errbuf, "No valid device tree blob found!");
675			return (0);
676		}
677	}
678
679	/* Create /chosen node (if not exists) */
680	if ((chosen = fdt_subnode_offset(fdtp, 0, "chosen")) ==
681	    -FDT_ERR_NOTFOUND)
682		chosen = fdt_add_subnode(fdtp, 0, "chosen");
683
684	/* Value assigned to fixup-applied does not matter. */
685	if (fdt_getprop(fdtp, chosen, "fixup-applied", NULL))
686		goto success;
687
688	/* Acquire sys_info */
689	si = ub_get_sys_info();
690
691	while ((env = ub_env_enum(env)) != NULL) {
692		if (strncmp(env, "eth", 3) == 0 &&
693		    strncmp(env + (strlen(env) - 4), "addr", 4) == 0) {
694			/*
695			 * Handle Ethernet addrs: parse uboot env eth%daddr
696			 */
697
698			if (!eth_no) {
699				/*
700				 * Check how many chars we will need to store
701				 * maximal eth iface number.
702				 */
703				len = strlen(STRINGIFY(TMP_MAX_ETH)) +
704				    strlen("ethernet");
705
706				/*
707				 * Reserve mem for string "ethernet" and len
708				 * chars for iface no.
709				 */
710				ethstr = (char *)malloc(len * sizeof(char));
711				bzero(ethstr, len * sizeof(char));
712				strcpy(ethstr, "ethernet0");
713			}
714
715			/* Modify blob */
716			fixup_ethernet(env, ethstr, &eth_no, len);
717
718		} else if (strcmp(env, "consoledev") == 0)
719			fixup_stdout(env);
720	}
721
722	/* Modify cpu(s) and bus clock frequenties in /cpus node [Hz] */
723	fixup_cpubusfreqs(si->clk_cpu, si->clk_bus);
724
725	/* Fixup memory regions */
726	fixup_memory(si);
727
728	fdt_setprop(fdtp, chosen, "fixup-applied", NULL, 0);
729
730success:
731	/* Overwrite the FDT with the fixed version. */
732	COPYIN(fdtp, fdtp_va, fdtp_size);
733	return (fdtp_va);
734}
735
736/*
737 * Copy DTB blob to specified location and its return size
738 */
739int
740fdt_copy(vm_offset_t va)
741{
742	int err;
743
744	if (fdtp == NULL) {
745		err = fdt_setup_fdtp();
746		if (err) {
747			printf("No valid device tree blob found!");
748			return (0);
749		}
750	}
751
752	if (fdt_fixup() == 0)
753		return (0);
754
755	COPYIN(fdtp, va, fdtp_size);
756	return (fdtp_size);
757}
758
759
760
761int
762command_fdt_internal(int argc, char *argv[])
763{
764	cmdf_t *cmdh;
765	int flags;
766	char *cmd;
767	int i, err;
768
769	if (argc < 2) {
770		command_errmsg = "usage is 'fdt <command> [<args>]";
771		return (CMD_ERROR);
772	}
773
774	/*
775	 * Validate fdt <command>.
776	 */
777	cmd = strdup(argv[1]);
778	i = 0;
779	cmdh = NULL;
780	while (!(commands[i].name == NULL)) {
781		if (strcmp(cmd, commands[i].name) == 0) {
782			/* found it */
783			cmdh = commands[i].handler;
784			flags = commands[i].flags;
785			break;
786		}
787		i++;
788	}
789	if (cmdh == NULL) {
790		command_errmsg = "unknown command";
791		return (CMD_ERROR);
792	}
793
794	if (flags & CMD_REQUIRES_BLOB) {
795		/*
796		 * Check if uboot env vars were parsed already. If not, do it now.
797		 */
798		if (fdt_fixup() == 0)
799			return (CMD_ERROR);
800	}
801
802	/*
803	 * Call command handler.
804	 */
805	err = (*cmdh)(argc, argv);
806
807	return (err);
808}
809
810static int
811fdt_cmd_addr(int argc, char *argv[])
812{
813	struct fdt_header *hdr;
814	const char *addr, *cp;
815
816	if (argc > 2)
817		addr = argv[2];
818	else {
819		sprintf(command_errbuf, "no address specified");
820		return (CMD_ERROR);
821	}
822
823	hdr = (struct fdt_header *)strtoul(addr, &cp, 0);
824	if (cp == addr) {
825		sprintf(command_errbuf, "Invalid address: %s", addr);
826		return (CMD_ERROR);
827	}
828
829	if (fdt_load_dtb_addr(hdr) != 0)
830		return (CMD_ERROR);
831
832	return (CMD_OK);
833}
834
835static int
836fdt_cmd_cd(int argc, char *argv[])
837{
838	char *path;
839	char tmp[FDT_CWD_LEN];
840	int len, o;
841
842	path = (argc > 2) ? argv[2] : "/";
843
844	if (path[0] == '/') {
845		len = strlen(path);
846		if (len >= FDT_CWD_LEN)
847			goto fail;
848	} else {
849		/* Handle path specification relative to cwd */
850		len = strlen(cwd) + strlen(path) + 1;
851		if (len >= FDT_CWD_LEN)
852			goto fail;
853
854		strcpy(tmp, cwd);
855		strcat(tmp, "/");
856		strcat(tmp, path);
857		path = tmp;
858	}
859
860	o = fdt_path_offset(fdtp, path);
861	if (o < 0) {
862		sprintf(command_errbuf, "could not find node: '%s'", path);
863		return (CMD_ERROR);
864	}
865
866	strcpy(cwd, path);
867	return (CMD_OK);
868
869fail:
870	sprintf(command_errbuf, "path too long: %d, max allowed: %d",
871	    len, FDT_CWD_LEN - 1);
872	return (CMD_ERROR);
873}
874
875static int
876fdt_cmd_hdr(int argc __unused, char *argv[] __unused)
877{
878	char line[80];
879	int ver;
880
881	if (fdtp == NULL) {
882		command_errmsg = "no device tree blob pointer?!";
883		return (CMD_ERROR);
884	}
885
886	ver = fdt_version(fdtp);
887	pager_open();
888	sprintf(line, "\nFlattened device tree header (%p):\n", fdtp);
889	pager_output(line);
890	sprintf(line, " magic                   = 0x%08x\n", fdt_magic(fdtp));
891	pager_output(line);
892	sprintf(line, " size                    = %d\n", fdt_totalsize(fdtp));
893	pager_output(line);
894	sprintf(line, " off_dt_struct           = 0x%08x\n",
895	    fdt_off_dt_struct(fdtp));
896	pager_output(line);
897	sprintf(line, " off_dt_strings          = 0x%08x\n",
898	    fdt_off_dt_strings(fdtp));
899	pager_output(line);
900	sprintf(line, " off_mem_rsvmap          = 0x%08x\n",
901	    fdt_off_mem_rsvmap(fdtp));
902	pager_output(line);
903	sprintf(line, " version                 = %d\n", ver);
904	pager_output(line);
905	sprintf(line, " last compatible version = %d\n",
906	    fdt_last_comp_version(fdtp));
907	pager_output(line);
908	if (ver >= 2) {
909		sprintf(line, " boot_cpuid              = %d\n",
910		    fdt_boot_cpuid_phys(fdtp));
911		pager_output(line);
912	}
913	if (ver >= 3) {
914		sprintf(line, " size_dt_strings         = %d\n",
915		    fdt_size_dt_strings(fdtp));
916		pager_output(line);
917	}
918	if (ver >= 17) {
919		sprintf(line, " size_dt_struct          = %d\n",
920		    fdt_size_dt_struct(fdtp));
921		pager_output(line);
922	}
923	pager_close();
924
925	return (CMD_OK);
926}
927
928static int
929fdt_cmd_ls(int argc, char *argv[])
930{
931	const char *prevname[FDT_MAX_DEPTH] = { NULL };
932	const char *name;
933	char *path;
934	int i, o, depth, len;
935
936	path = (argc > 2) ? argv[2] : NULL;
937	if (path == NULL)
938		path = cwd;
939
940	o = fdt_path_offset(fdtp, path);
941	if (o < 0) {
942		sprintf(command_errbuf, "could not find node: '%s'", path);
943		return (CMD_ERROR);
944	}
945
946	for (depth = 0;
947	    (o >= 0) && (depth >= 0);
948	    o = fdt_next_node(fdtp, o, &depth)) {
949
950		name = fdt_get_name(fdtp, o, &len);
951
952		if (depth > FDT_MAX_DEPTH) {
953			printf("max depth exceeded: %d\n", depth);
954			continue;
955		}
956
957		prevname[depth] = name;
958
959		/* Skip root (i = 1) when printing devices */
960		for (i = 1; i <= depth; i++) {
961			if (prevname[i] == NULL)
962				break;
963
964			if (strcmp(cwd, "/") == 0)
965				printf("/");
966			printf("%s", prevname[i]);
967		}
968		printf("\n");
969	}
970
971	return (CMD_OK);
972}
973
974static __inline int
975isprint(int c)
976{
977
978	return (c >= ' ' && c <= 0x7e);
979}
980
981static int
982fdt_isprint(const void *data, int len, int *count)
983{
984	const char *d;
985	char ch;
986	int yesno, i;
987
988	if (len == 0)
989		return (0);
990
991	d = (const char *)data;
992	if (d[len - 1] != '\0')
993		return (0);
994
995	*count = 0;
996	yesno = 1;
997	for (i = 0; i < len; i++) {
998		ch = *(d + i);
999		if (isprint(ch) || (ch == '\0' && i > 0)) {
1000			/* Count strings */
1001			if (ch == '\0')
1002				(*count)++;
1003			continue;
1004		}
1005
1006		yesno = 0;
1007		break;
1008	}
1009
1010	return (yesno);
1011}
1012
1013static int
1014fdt_data_str(const void *data, int len, int count, char **buf)
1015{
1016	char *b, *tmp;
1017	const char *d;
1018	int buf_len, i, l;
1019
1020	/*
1021	 * Calculate the length for the string and allocate memory.
1022	 *
1023	 * Note that 'len' already includes at least one terminator.
1024	 */
1025	buf_len = len;
1026	if (count > 1) {
1027		/*
1028		 * Each token had already a terminator buried in 'len', but we
1029		 * only need one eventually, don't count space for these.
1030		 */
1031		buf_len -= count - 1;
1032
1033		/* Each consecutive token requires a ", " separator. */
1034		buf_len += count * 2;
1035	}
1036
1037	/* Add some space for surrounding double quotes. */
1038	buf_len += count * 2;
1039
1040	/* Note that string being put in 'tmp' may be as big as 'buf_len'. */
1041	b = (char *)malloc(buf_len);
1042	tmp = (char *)malloc(buf_len);
1043	if (b == NULL)
1044		goto error;
1045
1046	if (tmp == NULL) {
1047		free(b);
1048		goto error;
1049	}
1050
1051	b[0] = '\0';
1052
1053	/*
1054	 * Now that we have space, format the string.
1055	 */
1056	i = 0;
1057	do {
1058		d = (const char *)data + i;
1059		l = strlen(d) + 1;
1060
1061		sprintf(tmp, "\"%s\"%s", d,
1062		    (i + l) < len ?  ", " : "");
1063		strcat(b, tmp);
1064
1065		i += l;
1066
1067	} while (i < len);
1068	*buf = b;
1069
1070	free(tmp);
1071
1072	return (0);
1073error:
1074	return (1);
1075}
1076
1077static int
1078fdt_data_cell(const void *data, int len, char **buf)
1079{
1080	char *b, *tmp;
1081	const uint32_t *c;
1082	int count, i, l;
1083
1084	/* Number of cells */
1085	count = len / 4;
1086
1087	/*
1088	 * Calculate the length for the string and allocate memory.
1089	 */
1090
1091	/* Each byte translates to 2 output characters */
1092	l = len * 2;
1093	if (count > 1) {
1094		/* Each consecutive cell requires a " " separator. */
1095		l += (count - 1) * 1;
1096	}
1097	/* Each cell will have a "0x" prefix */
1098	l += count * 2;
1099	/* Space for surrounding <> and terminator */
1100	l += 3;
1101
1102	b = (char *)malloc(l);
1103	tmp = (char *)malloc(l);
1104	if (b == NULL)
1105		goto error;
1106
1107	if (tmp == NULL) {
1108		free(b);
1109		goto error;
1110	}
1111
1112	b[0] = '\0';
1113	strcat(b, "<");
1114
1115	for (i = 0; i < len; i += 4) {
1116		c = (const uint32_t *)((const uint8_t *)data + i);
1117		sprintf(tmp, "0x%08x%s", fdt32_to_cpu(*c),
1118		    i < (len - 4) ? " " : "");
1119		strcat(b, tmp);
1120	}
1121	strcat(b, ">");
1122	*buf = b;
1123
1124	free(tmp);
1125
1126	return (0);
1127error:
1128	return (1);
1129}
1130
1131static int
1132fdt_data_bytes(const void *data, int len, char **buf)
1133{
1134	char *b, *tmp;
1135	const char *d;
1136	int i, l;
1137
1138	/*
1139	 * Calculate the length for the string and allocate memory.
1140	 */
1141
1142	/* Each byte translates to 2 output characters */
1143	l = len * 2;
1144	if (len > 1)
1145		/* Each consecutive byte requires a " " separator. */
1146		l += (len - 1) * 1;
1147	/* Each byte will have a "0x" prefix */
1148	l += len * 2;
1149	/* Space for surrounding [] and terminator. */
1150	l += 3;
1151
1152	b = (char *)malloc(l);
1153	tmp = (char *)malloc(l);
1154	if (b == NULL)
1155		goto error;
1156
1157	if (tmp == NULL) {
1158		free(b);
1159		goto error;
1160	}
1161
1162	b[0] = '\0';
1163	strcat(b, "[");
1164
1165	for (i = 0, d = data; i < len; i++) {
1166		sprintf(tmp, "0x%02x%s", d[i], i < len - 1 ? " " : "");
1167		strcat(b, tmp);
1168	}
1169	strcat(b, "]");
1170	*buf = b;
1171
1172	free(tmp);
1173
1174	return (0);
1175error:
1176	return (1);
1177}
1178
1179static int
1180fdt_data_fmt(const void *data, int len, char **buf)
1181{
1182	int count;
1183
1184	if (len == 0) {
1185		*buf = NULL;
1186		return (1);
1187	}
1188
1189	if (fdt_isprint(data, len, &count))
1190		return (fdt_data_str(data, len, count, buf));
1191
1192	else if ((len % 4) == 0)
1193		return (fdt_data_cell(data, len, buf));
1194
1195	else
1196		return (fdt_data_bytes(data, len, buf));
1197}
1198
1199static int
1200fdt_prop(int offset)
1201{
1202	char *line, *buf;
1203	const struct fdt_property *prop;
1204	const char *name;
1205	const void *data;
1206	int len, rv;
1207
1208	line = NULL;
1209	prop = fdt_offset_ptr(fdtp, offset, sizeof(*prop));
1210	if (prop == NULL)
1211		return (1);
1212
1213	name = fdt_string(fdtp, fdt32_to_cpu(prop->nameoff));
1214	len = fdt32_to_cpu(prop->len);
1215
1216	rv = 0;
1217	buf = NULL;
1218	if (len == 0) {
1219		/* Property without value */
1220		line = (char *)malloc(strlen(name) + 2);
1221		if (line == NULL) {
1222			rv = 2;
1223			goto out2;
1224		}
1225		sprintf(line, "%s\n", name);
1226		goto out1;
1227	}
1228
1229	/*
1230	 * Process property with value
1231	 */
1232	data = prop->data;
1233
1234	if (fdt_data_fmt(data, len, &buf) != 0) {
1235		rv = 3;
1236		goto out2;
1237	}
1238
1239	line = (char *)malloc(strlen(name) + strlen(FDT_PROP_SEP) +
1240	    strlen(buf) + 2);
1241	if (line == NULL) {
1242		sprintf(command_errbuf, "could not allocate space for string");
1243		rv = 4;
1244		goto out2;
1245	}
1246
1247	sprintf(line, "%s" FDT_PROP_SEP "%s\n", name, buf);
1248
1249out1:
1250	pager_open();
1251	pager_output(line);
1252	pager_close();
1253
1254out2:
1255	if (buf)
1256		free(buf);
1257
1258	if (line)
1259		free(line);
1260
1261	return (rv);
1262}
1263
1264static int
1265fdt_modprop(int nodeoff, char *propname, void *value, char mode)
1266{
1267	uint32_t cells[100];
1268	char *buf;
1269	int len, rv;
1270	const struct fdt_property *p;
1271
1272	p = fdt_get_property(fdtp, nodeoff, propname, NULL);
1273
1274	if (p != NULL) {
1275		if (mode == 1) {
1276			 /* Adding inexistant value in mode 1 is forbidden */
1277			sprintf(command_errbuf, "property already exists!");
1278			return (CMD_ERROR);
1279		}
1280	} else if (mode == 0) {
1281		sprintf(command_errbuf, "property does not exist!");
1282		return (CMD_ERROR);
1283	}
1284	len = strlen(value);
1285	rv = 0;
1286	buf = (char *)value;
1287
1288	switch (*buf) {
1289	case '&':
1290		/* phandles */
1291		break;
1292	case '<':
1293		/* Data cells */
1294		len = fdt_strtovect(buf, (void *)&cells, 100,
1295		    sizeof(uint32_t));
1296
1297		rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
1298		    len * sizeof(uint32_t));
1299		break;
1300	case '[':
1301		/* Data bytes */
1302		len = fdt_strtovect(buf, (void *)&cells, 100,
1303		    sizeof(uint8_t));
1304
1305		rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
1306		    len * sizeof(uint8_t));
1307		break;
1308	case '"':
1309	default:
1310		/* Default -- string */
1311		rv = fdt_setprop_string(fdtp, nodeoff, propname, value);
1312		break;
1313	}
1314
1315	if (rv != 0) {
1316		if (rv == -FDT_ERR_NOSPACE)
1317			sprintf(command_errbuf,
1318			    "Device tree blob is too small!\n");
1319		else
1320			sprintf(command_errbuf,
1321			    "Could not add/modify property!\n");
1322	} else {
1323		COPYIN(fdtp, fdtp_va, fdtp_size);
1324	}
1325	return (rv);
1326}
1327
1328/* Merge strings from argv into a single string */
1329static int
1330fdt_merge_strings(int argc, char *argv[], int start, char **buffer)
1331{
1332	char *buf;
1333	int i, idx, sz;
1334
1335	*buffer = NULL;
1336	sz = 0;
1337
1338	for (i = start; i < argc; i++)
1339		sz += strlen(argv[i]);
1340
1341	/* Additional bytes for whitespaces between args */
1342	sz += argc - start;
1343
1344	buf = (char *)malloc(sizeof(char) * sz);
1345	bzero(buf, sizeof(char) * sz);
1346
1347	if (buf == NULL) {
1348		sprintf(command_errbuf, "could not allocate space "
1349		    "for string");
1350		return (1);
1351	}
1352
1353	idx = 0;
1354	for (i = start, idx = 0; i < argc; i++) {
1355		strcpy(buf + idx, argv[i]);
1356		idx += strlen(argv[i]);
1357		buf[idx] = ' ';
1358		idx++;
1359	}
1360	buf[sz - 1] = '\0';
1361	*buffer = buf;
1362	return (0);
1363}
1364
1365/* Extract offset and name of node/property from a given path */
1366static int
1367fdt_extract_nameloc(char **pathp, char **namep, int *nodeoff)
1368{
1369	int o;
1370	char *path = *pathp, *name = NULL, *subpath = NULL;
1371
1372	subpath = strrchr(path, '/');
1373	if (subpath == NULL) {
1374		o = fdt_path_offset(fdtp, cwd);
1375		name = path;
1376		path = (char *)&cwd;
1377	} else {
1378		*subpath = '\0';
1379		if (strlen(path) == 0)
1380			path = cwd;
1381
1382		name = subpath + 1;
1383		o = fdt_path_offset(fdtp, path);
1384	}
1385
1386	if (strlen(name) == 0) {
1387		sprintf(command_errbuf, "name not specified");
1388		return (1);
1389	}
1390	if (o < 0) {
1391		sprintf(command_errbuf, "could not find node: '%s'", path);
1392		return (1);
1393	}
1394	*namep = name;
1395	*nodeoff = o;
1396	*pathp = path;
1397	return (0);
1398}
1399
1400static int
1401fdt_cmd_prop(int argc, char *argv[])
1402{
1403	char *path, *propname, *value;
1404	int o, next, depth, rv;
1405	uint32_t tag;
1406
1407	path = (argc > 2) ? argv[2] : NULL;
1408
1409	value = NULL;
1410
1411	if (argc > 3) {
1412		/* Merge property value strings into one */
1413		if (fdt_merge_strings(argc, argv, 3, &value) != 0)
1414			return (CMD_ERROR);
1415	} else
1416		value = NULL;
1417
1418	if (path == NULL)
1419		path = cwd;
1420
1421	rv = CMD_OK;
1422
1423	if (value) {
1424		/* If value is specified -- try to modify prop. */
1425		if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1426			return (CMD_ERROR);
1427
1428		rv = fdt_modprop(o, propname, value, 0);
1429		if (rv)
1430			return (CMD_ERROR);
1431		return (CMD_OK);
1432
1433	}
1434	/* User wants to display properties */
1435	o = fdt_path_offset(fdtp, path);
1436
1437	if (o < 0) {
1438		sprintf(command_errbuf, "could not find node: '%s'", path);
1439		rv = CMD_ERROR;
1440		goto out;
1441	}
1442
1443	depth = 0;
1444	while (depth >= 0) {
1445		tag = fdt_next_tag(fdtp, o, &next);
1446		switch (tag) {
1447		case FDT_NOP:
1448			break;
1449		case FDT_PROP:
1450			if (depth > 1)
1451				/* Don't process properties of nested nodes */
1452				break;
1453
1454			if (fdt_prop(o) != 0) {
1455				sprintf(command_errbuf, "could not process "
1456				    "property");
1457				rv = CMD_ERROR;
1458				goto out;
1459			}
1460			break;
1461		case FDT_BEGIN_NODE:
1462			depth++;
1463			if (depth > FDT_MAX_DEPTH) {
1464				printf("warning: nesting too deep: %d\n",
1465				    depth);
1466				goto out;
1467			}
1468			break;
1469		case FDT_END_NODE:
1470			depth--;
1471			if (depth == 0)
1472				/*
1473				 * This is the end of our starting node, force
1474				 * the loop finish.
1475				 */
1476				depth--;
1477			break;
1478		}
1479		o = next;
1480	}
1481out:
1482	return (rv);
1483}
1484
1485static int
1486fdt_cmd_mkprop(int argc, char *argv[])
1487{
1488	int o;
1489	char *path, *propname, *value;
1490
1491	path = (argc > 2) ? argv[2] : NULL;
1492
1493	value = NULL;
1494
1495	if (argc > 3) {
1496		/* Merge property value strings into one */
1497		if (fdt_merge_strings(argc, argv, 3, &value) != 0)
1498			return (CMD_ERROR);
1499	} else
1500		value = NULL;
1501
1502	if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1503		return (CMD_ERROR);
1504
1505	if (fdt_modprop(o, propname, value, 1))
1506		return (CMD_ERROR);
1507
1508	COPYIN(fdtp, fdtp_va, fdtp_size);
1509	return (CMD_OK);
1510}
1511
1512static int
1513fdt_cmd_rm(int argc, char *argv[])
1514{
1515	int o, rv;
1516	char *path = NULL, *propname;
1517
1518	if (argc > 2)
1519		path = argv[2];
1520	else {
1521		sprintf(command_errbuf, "no node/property name specified");
1522		return (CMD_ERROR);
1523	}
1524
1525	o = fdt_path_offset(fdtp, path);
1526	if (o < 0) {
1527		/* If node not found -- try to find & delete property */
1528		if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1529			return (CMD_ERROR);
1530
1531		if ((rv = fdt_delprop(fdtp, o, propname)) != 0) {
1532			sprintf(command_errbuf, "could not delete"
1533			    "%s\n", (rv == -FDT_ERR_NOTFOUND) ?
1534			    "(property/node does not exist)" : "");
1535			return (CMD_ERROR);
1536
1537		} else
1538			return (CMD_OK);
1539	}
1540	/* If node exists -- remove node */
1541	rv = fdt_del_node(fdtp, o);
1542	if (rv) {
1543		sprintf(command_errbuf, "could not delete node");
1544		return (CMD_ERROR);
1545	} else {
1546		COPYIN(fdtp, fdtp_va, fdtp_size);
1547	}
1548	return (CMD_OK);
1549}
1550
1551static int
1552fdt_cmd_mknode(int argc, char *argv[])
1553{
1554	int o, rv;
1555	char *path = NULL, *nodename = NULL;
1556
1557	if (argc > 2)
1558		path = argv[2];
1559	else {
1560		sprintf(command_errbuf, "no node name specified");
1561		return (CMD_ERROR);
1562	}
1563
1564	if (fdt_extract_nameloc(&path, &nodename, &o) != 0)
1565		return (CMD_ERROR);
1566
1567	rv = fdt_add_subnode(fdtp, o, nodename);
1568
1569	if (rv < 0) {
1570		if (rv == -FDT_ERR_NOSPACE)
1571			sprintf(command_errbuf,
1572			    "Device tree blob is too small!\n");
1573		else
1574			sprintf(command_errbuf,
1575			    "Could not add node!\n");
1576		return (CMD_ERROR);
1577	} else {
1578		COPYIN(fdtp, fdtp_va, fdtp_size);
1579	}
1580	return (CMD_OK);
1581}
1582
1583static int
1584fdt_cmd_pwd(int argc, char *argv[])
1585{
1586	char line[FDT_CWD_LEN];
1587
1588	pager_open();
1589	sprintf(line, "%s\n", cwd);
1590	pager_output(line);
1591	pager_close();
1592	return (CMD_OK);
1593}
1594
1595static int
1596fdt_cmd_mres(int argc, char *argv[])
1597{
1598	uint64_t start, size;
1599	int i, total;
1600	char line[80];
1601
1602	pager_open();
1603	total = fdt_num_mem_rsv(fdtp);
1604	if (total > 0) {
1605		pager_output("Reserved memory regions:\n");
1606		for (i = 0; i < total; i++) {
1607			fdt_get_mem_rsv(fdtp, i, &start, &size);
1608			sprintf(line, "reg#%d: (start: 0x%jx, size: 0x%jx)\n",
1609			    i, start, size);
1610			pager_output(line);
1611		}
1612	} else
1613		pager_output("No reserved memory regions\n");
1614	pager_close();
1615
1616	return (CMD_OK);
1617}
1618
1619static int
1620fdt_cmd_nyi(int argc, char *argv[])
1621{
1622
1623	printf("command not yet implemented\n");
1624	return (CMD_ERROR);
1625}
1626