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