fdt_loader_cmd.c revision 275763
1208538Sraj/*-
2208538Sraj * Copyright (c) 2009-2010 The FreeBSD Foundation
3208538Sraj * All rights reserved.
4208538Sraj *
5208538Sraj * This software was developed by Semihalf under sponsorship from
6208538Sraj * the FreeBSD Foundation.
7208538Sraj *
8208538Sraj * Redistribution and use in source and binary forms, with or without
9208538Sraj * modification, are permitted provided that the following conditions
10208538Sraj * are met:
11208538Sraj * 1. Redistributions of source code must retain the above copyright
12208538Sraj *    notice, this list of conditions and the following disclaimer.
13208538Sraj * 2. Redistributions in binary form must reproduce the above copyright
14208538Sraj *    notice, this list of conditions and the following disclaimer in the
15208538Sraj *    documentation and/or other materials provided with the distribution.
16208538Sraj *
17208538Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18208538Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19208538Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20208538Sraj * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21208538Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22208538Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23208538Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24208538Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25208538Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26208538Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27208538Sraj * SUCH DAMAGE.
28208538Sraj */
29208538Sraj
30208538Sraj#include <sys/cdefs.h>
31208538Sraj__FBSDID("$FreeBSD: stable/10/sys/boot/fdt/fdt_loader_cmd.c 275763 2014-12-14 15:33:45Z andrew $");
32208538Sraj
33208538Sraj#include <stand.h>
34208538Sraj#include <fdt.h>
35208538Sraj#include <libfdt.h>
36233230Sraj#include <sys/param.h>
37233230Sraj#include <sys/linker.h>
38233230Sraj#include <machine/elf.h>
39208538Sraj
40208538Sraj#include "bootstrap.h"
41275763Sandrew#include "fdt_platform.h"
42208538Sraj
43208538Sraj#ifdef DEBUG
44208538Sraj#define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
45208538Sraj    printf(fmt,##args); } while (0)
46208538Sraj#else
47208538Sraj#define debugf(fmt, args...)
48208538Sraj#endif
49208538Sraj
50208538Sraj#define FDT_CWD_LEN	256
51208538Sraj#define FDT_MAX_DEPTH	6
52208538Sraj
53208538Sraj#define FDT_PROP_SEP	" = "
54208538Sraj
55235529Skientzle#define COPYOUT(s,d,l)	archsw.arch_copyout(s, d, l)
56235529Skientzle#define COPYIN(s,d,l)	archsw.arch_copyin(s, d, l)
57233230Sraj
58233230Sraj#define FDT_STATIC_DTB_SYMBOL	"fdt_static_dtb"
59233230Sraj
60243693Sgonzo#define	CMD_REQUIRES_BLOB	0x01
61243693Sgonzo
62247201Skientzle/* Location of FDT yet to be loaded. */
63247250Skientzle/* This may be in read-only memory, so can't be manipulated directly. */
64247201Skientzlestatic struct fdt_header *fdt_to_load = NULL;
65247250Skientzle/* Location of FDT on heap. */
66247250Skientzle/* This is the copy we actually manipulate. */
67208538Srajstatic struct fdt_header *fdtp = NULL;
68235529Skientzle/* Size of FDT blob */
69235529Skientzlestatic size_t fdtp_size = 0;
70247250Skientzle/* Location of FDT in kernel or module. */
71247250Skientzle/* This won't be set if FDT is loaded from disk or memory. */
72247250Skientzle/* If it is set, we'll update it when fdt_copy() gets called. */
73235529Skientzlestatic vm_offset_t fdtp_va = 0;
74208538Sraj
75243693Sgonzostatic int fdt_load_dtb(vm_offset_t va);
76243693Sgonzo
77208538Srajstatic int fdt_cmd_nyi(int argc, char *argv[]);
78208538Sraj
79243693Sgonzostatic int fdt_cmd_addr(int argc, char *argv[]);
80208538Srajstatic int fdt_cmd_mkprop(int argc, char *argv[]);
81208538Srajstatic int fdt_cmd_cd(int argc, char *argv[]);
82208538Srajstatic int fdt_cmd_hdr(int argc, char *argv[]);
83208538Srajstatic int fdt_cmd_ls(int argc, char *argv[]);
84208538Srajstatic int fdt_cmd_prop(int argc, char *argv[]);
85208538Srajstatic int fdt_cmd_pwd(int argc, char *argv[]);
86208538Srajstatic int fdt_cmd_rm(int argc, char *argv[]);
87208538Srajstatic int fdt_cmd_mknode(int argc, char *argv[]);
88243693Sgonzostatic int fdt_cmd_mres(int argc, char *argv[]);
89208538Sraj
90208538Srajtypedef int cmdf_t(int, char *[]);
91208538Sraj
92208538Srajstruct cmdtab {
93275762Sandrew	const char	*name;
94275762Sandrew	cmdf_t		*handler;
95275762Sandrew	int		flags;
96208538Sraj};
97208538Sraj
98208538Srajstatic const struct cmdtab commands[] = {
99243693Sgonzo	{ "addr", &fdt_cmd_addr,	0 },
100243693Sgonzo	{ "alias", &fdt_cmd_nyi,	0 },
101243693Sgonzo	{ "cd", &fdt_cmd_cd,		CMD_REQUIRES_BLOB },
102243693Sgonzo	{ "header", &fdt_cmd_hdr,	CMD_REQUIRES_BLOB },
103243693Sgonzo	{ "ls", &fdt_cmd_ls,		CMD_REQUIRES_BLOB },
104243693Sgonzo	{ "mknode", &fdt_cmd_mknode,	CMD_REQUIRES_BLOB },
105243693Sgonzo	{ "mkprop", &fdt_cmd_mkprop,	CMD_REQUIRES_BLOB },
106243693Sgonzo	{ "mres", &fdt_cmd_mres,	CMD_REQUIRES_BLOB },
107243693Sgonzo	{ "prop", &fdt_cmd_prop,	CMD_REQUIRES_BLOB },
108243693Sgonzo	{ "pwd", &fdt_cmd_pwd,		CMD_REQUIRES_BLOB },
109243693Sgonzo	{ "rm", &fdt_cmd_rm,		CMD_REQUIRES_BLOB },
110208538Sraj	{ NULL, NULL }
111208538Sraj};
112208538Sraj
113208538Srajstatic char cwd[FDT_CWD_LEN] = "/";
114208538Sraj
115233230Srajstatic vm_offset_t
116235529Skientzlefdt_find_static_dtb()
117233230Sraj{
118248121Sian	Elf_Ehdr *ehdr;
119248121Sian	Elf_Shdr *shdr;
120233230Sraj	Elf_Sym sym;
121248121Sian	vm_offset_t strtab, symtab, fdt_start;
122233230Sraj	uint64_t offs;
123233230Sraj	struct preloaded_file *kfp;
124233230Sraj	struct file_metadata *md;
125235529Skientzle	char *strp;
126248121Sian	int i, sym_count;
127233230Sraj
128265068Sian	debugf("fdt_find_static_dtb()\n");
129265068Sian
130248934Skientzle	sym_count = symtab = strtab = 0;
131235529Skientzle	strp = NULL;
132233230Sraj
133233230Sraj	offs = __elfN(relocation_offset);
134233230Sraj
135233230Sraj	kfp = file_findfile(NULL, NULL);
136233230Sraj	if (kfp == NULL)
137233230Sraj		return (0);
138233230Sraj
139248121Sian	/* Locate the dynamic symbols and strtab. */
140248121Sian	md = file_findmetadata(kfp, MODINFOMD_ELFHDR);
141233230Sraj	if (md == NULL)
142233230Sraj		return (0);
143248121Sian	ehdr = (Elf_Ehdr *)md->md_data;
144233230Sraj
145248121Sian	md = file_findmetadata(kfp, MODINFOMD_SHDR);
146233230Sraj	if (md == NULL)
147233230Sraj		return (0);
148248121Sian	shdr = (Elf_Shdr *)md->md_data;
149233230Sraj
150248121Sian	for (i = 0; i < ehdr->e_shnum; ++i) {
151248121Sian		if (shdr[i].sh_type == SHT_DYNSYM && symtab == 0) {
152248121Sian			symtab = shdr[i].sh_addr + offs;
153248121Sian			sym_count = shdr[i].sh_size / sizeof(Elf_Sym);
154248121Sian		} else if (shdr[i].sh_type == SHT_STRTAB && strtab == 0) {
155248121Sian			strtab = shdr[i].sh_addr + offs;
156233230Sraj		}
157233230Sraj	}
158233230Sraj
159233230Sraj	/*
160233230Sraj	 * The most efficent way to find a symbol would be to calculate a
161233230Sraj	 * hash, find proper bucket and chain, and thus find a symbol.
162233230Sraj	 * However, that would involve code duplication (e.g. for hash
163233230Sraj	 * function). So we're using simpler and a bit slower way: we're
164233230Sraj	 * iterating through symbols, searching for the one which name is
165233230Sraj	 * 'equal' to 'fdt_static_dtb'. To speed up the process a little bit,
166233230Sraj	 * we are eliminating symbols type of which is not STT_NOTYPE, or(and)
167233230Sraj	 * those which binding attribute is not STB_GLOBAL.
168233230Sraj	 */
169235529Skientzle	fdt_start = 0;
170235529Skientzle	while (sym_count > 0 && fdt_start == 0) {
171235529Skientzle		COPYOUT(symtab, &sym, sizeof(sym));
172235529Skientzle		symtab += sizeof(sym);
173235529Skientzle		--sym_count;
174233230Sraj		if (ELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
175233230Sraj		    ELF_ST_TYPE(sym.st_info) != STT_NOTYPE)
176233230Sraj			continue;
177235529Skientzle		strp = strdupout(strtab + sym.st_name);
178235529Skientzle		if (strcmp(strp, FDT_STATIC_DTB_SYMBOL) == 0)
179235529Skientzle			fdt_start = (vm_offset_t)sym.st_value + offs;
180233230Sraj		free(strp);
181233230Sraj	}
182235529Skientzle	return (fdt_start);
183233230Sraj}
184233230Sraj
185208538Srajstatic int
186243693Sgonzofdt_load_dtb(vm_offset_t va)
187208538Sraj{
188235529Skientzle	struct fdt_header header;
189208538Sraj	int err;
190208538Sraj
191265068Sian	debugf("fdt_load_dtb(0x%08jx)\n", (uintmax_t)va);
192265068Sian
193243693Sgonzo	COPYOUT(va, &header, sizeof(header));
194243693Sgonzo	err = fdt_check_header(&header);
195243693Sgonzo	if (err < 0) {
196243693Sgonzo		if (err == -FDT_ERR_BADVERSION)
197243693Sgonzo			sprintf(command_errbuf,
198243693Sgonzo			    "incompatible blob version: %d, should be: %d",
199243693Sgonzo			    fdt_version(fdtp), FDT_LAST_SUPPORTED_VERSION);
200243693Sgonzo
201243693Sgonzo		else
202243693Sgonzo			sprintf(command_errbuf, "error validating blob: %s",
203243693Sgonzo			    fdt_strerror(err));
204243693Sgonzo		return (1);
205243693Sgonzo	}
206243693Sgonzo
207208538Sraj	/*
208243693Sgonzo	 * Release previous blob
209208538Sraj	 */
210243693Sgonzo	if (fdtp)
211243693Sgonzo		free(fdtp);
212208538Sraj
213235529Skientzle	fdtp_size = fdt_totalsize(&header);
214235529Skientzle	fdtp = malloc(fdtp_size);
215243693Sgonzo
216235529Skientzle	if (fdtp == NULL) {
217235529Skientzle		command_errmsg = "can't allocate memory for device tree copy";
218243693Sgonzo		return (1);
219235529Skientzle	}
220235529Skientzle
221243693Sgonzo	fdtp_va = va;
222243693Sgonzo	COPYOUT(va, fdtp, fdtp_size);
223243693Sgonzo	debugf("DTB blob found at 0x%jx, size: 0x%jx\n", (uintmax_t)va, (uintmax_t)fdtp_size);
224208538Sraj
225243693Sgonzo	return (0);
226243693Sgonzo}
227243693Sgonzo
228275763Sandrewint
229247045Skientzlefdt_load_dtb_addr(struct fdt_header *header)
230243693Sgonzo{
231265065Sian	int err;
232243693Sgonzo
233265068Sian	debugf("fdt_load_dtb_addr(0x%p)\n", header);
234265068Sian
235247250Skientzle	fdtp_size = fdt_totalsize(header);
236265065Sian	err = fdt_check_header(header);
237265065Sian	if (err < 0) {
238265065Sian		sprintf(command_errbuf, "error validating blob: %s",
239265065Sian		    fdt_strerror(err));
240265065Sian		return (err);
241265065Sian	}
242247250Skientzle	free(fdtp);
243247250Skientzle	if ((fdtp = malloc(fdtp_size)) == NULL) {
244247250Skientzle		command_errmsg = "can't allocate memory for device tree copy";
245247045Skientzle		return (1);
246208538Sraj	}
247247250Skientzle
248247250Skientzle	fdtp_va = 0; // Don't write this back into module or kernel.
249247250Skientzle	bcopy(header, fdtp, fdtp_size);
250247250Skientzle	return (0);
251247045Skientzle}
252243693Sgonzo
253275763Sandrewint
254265068Sianfdt_load_dtb_file(const char * filename)
255265068Sian{
256265068Sian	struct preloaded_file *bfp, *oldbfp;
257265068Sian	int err;
258265068Sian
259265068Sian	debugf("fdt_load_dtb_file(%s)\n", filename);
260265068Sian
261265068Sian	oldbfp = file_findfile(NULL, "dtb");
262265068Sian
263265068Sian	/* Attempt to load and validate a new dtb from a file. */
264265068Sian	if ((bfp = file_loadraw(filename, "dtb")) == NULL) {
265265068Sian		sprintf(command_errbuf, "failed to load file '%s'", filename);
266265068Sian		return (1);
267265068Sian	}
268265068Sian	if ((err = fdt_load_dtb(bfp->f_addr)) != 0) {
269265068Sian		file_discard(bfp);
270265068Sian		return (err);
271265068Sian	}
272265068Sian
273265068Sian	/* A new dtb was validated, discard any previous file. */
274265068Sian	if (oldbfp)
275265068Sian		file_discard(oldbfp);
276265068Sian	return (0);
277265068Sian}
278265068Sian
279265069Sianint
280247045Skientzlefdt_setup_fdtp()
281247045Skientzle{
282265066Sian	struct preloaded_file *bfp;
283265066Sian	vm_offset_t va;
284265066Sian
285265068Sian	debugf("fdt_setup_fdtp()\n");
286265068Sian
287265068Sian	/* If we already loaded a file, use it. */
288265066Sian	if ((bfp = file_findfile(NULL, "dtb")) != NULL) {
289265068Sian		if (fdt_load_dtb(bfp->f_addr) == 0) {
290265068Sian			printf("Using DTB from loaded file '%s'.\n",
291265068Sian			    bfp->f_name);
292265068Sian			return (0);
293265068Sian		}
294265066Sian	}
295265068Sian
296265068Sian	/* If we were given the address of a valid blob in memory, use it. */
297265066Sian	if (fdt_to_load != NULL) {
298265068Sian		if (fdt_load_dtb_addr(fdt_to_load) == 0) {
299265068Sian			printf("Using DTB from memory address 0x%08X.\n",
300265068Sian			    (unsigned int)fdt_to_load);
301265068Sian			return (0);
302265068Sian		}
303265066Sian	}
304247045Skientzle
305275763Sandrew	if (fdt_platform_load_dtb() == 0)
306275763Sandrew		return (0);
307265068Sian
308265068Sian	/* If there is a dtb compiled into the kernel, use it. */
309265066Sian	if ((va = fdt_find_static_dtb()) != 0) {
310265068Sian		if (fdt_load_dtb(va) == 0) {
311265068Sian			printf("Using DTB compiled into kernel.\n");
312265068Sian			return (0);
313265068Sian		}
314265066Sian	}
315265066Sian
316265068Sian	command_errmsg = "No device tree blob found!\n";
317265066Sian	return (1);
318208538Sraj}
319208538Sraj
320208538Sraj#define fdt_strtovect(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
321208538Sraj    (cellbuf), (lim), (cellsize), 0);
322208538Sraj
323208538Sraj/* Force using base 16 */
324208538Sraj#define fdt_strtovectx(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
325208538Sraj    (cellbuf), (lim), (cellsize), 16);
326208538Sraj
327208538Srajstatic int
328275762Sandrew_fdt_strtovect(const char *str, void *cellbuf, int lim, unsigned char cellsize,
329208538Sraj    uint8_t base)
330208538Sraj{
331275762Sandrew	const char *buf = str;
332275762Sandrew	const char *end = str + strlen(str) - 2;
333208538Sraj	uint32_t *u32buf = NULL;
334208538Sraj	uint8_t *u8buf = NULL;
335208538Sraj	int cnt = 0;
336208538Sraj
337208538Sraj	if (cellsize == sizeof(uint32_t))
338208538Sraj		u32buf = (uint32_t *)cellbuf;
339208538Sraj	else
340208538Sraj		u8buf = (uint8_t *)cellbuf;
341208538Sraj
342208538Sraj	if (lim == 0)
343208538Sraj		return (0);
344208538Sraj
345208538Sraj	while (buf < end) {
346208538Sraj
347208538Sraj		/* Skip white whitespace(s)/separators */
348208538Sraj		while (!isxdigit(*buf) && buf < end)
349208538Sraj			buf++;
350208538Sraj
351208538Sraj		if (u32buf != NULL)
352208538Sraj			u32buf[cnt] =
353208538Sraj			    cpu_to_fdt32((uint32_t)strtol(buf, NULL, base));
354208538Sraj
355208538Sraj		else
356208538Sraj			u8buf[cnt] = (uint8_t)strtol(buf, NULL, base);
357208538Sraj
358208538Sraj		if (cnt + 1 <= lim - 1)
359208538Sraj			cnt++;
360208538Sraj		else
361208538Sraj			break;
362208538Sraj		buf++;
363208538Sraj		/* Find another number */
364208538Sraj		while ((isxdigit(*buf) || *buf == 'x') && buf < end)
365208538Sraj			buf++;
366208538Sraj	}
367208538Sraj	return (cnt);
368208538Sraj}
369208538Sraj
370275763Sandrewvoid
371275763Sandrewfdt_fixup_ethernet(const char *str, char *ethstr, int len)
372208538Sraj{
373208538Sraj	uint8_t tmp_addr[6];
374208538Sraj
375208538Sraj	/* Convert macaddr string into a vector of uints */
376208538Sraj	fdt_strtovectx(str, &tmp_addr, 6, sizeof(uint8_t));
377208538Sraj	/* Set actual property to a value from vect */
378208538Sraj	fdt_setprop(fdtp, fdt_path_offset(fdtp, ethstr),
379208538Sraj	    "local-mac-address", &tmp_addr, 6 * sizeof(uint8_t));
380208538Sraj}
381208538Sraj
382275763Sandrewvoid
383275763Sandrewfdt_fixup_cpubusfreqs(unsigned long cpufreq, unsigned long busfreq)
384208538Sraj{
385208538Sraj	int lo, o = 0, o2, maxo = 0, depth;
386208538Sraj	const uint32_t zero = 0;
387208538Sraj
388208538Sraj	/* We want to modify every subnode of /cpus */
389208538Sraj	o = fdt_path_offset(fdtp, "/cpus");
390235261Skientzle	if (o < 0)
391235261Skientzle		return;
392208538Sraj
393208538Sraj	/* maxo should contain offset of node next to /cpus */
394208538Sraj	depth = 0;
395208538Sraj	maxo = o;
396208538Sraj	while (depth != -1)
397208538Sraj		maxo = fdt_next_node(fdtp, maxo, &depth);
398208538Sraj
399208538Sraj	/* Find CPU frequency properties */
400208538Sraj	o = fdt_node_offset_by_prop_value(fdtp, o, "clock-frequency",
401208538Sraj	    &zero, sizeof(uint32_t));
402208538Sraj
403208538Sraj	o2 = fdt_node_offset_by_prop_value(fdtp, o, "bus-frequency", &zero,
404208538Sraj	    sizeof(uint32_t));
405208538Sraj
406208538Sraj	lo = MIN(o, o2);
407208538Sraj
408208538Sraj	while (o != -FDT_ERR_NOTFOUND && o2 != -FDT_ERR_NOTFOUND) {
409208538Sraj
410208538Sraj		o = fdt_node_offset_by_prop_value(fdtp, lo,
411208538Sraj		    "clock-frequency", &zero, sizeof(uint32_t));
412208538Sraj
413208538Sraj		o2 = fdt_node_offset_by_prop_value(fdtp, lo, "bus-frequency",
414208538Sraj		    &zero, sizeof(uint32_t));
415208538Sraj
416208538Sraj		/* We're only interested in /cpus subnode(s) */
417208538Sraj		if (lo > maxo)
418208538Sraj			break;
419208538Sraj
420208538Sraj		fdt_setprop_inplace_cell(fdtp, lo, "clock-frequency",
421208538Sraj		    (uint32_t)cpufreq);
422208538Sraj
423208538Sraj		fdt_setprop_inplace_cell(fdtp, lo, "bus-frequency",
424208538Sraj		    (uint32_t)busfreq);
425208538Sraj
426208538Sraj		lo = MIN(o, o2);
427208538Sraj	}
428208538Sraj}
429208538Sraj
430247250Skientzlestatic int
431208538Srajfdt_reg_valid(uint32_t *reg, int len, int addr_cells, int size_cells)
432208538Sraj{
433208538Sraj	int cells_in_tuple, i, tuples, tuple_size;
434208538Sraj	uint32_t cur_start, cur_size;
435208538Sraj
436208538Sraj	cells_in_tuple = (addr_cells + size_cells);
437208538Sraj	tuple_size = cells_in_tuple * sizeof(uint32_t);
438208538Sraj	tuples = len / tuple_size;
439208538Sraj	if (tuples == 0)
440208538Sraj		return (EINVAL);
441208538Sraj
442208538Sraj	for (i = 0; i < tuples; i++) {
443208538Sraj		if (addr_cells == 2)
444208538Sraj			cur_start = fdt64_to_cpu(reg[i * cells_in_tuple]);
445208538Sraj		else
446208538Sraj			cur_start = fdt32_to_cpu(reg[i * cells_in_tuple]);
447208538Sraj
448208538Sraj		if (size_cells == 2)
449208538Sraj			cur_size = fdt64_to_cpu(reg[i * cells_in_tuple + 2]);
450208538Sraj		else
451208538Sraj			cur_size = fdt32_to_cpu(reg[i * cells_in_tuple + 1]);
452208538Sraj
453208538Sraj		if (cur_size == 0)
454208538Sraj			return (EINVAL);
455208538Sraj
456208538Sraj		debugf(" reg#%d (start: 0x%0x size: 0x%0x) valid!\n",
457208538Sraj		    i, cur_start, cur_size);
458208538Sraj	}
459208538Sraj	return (0);
460208538Sraj}
461208538Sraj
462275763Sandrewvoid
463275763Sandrewfdt_fixup_memory(struct fdt_mem_region *region, size_t num)
464208538Sraj{
465275763Sandrew	struct fdt_mem_region *curmr;
466208538Sraj	uint32_t addr_cells, size_cells;
467208538Sraj	uint32_t *addr_cellsp, *reg,  *size_cellsp;
468275763Sandrew	int err, i, len, memory, root;
469275763Sandrew	size_t realmrno;
470208538Sraj	uint8_t *buf, *sb;
471243693Sgonzo	uint64_t rstart, rsize;
472243693Sgonzo	int reserved;
473208538Sraj
474208538Sraj	root = fdt_path_offset(fdtp, "/");
475208538Sraj	if (root < 0) {
476208538Sraj		sprintf(command_errbuf, "Could not find root node !");
477208538Sraj		return;
478208538Sraj	}
479208538Sraj
480208538Sraj	memory = fdt_path_offset(fdtp, "/memory");
481208538Sraj	if (memory <= 0) {
482208538Sraj		/* Create proper '/memory' node. */
483208538Sraj		memory = fdt_add_subnode(fdtp, root, "memory");
484208538Sraj		if (memory <= 0) {
485208538Sraj			sprintf(command_errbuf, "Could not fixup '/memory' "
486208538Sraj			    "node, error code : %d!\n", memory);
487208538Sraj			return;
488208538Sraj		}
489208538Sraj
490208538Sraj		err = fdt_setprop(fdtp, memory, "device_type", "memory",
491208538Sraj		    sizeof("memory"));
492208538Sraj
493208538Sraj		if (err < 0)
494208538Sraj			return;
495208538Sraj	}
496208538Sraj
497208538Sraj	addr_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#address-cells",
498208538Sraj	    NULL);
499208538Sraj	size_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#size-cells", NULL);
500208538Sraj
501208538Sraj	if (addr_cellsp == NULL || size_cellsp == NULL) {
502208538Sraj		sprintf(command_errbuf, "Could not fixup '/memory' node : "
503208538Sraj		    "%s %s property not found in root node!\n",
504208538Sraj		    (!addr_cellsp) ? "#address-cells" : "",
505208538Sraj		    (!size_cellsp) ? "#size-cells" : "");
506208538Sraj		return;
507208538Sraj	}
508208538Sraj
509208538Sraj	addr_cells = fdt32_to_cpu(*addr_cellsp);
510208538Sraj	size_cells = fdt32_to_cpu(*size_cellsp);
511208538Sraj
512243693Sgonzo	/*
513243693Sgonzo	 * Convert memreserve data to memreserve property
514243693Sgonzo	 * Check if property already exists
515243693Sgonzo	 */
516243693Sgonzo	reserved = fdt_num_mem_rsv(fdtp);
517243693Sgonzo	if (reserved &&
518243693Sgonzo	    (fdt_getprop(fdtp, root, "memreserve", NULL) == NULL)) {
519243693Sgonzo		len = (addr_cells + size_cells) * reserved * sizeof(uint32_t);
520243693Sgonzo		sb = buf = (uint8_t *)malloc(len);
521243693Sgonzo		if (!buf)
522243693Sgonzo			return;
523243693Sgonzo
524243693Sgonzo		bzero(buf, len);
525243693Sgonzo
526243693Sgonzo		for (i = 0; i < reserved; i++) {
527243693Sgonzo			if (fdt_get_mem_rsv(fdtp, i, &rstart, &rsize))
528243693Sgonzo				break;
529243693Sgonzo			if (rsize) {
530243693Sgonzo				/* Ensure endianess, and put cells into a buffer */
531243693Sgonzo				if (addr_cells == 2)
532243693Sgonzo					*(uint64_t *)buf =
533243693Sgonzo					    cpu_to_fdt64(rstart);
534243693Sgonzo				else
535243693Sgonzo					*(uint32_t *)buf =
536243693Sgonzo					    cpu_to_fdt32(rstart);
537243693Sgonzo
538243693Sgonzo				buf += sizeof(uint32_t) * addr_cells;
539243693Sgonzo				if (size_cells == 2)
540243693Sgonzo					*(uint64_t *)buf =
541243693Sgonzo					    cpu_to_fdt64(rsize);
542243693Sgonzo				else
543243693Sgonzo					*(uint32_t *)buf =
544243693Sgonzo					    cpu_to_fdt32(rsize);
545243693Sgonzo
546243693Sgonzo				buf += sizeof(uint32_t) * size_cells;
547243693Sgonzo			}
548243693Sgonzo		}
549243693Sgonzo
550243693Sgonzo		/* Set property */
551243693Sgonzo		if ((err = fdt_setprop(fdtp, root, "memreserve", sb, len)) < 0)
552243693Sgonzo			printf("Could not fixup 'memreserve' property.\n");
553243693Sgonzo
554243693Sgonzo		free(sb);
555243693Sgonzo	}
556243693Sgonzo
557208538Sraj	/* Count valid memory regions entries in sysinfo. */
558275763Sandrew	realmrno = num;
559275763Sandrew	for (i = 0; i < num; i++)
560275763Sandrew		if (region[i].start == 0 && region[i].size == 0)
561208538Sraj			realmrno--;
562208538Sraj
563208538Sraj	if (realmrno == 0) {
564208538Sraj		sprintf(command_errbuf, "Could not fixup '/memory' node : "
565208538Sraj		    "sysinfo doesn't contain valid memory regions info!\n");
566208538Sraj		return;
567208538Sraj	}
568208538Sraj
569208538Sraj	if ((reg = (uint32_t *)fdt_getprop(fdtp, memory, "reg",
570208538Sraj	    &len)) != NULL) {
571208538Sraj
572208538Sraj		if (fdt_reg_valid(reg, len, addr_cells, size_cells) == 0)
573208538Sraj			/*
574208538Sraj			 * Do not apply fixup if existing 'reg' property
575208538Sraj			 * seems to be valid.
576208538Sraj			 */
577208538Sraj			return;
578208538Sraj	}
579208538Sraj
580208538Sraj	len = (addr_cells + size_cells) * realmrno * sizeof(uint32_t);
581208538Sraj	sb = buf = (uint8_t *)malloc(len);
582208538Sraj	if (!buf)
583208538Sraj		return;
584208538Sraj
585208538Sraj	bzero(buf, len);
586208538Sraj
587275763Sandrew	for (i = 0; i < num; i++) {
588275763Sandrew		curmr = &region[i];
589208538Sraj		if (curmr->size != 0) {
590208538Sraj			/* Ensure endianess, and put cells into a buffer */
591208538Sraj			if (addr_cells == 2)
592208538Sraj				*(uint64_t *)buf =
593208538Sraj				    cpu_to_fdt64(curmr->start);
594208538Sraj			else
595208538Sraj				*(uint32_t *)buf =
596208538Sraj				    cpu_to_fdt32(curmr->start);
597208538Sraj
598208538Sraj			buf += sizeof(uint32_t) * addr_cells;
599208538Sraj			if (size_cells == 2)
600208538Sraj				*(uint64_t *)buf =
601208538Sraj				    cpu_to_fdt64(curmr->size);
602208538Sraj			else
603208538Sraj				*(uint32_t *)buf =
604208538Sraj				    cpu_to_fdt32(curmr->size);
605208538Sraj
606208538Sraj			buf += sizeof(uint32_t) * size_cells;
607208538Sraj		}
608208538Sraj	}
609208538Sraj
610208538Sraj	/* Set property */
611208538Sraj	if ((err = fdt_setprop(fdtp, memory, "reg", sb, len)) < 0)
612208538Sraj		sprintf(command_errbuf, "Could not fixup '/memory' node.\n");
613243693Sgonzo
614243693Sgonzo	free(sb);
615208538Sraj}
616208538Sraj
617275763Sandrewvoid
618275763Sandrewfdt_fixup_stdout(const char *str)
619208538Sraj{
620208538Sraj	char *ptr;
621208538Sraj	int serialno;
622208538Sraj	int len, no, sero;
623208538Sraj	const struct fdt_property *prop;
624208538Sraj	char *tmp[10];
625208538Sraj
626208538Sraj	ptr = (char *)str + strlen(str) - 1;
627208538Sraj	while (ptr > str && isdigit(*(str - 1)))
628208538Sraj		str--;
629208538Sraj
630208538Sraj	if (ptr == str)
631208538Sraj		return;
632208538Sraj
633208538Sraj	serialno = (int)strtol(ptr, NULL, 0);
634208538Sraj	no = fdt_path_offset(fdtp, "/chosen");
635208538Sraj	if (no < 0)
636208538Sraj		return;
637208538Sraj
638208538Sraj	prop = fdt_get_property(fdtp, no, "stdout", &len);
639208538Sraj
640208538Sraj	/* If /chosen/stdout does not extist, create it */
641208538Sraj	if (prop == NULL || (prop != NULL && len == 0)) {
642208538Sraj
643208538Sraj		bzero(tmp, 10 * sizeof(char));
644208538Sraj		strcpy((char *)&tmp, "serial");
645208538Sraj		if (strlen(ptr) > 3)
646208538Sraj			/* Serial number too long */
647208538Sraj			return;
648208538Sraj
649208538Sraj		strncpy((char *)tmp + 6, ptr, 3);
650208538Sraj		sero = fdt_path_offset(fdtp, (const char *)tmp);
651208538Sraj		if (sero < 0)
652208538Sraj			/*
653208538Sraj			 * If serial device we're trying to assign
654208538Sraj			 * stdout to doesn't exist in DT -- return.
655208538Sraj			 */
656208538Sraj			return;
657208538Sraj
658208538Sraj		fdt_setprop(fdtp, no, "stdout", &tmp,
659208538Sraj		    strlen((char *)&tmp) + 1);
660208538Sraj		fdt_setprop(fdtp, no, "stdin", &tmp,
661208538Sraj		    strlen((char *)&tmp) + 1);
662208538Sraj	}
663208538Sraj}
664208538Sraj
665233230Sraj/*
666233230Sraj * Locate the blob, fix it up and return its location.
667233230Sraj */
668247250Skientzlestatic int
669208538Srajfdt_fixup(void)
670208538Sraj{
671275763Sandrew	int chosen, len;
672208538Sraj
673208538Sraj	len = 0;
674208538Sraj
675265068Sian	debugf("fdt_fixup()\n");
676265068Sian
677265065Sian	if (fdtp == NULL && fdt_setup_fdtp() != 0)
678265065Sian		return (0);
679208538Sraj
680208538Sraj	/* Create /chosen node (if not exists) */
681208538Sraj	if ((chosen = fdt_subnode_offset(fdtp, 0, "chosen")) ==
682208538Sraj	    -FDT_ERR_NOTFOUND)
683208538Sraj		chosen = fdt_add_subnode(fdtp, 0, "chosen");
684208538Sraj
685208538Sraj	/* Value assigned to fixup-applied does not matter. */
686208538Sraj	if (fdt_getprop(fdtp, chosen, "fixup-applied", NULL))
687247250Skientzle		return (1);
688208538Sraj
689275763Sandrew	fdt_platform_fixups();
690208538Sraj
691208538Sraj	fdt_setprop(fdtp, chosen, "fixup-applied", NULL, 0);
692247250Skientzle	return (1);
693208538Sraj}
694208538Sraj
695243693Sgonzo/*
696247250Skientzle * Copy DTB blob to specified location and return size
697243693Sgonzo */
698208538Srajint
699243693Sgonzofdt_copy(vm_offset_t va)
700243693Sgonzo{
701243693Sgonzo	int err;
702265068Sian	debugf("fdt_copy va 0x%08x\n", va);
703243693Sgonzo	if (fdtp == NULL) {
704243693Sgonzo		err = fdt_setup_fdtp();
705243693Sgonzo		if (err) {
706265065Sian			printf("No valid device tree blob found!\n");
707243693Sgonzo			return (0);
708243693Sgonzo		}
709243693Sgonzo	}
710243693Sgonzo
711243693Sgonzo	if (fdt_fixup() == 0)
712243693Sgonzo		return (0);
713243693Sgonzo
714247250Skientzle	if (fdtp_va != 0) {
715247250Skientzle		/* Overwrite the FDT with the fixed version. */
716247250Skientzle		/* XXX Is this really appropriate? */
717247250Skientzle		COPYIN(fdtp, fdtp_va, fdtp_size);
718247250Skientzle	}
719243693Sgonzo	COPYIN(fdtp, va, fdtp_size);
720243693Sgonzo	return (fdtp_size);
721243693Sgonzo}
722243693Sgonzo
723243693Sgonzo
724243693Sgonzo
725243693Sgonzoint
726208538Srajcommand_fdt_internal(int argc, char *argv[])
727208538Sraj{
728208538Sraj	cmdf_t *cmdh;
729243693Sgonzo	int flags;
730208538Sraj	char *cmd;
731208538Sraj	int i, err;
732208538Sraj
733208538Sraj	if (argc < 2) {
734208538Sraj		command_errmsg = "usage is 'fdt <command> [<args>]";
735208538Sraj		return (CMD_ERROR);
736208538Sraj	}
737208538Sraj
738208538Sraj	/*
739208538Sraj	 * Validate fdt <command>.
740208538Sraj	 */
741208538Sraj	cmd = strdup(argv[1]);
742208538Sraj	i = 0;
743208538Sraj	cmdh = NULL;
744208538Sraj	while (!(commands[i].name == NULL)) {
745208538Sraj		if (strcmp(cmd, commands[i].name) == 0) {
746208538Sraj			/* found it */
747208538Sraj			cmdh = commands[i].handler;
748243693Sgonzo			flags = commands[i].flags;
749208538Sraj			break;
750208538Sraj		}
751208538Sraj		i++;
752208538Sraj	}
753208538Sraj	if (cmdh == NULL) {
754208538Sraj		command_errmsg = "unknown command";
755208538Sraj		return (CMD_ERROR);
756208538Sraj	}
757208538Sraj
758243693Sgonzo	if (flags & CMD_REQUIRES_BLOB) {
759243693Sgonzo		/*
760243693Sgonzo		 * Check if uboot env vars were parsed already. If not, do it now.
761243693Sgonzo		 */
762243693Sgonzo		if (fdt_fixup() == 0)
763243693Sgonzo			return (CMD_ERROR);
764243693Sgonzo	}
765243693Sgonzo
766208538Sraj	/*
767208538Sraj	 * Call command handler.
768208538Sraj	 */
769208538Sraj	err = (*cmdh)(argc, argv);
770208538Sraj
771208538Sraj	return (err);
772208538Sraj}
773208538Sraj
774208538Srajstatic int
775243693Sgonzofdt_cmd_addr(int argc, char *argv[])
776243693Sgonzo{
777247201Skientzle	struct preloaded_file *fp;
778247045Skientzle	struct fdt_header *hdr;
779247201Skientzle	const char *addr;
780247201Skientzle	char *cp;
781243693Sgonzo
782247201Skientzle	fdt_to_load = NULL;
783247201Skientzle
784243693Sgonzo	if (argc > 2)
785243693Sgonzo		addr = argv[2];
786243693Sgonzo	else {
787243693Sgonzo		sprintf(command_errbuf, "no address specified");
788243693Sgonzo		return (CMD_ERROR);
789243693Sgonzo	}
790243693Sgonzo
791247201Skientzle	hdr = (struct fdt_header *)strtoul(addr, &cp, 16);
792243693Sgonzo	if (cp == addr) {
793243693Sgonzo		sprintf(command_errbuf, "Invalid address: %s", addr);
794243693Sgonzo		return (CMD_ERROR);
795243693Sgonzo	}
796243693Sgonzo
797247201Skientzle	while ((fp = file_findfile(NULL, "dtb")) != NULL) {
798247201Skientzle		file_discard(fp);
799247201Skientzle	}
800243693Sgonzo
801247201Skientzle	fdt_to_load = hdr;
802243693Sgonzo	return (CMD_OK);
803243693Sgonzo}
804243693Sgonzo
805243693Sgonzostatic int
806208538Srajfdt_cmd_cd(int argc, char *argv[])
807208538Sraj{
808208538Sraj	char *path;
809208538Sraj	char tmp[FDT_CWD_LEN];
810208538Sraj	int len, o;
811208538Sraj
812208538Sraj	path = (argc > 2) ? argv[2] : "/";
813208538Sraj
814208538Sraj	if (path[0] == '/') {
815208538Sraj		len = strlen(path);
816208538Sraj		if (len >= FDT_CWD_LEN)
817208538Sraj			goto fail;
818208538Sraj	} else {
819208538Sraj		/* Handle path specification relative to cwd */
820208538Sraj		len = strlen(cwd) + strlen(path) + 1;
821208538Sraj		if (len >= FDT_CWD_LEN)
822208538Sraj			goto fail;
823208538Sraj
824208538Sraj		strcpy(tmp, cwd);
825208538Sraj		strcat(tmp, "/");
826208538Sraj		strcat(tmp, path);
827208538Sraj		path = tmp;
828208538Sraj	}
829208538Sraj
830208538Sraj	o = fdt_path_offset(fdtp, path);
831208538Sraj	if (o < 0) {
832208538Sraj		sprintf(command_errbuf, "could not find node: '%s'", path);
833208538Sraj		return (CMD_ERROR);
834208538Sraj	}
835208538Sraj
836208538Sraj	strcpy(cwd, path);
837208538Sraj	return (CMD_OK);
838208538Sraj
839208538Srajfail:
840208538Sraj	sprintf(command_errbuf, "path too long: %d, max allowed: %d",
841208538Sraj	    len, FDT_CWD_LEN - 1);
842208538Sraj	return (CMD_ERROR);
843208538Sraj}
844208538Sraj
845208538Srajstatic int
846208538Srajfdt_cmd_hdr(int argc __unused, char *argv[] __unused)
847208538Sraj{
848208538Sraj	char line[80];
849208538Sraj	int ver;
850208538Sraj
851208538Sraj	if (fdtp == NULL) {
852208538Sraj		command_errmsg = "no device tree blob pointer?!";
853208538Sraj		return (CMD_ERROR);
854208538Sraj	}
855208538Sraj
856208538Sraj	ver = fdt_version(fdtp);
857208538Sraj	pager_open();
858208538Sraj	sprintf(line, "\nFlattened device tree header (%p):\n", fdtp);
859208538Sraj	pager_output(line);
860208538Sraj	sprintf(line, " magic                   = 0x%08x\n", fdt_magic(fdtp));
861208538Sraj	pager_output(line);
862208538Sraj	sprintf(line, " size                    = %d\n", fdt_totalsize(fdtp));
863208538Sraj	pager_output(line);
864208538Sraj	sprintf(line, " off_dt_struct           = 0x%08x\n",
865208538Sraj	    fdt_off_dt_struct(fdtp));
866208538Sraj	pager_output(line);
867208538Sraj	sprintf(line, " off_dt_strings          = 0x%08x\n",
868208538Sraj	    fdt_off_dt_strings(fdtp));
869208538Sraj	pager_output(line);
870208538Sraj	sprintf(line, " off_mem_rsvmap          = 0x%08x\n",
871208538Sraj	    fdt_off_mem_rsvmap(fdtp));
872208538Sraj	pager_output(line);
873208538Sraj	sprintf(line, " version                 = %d\n", ver);
874208538Sraj	pager_output(line);
875208538Sraj	sprintf(line, " last compatible version = %d\n",
876208538Sraj	    fdt_last_comp_version(fdtp));
877208538Sraj	pager_output(line);
878208538Sraj	if (ver >= 2) {
879208538Sraj		sprintf(line, " boot_cpuid              = %d\n",
880208538Sraj		    fdt_boot_cpuid_phys(fdtp));
881208538Sraj		pager_output(line);
882208538Sraj	}
883208538Sraj	if (ver >= 3) {
884208538Sraj		sprintf(line, " size_dt_strings         = %d\n",
885208538Sraj		    fdt_size_dt_strings(fdtp));
886208538Sraj		pager_output(line);
887208538Sraj	}
888208538Sraj	if (ver >= 17) {
889208538Sraj		sprintf(line, " size_dt_struct          = %d\n",
890208538Sraj		    fdt_size_dt_struct(fdtp));
891208538Sraj		pager_output(line);
892208538Sraj	}
893208538Sraj	pager_close();
894208538Sraj
895208538Sraj	return (CMD_OK);
896208538Sraj}
897208538Sraj
898208538Srajstatic int
899208538Srajfdt_cmd_ls(int argc, char *argv[])
900208538Sraj{
901208538Sraj	const char *prevname[FDT_MAX_DEPTH] = { NULL };
902208538Sraj	const char *name;
903208538Sraj	char *path;
904208538Sraj	int i, o, depth, len;
905208538Sraj
906208538Sraj	path = (argc > 2) ? argv[2] : NULL;
907208538Sraj	if (path == NULL)
908208538Sraj		path = cwd;
909208538Sraj
910208538Sraj	o = fdt_path_offset(fdtp, path);
911208538Sraj	if (o < 0) {
912208538Sraj		sprintf(command_errbuf, "could not find node: '%s'", path);
913208538Sraj		return (CMD_ERROR);
914208538Sraj	}
915208538Sraj
916208538Sraj	for (depth = 0;
917208538Sraj	    (o >= 0) && (depth >= 0);
918208538Sraj	    o = fdt_next_node(fdtp, o, &depth)) {
919208538Sraj
920208538Sraj		name = fdt_get_name(fdtp, o, &len);
921208538Sraj
922208538Sraj		if (depth > FDT_MAX_DEPTH) {
923208538Sraj			printf("max depth exceeded: %d\n", depth);
924208538Sraj			continue;
925208538Sraj		}
926208538Sraj
927208538Sraj		prevname[depth] = name;
928208538Sraj
929208538Sraj		/* Skip root (i = 1) when printing devices */
930208538Sraj		for (i = 1; i <= depth; i++) {
931208538Sraj			if (prevname[i] == NULL)
932208538Sraj				break;
933208538Sraj
934208538Sraj			if (strcmp(cwd, "/") == 0)
935208538Sraj				printf("/");
936208538Sraj			printf("%s", prevname[i]);
937208538Sraj		}
938208538Sraj		printf("\n");
939208538Sraj	}
940208538Sraj
941208538Sraj	return (CMD_OK);
942208538Sraj}
943208538Sraj
944208538Srajstatic __inline int
945208538Srajisprint(int c)
946208538Sraj{
947208538Sraj
948208538Sraj	return (c >= ' ' && c <= 0x7e);
949208538Sraj}
950208538Sraj
951208538Srajstatic int
952208538Srajfdt_isprint(const void *data, int len, int *count)
953208538Sraj{
954208538Sraj	const char *d;
955208538Sraj	char ch;
956208538Sraj	int yesno, i;
957208538Sraj
958208538Sraj	if (len == 0)
959208538Sraj		return (0);
960208538Sraj
961208538Sraj	d = (const char *)data;
962208538Sraj	if (d[len - 1] != '\0')
963208538Sraj		return (0);
964208538Sraj
965208538Sraj	*count = 0;
966208538Sraj	yesno = 1;
967208538Sraj	for (i = 0; i < len; i++) {
968208538Sraj		ch = *(d + i);
969208538Sraj		if (isprint(ch) || (ch == '\0' && i > 0)) {
970208538Sraj			/* Count strings */
971208538Sraj			if (ch == '\0')
972208538Sraj				(*count)++;
973208538Sraj			continue;
974208538Sraj		}
975208538Sraj
976208538Sraj		yesno = 0;
977208538Sraj		break;
978208538Sraj	}
979208538Sraj
980208538Sraj	return (yesno);
981208538Sraj}
982208538Sraj
983208538Srajstatic int
984208538Srajfdt_data_str(const void *data, int len, int count, char **buf)
985208538Sraj{
986233323Sraj	char *b, *tmp;
987208538Sraj	const char *d;
988233323Sraj	int buf_len, i, l;
989208538Sraj
990208538Sraj	/*
991208538Sraj	 * Calculate the length for the string and allocate memory.
992208538Sraj	 *
993233323Sraj	 * Note that 'len' already includes at least one terminator.
994208538Sraj	 */
995233323Sraj	buf_len = len;
996208538Sraj	if (count > 1) {
997208538Sraj		/*
998208538Sraj		 * Each token had already a terminator buried in 'len', but we
999208538Sraj		 * only need one eventually, don't count space for these.
1000208538Sraj		 */
1001233323Sraj		buf_len -= count - 1;
1002208538Sraj
1003208538Sraj		/* Each consecutive token requires a ", " separator. */
1004233323Sraj		buf_len += count * 2;
1005208538Sraj	}
1006208538Sraj
1007233323Sraj	/* Add some space for surrounding double quotes. */
1008233323Sraj	buf_len += count * 2;
1009233323Sraj
1010233323Sraj	/* Note that string being put in 'tmp' may be as big as 'buf_len'. */
1011233323Sraj	b = (char *)malloc(buf_len);
1012233323Sraj	tmp = (char *)malloc(buf_len);
1013208538Sraj	if (b == NULL)
1014233323Sraj		goto error;
1015233323Sraj
1016233323Sraj	if (tmp == NULL) {
1017233323Sraj		free(b);
1018233323Sraj		goto error;
1019233323Sraj	}
1020233323Sraj
1021208538Sraj	b[0] = '\0';
1022208538Sraj
1023208538Sraj	/*
1024208538Sraj	 * Now that we have space, format the string.
1025208538Sraj	 */
1026208538Sraj	i = 0;
1027208538Sraj	do {
1028208538Sraj		d = (const char *)data + i;
1029208538Sraj		l = strlen(d) + 1;
1030208538Sraj
1031208538Sraj		sprintf(tmp, "\"%s\"%s", d,
1032208538Sraj		    (i + l) < len ?  ", " : "");
1033208538Sraj		strcat(b, tmp);
1034208538Sraj
1035208538Sraj		i += l;
1036208538Sraj
1037208538Sraj	} while (i < len);
1038208538Sraj	*buf = b;
1039208538Sraj
1040233323Sraj	free(tmp);
1041233323Sraj
1042208538Sraj	return (0);
1043233323Srajerror:
1044233323Sraj	return (1);
1045208538Sraj}
1046208538Sraj
1047208538Srajstatic int
1048208538Srajfdt_data_cell(const void *data, int len, char **buf)
1049208538Sraj{
1050233323Sraj	char *b, *tmp;
1051208538Sraj	const uint32_t *c;
1052208538Sraj	int count, i, l;
1053208538Sraj
1054208538Sraj	/* Number of cells */
1055208538Sraj	count = len / 4;
1056208538Sraj
1057208538Sraj	/*
1058208538Sraj	 * Calculate the length for the string and allocate memory.
1059208538Sraj	 */
1060208538Sraj
1061208538Sraj	/* Each byte translates to 2 output characters */
1062208538Sraj	l = len * 2;
1063208538Sraj	if (count > 1) {
1064208538Sraj		/* Each consecutive cell requires a " " separator. */
1065208538Sraj		l += (count - 1) * 1;
1066208538Sraj	}
1067208538Sraj	/* Each cell will have a "0x" prefix */
1068208538Sraj	l += count * 2;
1069208538Sraj	/* Space for surrounding <> and terminator */
1070208538Sraj	l += 3;
1071208538Sraj
1072208538Sraj	b = (char *)malloc(l);
1073233323Sraj	tmp = (char *)malloc(l);
1074208538Sraj	if (b == NULL)
1075233323Sraj		goto error;
1076208538Sraj
1077233323Sraj	if (tmp == NULL) {
1078233323Sraj		free(b);
1079233323Sraj		goto error;
1080233323Sraj	}
1081233323Sraj
1082208538Sraj	b[0] = '\0';
1083208538Sraj	strcat(b, "<");
1084208538Sraj
1085208538Sraj	for (i = 0; i < len; i += 4) {
1086208538Sraj		c = (const uint32_t *)((const uint8_t *)data + i);
1087208538Sraj		sprintf(tmp, "0x%08x%s", fdt32_to_cpu(*c),
1088208538Sraj		    i < (len - 4) ? " " : "");
1089208538Sraj		strcat(b, tmp);
1090208538Sraj	}
1091208538Sraj	strcat(b, ">");
1092208538Sraj	*buf = b;
1093208538Sraj
1094233323Sraj	free(tmp);
1095233323Sraj
1096208538Sraj	return (0);
1097233323Srajerror:
1098233323Sraj	return (1);
1099208538Sraj}
1100208538Sraj
1101208538Srajstatic int
1102208538Srajfdt_data_bytes(const void *data, int len, char **buf)
1103208538Sraj{
1104233323Sraj	char *b, *tmp;
1105208538Sraj	const char *d;
1106208538Sraj	int i, l;
1107208538Sraj
1108208538Sraj	/*
1109208538Sraj	 * Calculate the length for the string and allocate memory.
1110208538Sraj	 */
1111208538Sraj
1112208538Sraj	/* Each byte translates to 2 output characters */
1113208538Sraj	l = len * 2;
1114208538Sraj	if (len > 1)
1115208538Sraj		/* Each consecutive byte requires a " " separator. */
1116208538Sraj		l += (len - 1) * 1;
1117208538Sraj	/* Each byte will have a "0x" prefix */
1118208538Sraj	l += len * 2;
1119208538Sraj	/* Space for surrounding [] and terminator. */
1120208538Sraj	l += 3;
1121208538Sraj
1122208538Sraj	b = (char *)malloc(l);
1123233323Sraj	tmp = (char *)malloc(l);
1124208538Sraj	if (b == NULL)
1125233323Sraj		goto error;
1126208538Sraj
1127233323Sraj	if (tmp == NULL) {
1128233323Sraj		free(b);
1129233323Sraj		goto error;
1130233323Sraj	}
1131233323Sraj
1132208538Sraj	b[0] = '\0';
1133208538Sraj	strcat(b, "[");
1134208538Sraj
1135208538Sraj	for (i = 0, d = data; i < len; i++) {
1136208538Sraj		sprintf(tmp, "0x%02x%s", d[i], i < len - 1 ? " " : "");
1137208538Sraj		strcat(b, tmp);
1138208538Sraj	}
1139208538Sraj	strcat(b, "]");
1140208538Sraj	*buf = b;
1141208538Sraj
1142233323Sraj	free(tmp);
1143233323Sraj
1144208538Sraj	return (0);
1145233323Srajerror:
1146233323Sraj	return (1);
1147208538Sraj}
1148208538Sraj
1149208538Srajstatic int
1150208538Srajfdt_data_fmt(const void *data, int len, char **buf)
1151208538Sraj{
1152208538Sraj	int count;
1153208538Sraj
1154208538Sraj	if (len == 0) {
1155208538Sraj		*buf = NULL;
1156208538Sraj		return (1);
1157208538Sraj	}
1158208538Sraj
1159208538Sraj	if (fdt_isprint(data, len, &count))
1160208538Sraj		return (fdt_data_str(data, len, count, buf));
1161208538Sraj
1162208538Sraj	else if ((len % 4) == 0)
1163208538Sraj		return (fdt_data_cell(data, len, buf));
1164208538Sraj
1165208538Sraj	else
1166208538Sraj		return (fdt_data_bytes(data, len, buf));
1167208538Sraj}
1168208538Sraj
1169208538Srajstatic int
1170208538Srajfdt_prop(int offset)
1171208538Sraj{
1172208538Sraj	char *line, *buf;
1173208538Sraj	const struct fdt_property *prop;
1174208538Sraj	const char *name;
1175208538Sraj	const void *data;
1176208538Sraj	int len, rv;
1177208538Sraj
1178208538Sraj	line = NULL;
1179208538Sraj	prop = fdt_offset_ptr(fdtp, offset, sizeof(*prop));
1180208538Sraj	if (prop == NULL)
1181208538Sraj		return (1);
1182208538Sraj
1183208538Sraj	name = fdt_string(fdtp, fdt32_to_cpu(prop->nameoff));
1184208538Sraj	len = fdt32_to_cpu(prop->len);
1185208538Sraj
1186208538Sraj	rv = 0;
1187208538Sraj	buf = NULL;
1188208538Sraj	if (len == 0) {
1189208538Sraj		/* Property without value */
1190208538Sraj		line = (char *)malloc(strlen(name) + 2);
1191208538Sraj		if (line == NULL) {
1192208538Sraj			rv = 2;
1193208538Sraj			goto out2;
1194208538Sraj		}
1195208538Sraj		sprintf(line, "%s\n", name);
1196208538Sraj		goto out1;
1197208538Sraj	}
1198208538Sraj
1199208538Sraj	/*
1200208538Sraj	 * Process property with value
1201208538Sraj	 */
1202208538Sraj	data = prop->data;
1203208538Sraj
1204208538Sraj	if (fdt_data_fmt(data, len, &buf) != 0) {
1205208538Sraj		rv = 3;
1206208538Sraj		goto out2;
1207208538Sraj	}
1208208538Sraj
1209208538Sraj	line = (char *)malloc(strlen(name) + strlen(FDT_PROP_SEP) +
1210208538Sraj	    strlen(buf) + 2);
1211208538Sraj	if (line == NULL) {
1212208538Sraj		sprintf(command_errbuf, "could not allocate space for string");
1213208538Sraj		rv = 4;
1214208538Sraj		goto out2;
1215208538Sraj	}
1216208538Sraj
1217208538Sraj	sprintf(line, "%s" FDT_PROP_SEP "%s\n", name, buf);
1218208538Sraj
1219208538Srajout1:
1220208538Sraj	pager_open();
1221208538Sraj	pager_output(line);
1222208538Sraj	pager_close();
1223208538Sraj
1224208538Srajout2:
1225208538Sraj	if (buf)
1226208538Sraj		free(buf);
1227208538Sraj
1228208538Sraj	if (line)
1229208538Sraj		free(line);
1230208538Sraj
1231208538Sraj	return (rv);
1232208538Sraj}
1233208538Sraj
1234208538Srajstatic int
1235208538Srajfdt_modprop(int nodeoff, char *propname, void *value, char mode)
1236208538Sraj{
1237208538Sraj	uint32_t cells[100];
1238275762Sandrew	const char *buf;
1239208538Sraj	int len, rv;
1240208538Sraj	const struct fdt_property *p;
1241208538Sraj
1242208538Sraj	p = fdt_get_property(fdtp, nodeoff, propname, NULL);
1243208538Sraj
1244208538Sraj	if (p != NULL) {
1245208538Sraj		if (mode == 1) {
1246208538Sraj			 /* Adding inexistant value in mode 1 is forbidden */
1247208538Sraj			sprintf(command_errbuf, "property already exists!");
1248208538Sraj			return (CMD_ERROR);
1249208538Sraj		}
1250208538Sraj	} else if (mode == 0) {
1251208538Sraj		sprintf(command_errbuf, "property does not exist!");
1252208538Sraj		return (CMD_ERROR);
1253208538Sraj	}
1254208538Sraj	len = strlen(value);
1255208538Sraj	rv = 0;
1256275762Sandrew	buf = value;
1257208538Sraj
1258208538Sraj	switch (*buf) {
1259208538Sraj	case '&':
1260208538Sraj		/* phandles */
1261208538Sraj		break;
1262208538Sraj	case '<':
1263208538Sraj		/* Data cells */
1264208538Sraj		len = fdt_strtovect(buf, (void *)&cells, 100,
1265208538Sraj		    sizeof(uint32_t));
1266208538Sraj
1267208538Sraj		rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
1268208538Sraj		    len * sizeof(uint32_t));
1269208538Sraj		break;
1270208538Sraj	case '[':
1271208538Sraj		/* Data bytes */
1272208538Sraj		len = fdt_strtovect(buf, (void *)&cells, 100,
1273208538Sraj		    sizeof(uint8_t));
1274208538Sraj
1275208538Sraj		rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
1276208538Sraj		    len * sizeof(uint8_t));
1277208538Sraj		break;
1278208538Sraj	case '"':
1279208538Sraj	default:
1280208538Sraj		/* Default -- string */
1281208538Sraj		rv = fdt_setprop_string(fdtp, nodeoff, propname, value);
1282208538Sraj		break;
1283208538Sraj	}
1284208538Sraj
1285233323Sraj	if (rv != 0) {
1286233323Sraj		if (rv == -FDT_ERR_NOSPACE)
1287233323Sraj			sprintf(command_errbuf,
1288233323Sraj			    "Device tree blob is too small!\n");
1289233323Sraj		else
1290233323Sraj			sprintf(command_errbuf,
1291233323Sraj			    "Could not add/modify property!\n");
1292233323Sraj	}
1293208538Sraj	return (rv);
1294208538Sraj}
1295208538Sraj
1296208538Sraj/* Merge strings from argv into a single string */
1297208538Srajstatic int
1298208538Srajfdt_merge_strings(int argc, char *argv[], int start, char **buffer)
1299208538Sraj{
1300208538Sraj	char *buf;
1301208538Sraj	int i, idx, sz;
1302208538Sraj
1303208538Sraj	*buffer = NULL;
1304208538Sraj	sz = 0;
1305208538Sraj
1306208538Sraj	for (i = start; i < argc; i++)
1307208538Sraj		sz += strlen(argv[i]);
1308208538Sraj
1309208538Sraj	/* Additional bytes for whitespaces between args */
1310208538Sraj	sz += argc - start;
1311208538Sraj
1312208538Sraj	buf = (char *)malloc(sizeof(char) * sz);
1313208538Sraj	bzero(buf, sizeof(char) * sz);
1314208538Sraj
1315208538Sraj	if (buf == NULL) {
1316208538Sraj		sprintf(command_errbuf, "could not allocate space "
1317208538Sraj		    "for string");
1318208538Sraj		return (1);
1319208538Sraj	}
1320208538Sraj
1321208538Sraj	idx = 0;
1322208538Sraj	for (i = start, idx = 0; i < argc; i++) {
1323208538Sraj		strcpy(buf + idx, argv[i]);
1324208538Sraj		idx += strlen(argv[i]);
1325208538Sraj		buf[idx] = ' ';
1326208538Sraj		idx++;
1327208538Sraj	}
1328208538Sraj	buf[sz - 1] = '\0';
1329208538Sraj	*buffer = buf;
1330208538Sraj	return (0);
1331208538Sraj}
1332208538Sraj
1333208538Sraj/* Extract offset and name of node/property from a given path */
1334208538Srajstatic int
1335208538Srajfdt_extract_nameloc(char **pathp, char **namep, int *nodeoff)
1336208538Sraj{
1337208538Sraj	int o;
1338208538Sraj	char *path = *pathp, *name = NULL, *subpath = NULL;
1339208538Sraj
1340208538Sraj	subpath = strrchr(path, '/');
1341208538Sraj	if (subpath == NULL) {
1342208538Sraj		o = fdt_path_offset(fdtp, cwd);
1343208538Sraj		name = path;
1344208538Sraj		path = (char *)&cwd;
1345208538Sraj	} else {
1346208538Sraj		*subpath = '\0';
1347208538Sraj		if (strlen(path) == 0)
1348208538Sraj			path = cwd;
1349208538Sraj
1350208538Sraj		name = subpath + 1;
1351208538Sraj		o = fdt_path_offset(fdtp, path);
1352208538Sraj	}
1353208538Sraj
1354208538Sraj	if (strlen(name) == 0) {
1355208538Sraj		sprintf(command_errbuf, "name not specified");
1356208538Sraj		return (1);
1357208538Sraj	}
1358208538Sraj	if (o < 0) {
1359208538Sraj		sprintf(command_errbuf, "could not find node: '%s'", path);
1360208538Sraj		return (1);
1361208538Sraj	}
1362208538Sraj	*namep = name;
1363208538Sraj	*nodeoff = o;
1364208538Sraj	*pathp = path;
1365208538Sraj	return (0);
1366208538Sraj}
1367208538Sraj
1368208538Srajstatic int
1369208538Srajfdt_cmd_prop(int argc, char *argv[])
1370208538Sraj{
1371208538Sraj	char *path, *propname, *value;
1372208538Sraj	int o, next, depth, rv;
1373208538Sraj	uint32_t tag;
1374208538Sraj
1375208538Sraj	path = (argc > 2) ? argv[2] : NULL;
1376208538Sraj
1377208538Sraj	value = NULL;
1378208538Sraj
1379208538Sraj	if (argc > 3) {
1380208538Sraj		/* Merge property value strings into one */
1381208538Sraj		if (fdt_merge_strings(argc, argv, 3, &value) != 0)
1382208538Sraj			return (CMD_ERROR);
1383208538Sraj	} else
1384208538Sraj		value = NULL;
1385208538Sraj
1386208538Sraj	if (path == NULL)
1387208538Sraj		path = cwd;
1388208538Sraj
1389208538Sraj	rv = CMD_OK;
1390208538Sraj
1391208538Sraj	if (value) {
1392208538Sraj		/* If value is specified -- try to modify prop. */
1393208538Sraj		if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1394208538Sraj			return (CMD_ERROR);
1395208538Sraj
1396208538Sraj		rv = fdt_modprop(o, propname, value, 0);
1397208538Sraj		if (rv)
1398208538Sraj			return (CMD_ERROR);
1399208538Sraj		return (CMD_OK);
1400208538Sraj
1401208538Sraj	}
1402208538Sraj	/* User wants to display properties */
1403208538Sraj	o = fdt_path_offset(fdtp, path);
1404208538Sraj
1405208538Sraj	if (o < 0) {
1406208538Sraj		sprintf(command_errbuf, "could not find node: '%s'", path);
1407208538Sraj		rv = CMD_ERROR;
1408208538Sraj		goto out;
1409208538Sraj	}
1410208538Sraj
1411208538Sraj	depth = 0;
1412208538Sraj	while (depth >= 0) {
1413208538Sraj		tag = fdt_next_tag(fdtp, o, &next);
1414208538Sraj		switch (tag) {
1415208538Sraj		case FDT_NOP:
1416208538Sraj			break;
1417208538Sraj		case FDT_PROP:
1418208538Sraj			if (depth > 1)
1419208538Sraj				/* Don't process properties of nested nodes */
1420208538Sraj				break;
1421208538Sraj
1422208538Sraj			if (fdt_prop(o) != 0) {
1423208538Sraj				sprintf(command_errbuf, "could not process "
1424208538Sraj				    "property");
1425208538Sraj				rv = CMD_ERROR;
1426208538Sraj				goto out;
1427208538Sraj			}
1428208538Sraj			break;
1429208538Sraj		case FDT_BEGIN_NODE:
1430208538Sraj			depth++;
1431208538Sraj			if (depth > FDT_MAX_DEPTH) {
1432208538Sraj				printf("warning: nesting too deep: %d\n",
1433208538Sraj				    depth);
1434208538Sraj				goto out;
1435208538Sraj			}
1436208538Sraj			break;
1437208538Sraj		case FDT_END_NODE:
1438208538Sraj			depth--;
1439208538Sraj			if (depth == 0)
1440208538Sraj				/*
1441208538Sraj				 * This is the end of our starting node, force
1442208538Sraj				 * the loop finish.
1443208538Sraj				 */
1444208538Sraj				depth--;
1445208538Sraj			break;
1446208538Sraj		}
1447208538Sraj		o = next;
1448208538Sraj	}
1449208538Srajout:
1450208538Sraj	return (rv);
1451208538Sraj}
1452208538Sraj
1453208538Srajstatic int
1454208538Srajfdt_cmd_mkprop(int argc, char *argv[])
1455208538Sraj{
1456208538Sraj	int o;
1457208538Sraj	char *path, *propname, *value;
1458208538Sraj
1459208538Sraj	path = (argc > 2) ? argv[2] : NULL;
1460208538Sraj
1461208538Sraj	value = NULL;
1462208538Sraj
1463208538Sraj	if (argc > 3) {
1464208538Sraj		/* Merge property value strings into one */
1465208538Sraj		if (fdt_merge_strings(argc, argv, 3, &value) != 0)
1466208538Sraj			return (CMD_ERROR);
1467208538Sraj	} else
1468208538Sraj		value = NULL;
1469208538Sraj
1470208538Sraj	if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1471208538Sraj		return (CMD_ERROR);
1472208538Sraj
1473208538Sraj	if (fdt_modprop(o, propname, value, 1))
1474208538Sraj		return (CMD_ERROR);
1475208538Sraj
1476208538Sraj	return (CMD_OK);
1477208538Sraj}
1478208538Sraj
1479208538Srajstatic int
1480208538Srajfdt_cmd_rm(int argc, char *argv[])
1481208538Sraj{
1482208538Sraj	int o, rv;
1483208538Sraj	char *path = NULL, *propname;
1484208538Sraj
1485208538Sraj	if (argc > 2)
1486208538Sraj		path = argv[2];
1487208538Sraj	else {
1488208538Sraj		sprintf(command_errbuf, "no node/property name specified");
1489208538Sraj		return (CMD_ERROR);
1490208538Sraj	}
1491208538Sraj
1492208538Sraj	o = fdt_path_offset(fdtp, path);
1493208538Sraj	if (o < 0) {
1494208538Sraj		/* If node not found -- try to find & delete property */
1495208538Sraj		if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1496208538Sraj			return (CMD_ERROR);
1497208538Sraj
1498208538Sraj		if ((rv = fdt_delprop(fdtp, o, propname)) != 0) {
1499208538Sraj			sprintf(command_errbuf, "could not delete"
1500208538Sraj			    "%s\n", (rv == -FDT_ERR_NOTFOUND) ?
1501208538Sraj			    "(property/node does not exist)" : "");
1502208538Sraj			return (CMD_ERROR);
1503208538Sraj
1504208538Sraj		} else
1505208538Sraj			return (CMD_OK);
1506208538Sraj	}
1507208538Sraj	/* If node exists -- remove node */
1508208538Sraj	rv = fdt_del_node(fdtp, o);
1509208538Sraj	if (rv) {
1510208538Sraj		sprintf(command_errbuf, "could not delete node");
1511208538Sraj		return (CMD_ERROR);
1512208538Sraj	}
1513208538Sraj	return (CMD_OK);
1514208538Sraj}
1515208538Sraj
1516208538Srajstatic int
1517208538Srajfdt_cmd_mknode(int argc, char *argv[])
1518208538Sraj{
1519208538Sraj	int o, rv;
1520208538Sraj	char *path = NULL, *nodename = NULL;
1521208538Sraj
1522208538Sraj	if (argc > 2)
1523208538Sraj		path = argv[2];
1524208538Sraj	else {
1525208538Sraj		sprintf(command_errbuf, "no node name specified");
1526208538Sraj		return (CMD_ERROR);
1527208538Sraj	}
1528208538Sraj
1529208538Sraj	if (fdt_extract_nameloc(&path, &nodename, &o) != 0)
1530208538Sraj		return (CMD_ERROR);
1531208538Sraj
1532208538Sraj	rv = fdt_add_subnode(fdtp, o, nodename);
1533208538Sraj
1534208538Sraj	if (rv < 0) {
1535233323Sraj		if (rv == -FDT_ERR_NOSPACE)
1536233323Sraj			sprintf(command_errbuf,
1537233323Sraj			    "Device tree blob is too small!\n");
1538233323Sraj		else
1539233323Sraj			sprintf(command_errbuf,
1540233323Sraj			    "Could not add node!\n");
1541208538Sraj		return (CMD_ERROR);
1542208538Sraj	}
1543208538Sraj	return (CMD_OK);
1544208538Sraj}
1545208538Sraj
1546208538Srajstatic int
1547208538Srajfdt_cmd_pwd(int argc, char *argv[])
1548208538Sraj{
1549233323Sraj	char line[FDT_CWD_LEN];
1550208538Sraj
1551208538Sraj	pager_open();
1552208538Sraj	sprintf(line, "%s\n", cwd);
1553208538Sraj	pager_output(line);
1554208538Sraj	pager_close();
1555208538Sraj	return (CMD_OK);
1556208538Sraj}
1557208538Sraj
1558208538Srajstatic int
1559243693Sgonzofdt_cmd_mres(int argc, char *argv[])
1560243693Sgonzo{
1561243693Sgonzo	uint64_t start, size;
1562243693Sgonzo	int i, total;
1563243693Sgonzo	char line[80];
1564243693Sgonzo
1565243693Sgonzo	pager_open();
1566243693Sgonzo	total = fdt_num_mem_rsv(fdtp);
1567243693Sgonzo	if (total > 0) {
1568243693Sgonzo		pager_output("Reserved memory regions:\n");
1569243693Sgonzo		for (i = 0; i < total; i++) {
1570243693Sgonzo			fdt_get_mem_rsv(fdtp, i, &start, &size);
1571243693Sgonzo			sprintf(line, "reg#%d: (start: 0x%jx, size: 0x%jx)\n",
1572243693Sgonzo			    i, start, size);
1573243693Sgonzo			pager_output(line);
1574243693Sgonzo		}
1575243693Sgonzo	} else
1576243693Sgonzo		pager_output("No reserved memory regions\n");
1577243693Sgonzo	pager_close();
1578243693Sgonzo
1579243693Sgonzo	return (CMD_OK);
1580243693Sgonzo}
1581243693Sgonzo
1582243693Sgonzostatic int
1583208538Srajfdt_cmd_nyi(int argc, char *argv[])
1584208538Sraj{
1585208538Sraj
1586208538Sraj	printf("command not yet implemented\n");
1587208538Sraj	return (CMD_ERROR);
1588208538Sraj}
1589