fdt.c 26.9 KB
Newer Older
1 2 3 4 5 6 7
/*
 * (C) Copyright 2007
 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
 * Based on code written by:
 *   Pantelis Antoniou <pantelis.antoniou@gmail.com> and
 *   Matthew McClintock <msm@freescale.com>
 *
8
 * SPDX-License-Identifier:	GPL-2.0+
9 10 11 12 13 14 15 16
 */

#include <common.h>
#include <command.h>
#include <linux/ctype.h>
#include <linux/types.h>
#include <asm/global_data.h>
#include <libfdt.h>
17
#include <fdt_support.h>
18
#include <mapmem.h>
19
#include <asm/io.h>
20 21

#define MAX_LEVEL	32		/* how deeply nested we will go */
22
#define SCRATCHPAD	1024		/* bytes of scratchpad memory */
23 24 25
#ifndef CONFIG_CMD_FDT_MAX_DUMP
#define CONFIG_CMD_FDT_MAX_DUMP 64
#endif
26 27 28 29 30 31

/*
 * Global data (for the gd->bd)
 */
DECLARE_GLOBAL_DATA_PTR;

32
static int fdt_valid(struct fdt_header **blobp);
33
static int fdt_parse_prop(char *const*newval, int count, char *data, int *len);
34
static int fdt_print(const char *pathp, char *prop, int depth);
35
static int is_printable_string(const void *data, int len);
36

37 38 39 40 41
/*
 * The working_fdt points to our working flattened device tree.
 */
struct fdt_header *working_fdt;

42
void set_working_fdt_addr(ulong addr)
43
{
44 45
	void *buf;

46
	buf = map_sysmem(addr, 0);
47
	working_fdt = buf;
48
	setenv_hex("fdtaddr", addr);
49 50
}

51 52 53 54 55 56 57 58 59 60
/*
 * Get a value from the fdt and format it to be set in the environment
 */
static int fdt_value_setenv(const void *nodep, int len, const char *var)
{
	if (is_printable_string(nodep, len))
		setenv(var, (void *)nodep);
	else if (len == 4) {
		char buf[11];

61
		sprintf(buf, "0x%08X", fdt32_to_cpu(*(fdt32_t *)nodep));
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
		setenv(var, buf);
	} else if (len%4 == 0 && len <= 20) {
		/* Needed to print things like sha1 hashes. */
		char buf[41];
		int i;

		for (i = 0; i < len; i += sizeof(unsigned int))
			sprintf(buf + (i * 2), "%08x",
				*(unsigned int *)(nodep + i));
		setenv(var, buf);
	} else {
		printf("error: unprintable value\n");
		return 1;
	}
	return 0;
}

79 80 81
/*
 * Flattened Device Tree command, see the help for parameter definitions.
 */
82
static int do_fdt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
83
{
84
	if (argc < 2)
85
		return CMD_RET_USAGE;
86

87
	/*
88
	 * Set the address of the fdt
89
	 */
90
	if (strncmp(argv[1], "ad", 2) == 0) {
91
		unsigned long addr;
92 93
		int control = 0;
		struct fdt_header *blob;
94 95 96
		/*
		 * Set the address [and length] of the fdt.
		 */
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
		argc -= 2;
		argv += 2;
/* Temporary #ifdef - some archs don't have fdt_blob yet */
#ifdef CONFIG_OF_CONTROL
		if (argc && !strcmp(*argv, "-c")) {
			control = 1;
			argc--;
			argv++;
		}
#endif
		if (argc == 0) {
			if (control)
				blob = (struct fdt_header *)gd->fdt_blob;
			else
				blob = working_fdt;
			if (!blob || !fdt_valid(&blob))
113
				return 1;
114
			printf("The address of the fdt is %#08lx\n",
115
			       control ? (ulong)map_to_sysmem(blob) :
116
					getenv_hex("fdtaddr", 0));
117 118 119
			return 0;
		}

120
		addr = simple_strtoul(argv[0], NULL, 16);
121
		blob = map_sysmem(addr, 0);
122
		if (!fdt_valid(&blob))
123
			return 1;
124 125 126
		if (control)
			gd->fdt_blob = blob;
		else
127
			set_working_fdt_addr(addr);
128

129
		if (argc >= 2) {
130 131 132 133 134
			int  len;
			int  err;
			/*
			 * Optional new length
			 */
135 136
			len = simple_strtoul(argv[1], NULL, 16);
			if (len < fdt_totalsize(blob)) {
137 138
				printf ("New length %d < existing length %d, "
					"ignoring.\n",
139
					len, fdt_totalsize(blob));
140 141 142 143
			} else {
				/*
				 * Open in place with a new length.
				 */
144
				err = fdt_open_into(blob, blob, len);
145
				if (err != 0) {
146 147
					printf ("libfdt fdt_open_into(): %s\n",
						fdt_strerror(err));
148 149 150 151
				}
			}
		}

152 153 154 155 156 157 158 159 160 161 162
		return CMD_RET_SUCCESS;
	}

	if (!working_fdt) {
		puts(
			"No FDT memory address configured. Please configure\n"
			"the FDT address via \"fdt addr <address>\" command.\n"
			"Aborting!\n");
		return CMD_RET_FAILURE;
	}

163
	/*
164
	 * Move the working_fdt
165
	 */
166
	if (strncmp(argv[1], "mo", 2) == 0) {
167 168 169 170
		struct fdt_header *newaddr;
		int  len;
		int  err;

171
		if (argc < 4)
172
			return CMD_RET_USAGE;
173 174 175 176

		/*
		 * Set the address and length of the fdt.
		 */
177
		working_fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16);
178
		if (!fdt_valid(&working_fdt))
179 180
			return 1;

181
		newaddr = (struct fdt_header *)simple_strtoul(argv[3],NULL,16);
182 183 184 185 186 187

		/*
		 * If the user specifies a length, use that.  Otherwise use the
		 * current length.
		 */
		if (argc <= 4) {
188
			len = fdt_totalsize(working_fdt);
189 190
		} else {
			len = simple_strtoul(argv[4], NULL, 16);
191
			if (len < fdt_totalsize(working_fdt)) {
192 193
				printf ("New length 0x%X < existing length "
					"0x%X, aborting.\n",
194
					len, fdt_totalsize(working_fdt));
195 196
				return 1;
			}
197 198 199 200 201
		}

		/*
		 * Copy to the new location.
		 */
202
		err = fdt_open_into(working_fdt, newaddr, len);
203
		if (err != 0) {
204 205
			printf ("libfdt fdt_open_into(): %s\n",
				fdt_strerror(err));
206 207
			return 1;
		}
208
		working_fdt = newaddr;
209 210 211 212
#ifdef CONFIG_OF_SYSTEM_SETUP
	/* Call the board-specific fixup routine */
	} else if (strncmp(argv[1], "sys", 3) == 0) {
		int err = ft_system_setup(working_fdt, gd->bd);
213

214 215 216 217 218 219
		if (err) {
			printf("Failed to add system information to FDT: %s\n",
			       fdt_strerror(err));
			return CMD_RET_FAILURE;
		}
#endif
220
	/*
221
	 * Make a new node
222
	 */
223
	} else if (strncmp(argv[1], "mk", 2) == 0) {
224 225 226 227 228 229 230 231
		char *pathp;		/* path */
		char *nodep;		/* new node to add */
		int  nodeoffset;	/* node offset from libfdt */
		int  err;

		/*
		 * Parameters: Node path, new node to be appended to the path.
		 */
232
		if (argc < 4)
233
			return CMD_RET_USAGE;
234 235 236 237

		pathp = argv[2];
		nodep = argv[3];

238
		nodeoffset = fdt_path_offset (working_fdt, pathp);
239 240 241 242
		if (nodeoffset < 0) {
			/*
			 * Not found or something else bad happened.
			 */
243
			printf ("libfdt fdt_path_offset() returned %s\n",
244
				fdt_strerror(nodeoffset));
245 246
			return 1;
		}
247
		err = fdt_add_subnode(working_fdt, nodeoffset, nodep);
248
		if (err < 0) {
249 250
			printf ("libfdt fdt_add_subnode(): %s\n",
				fdt_strerror(err));
251 252 253
			return 1;
		}

254
	/*
255
	 * Set the value of a property in the working_fdt.
256
	 */
257
	} else if (argv[1][0] == 's') {
258
		char *pathp;		/* path */
259
		char *prop;		/* property */
260
		int  nodeoffset;	/* node offset from libfdt */
261 262 263
		static char data[SCRATCHPAD];	/* storage for the property */
		int  len;		/* new length of the property */
		int  ret;		/* return value */
264 265

		/*
266
		 * Parameters: Node path, property, optional value.
267
		 */
268
		if (argc < 4)
269
			return CMD_RET_USAGE;
270 271 272

		pathp  = argv[2];
		prop   = argv[3];
273 274 275
		if (argc == 4) {
			len = 0;
		} else {
276
			ret = fdt_parse_prop(&argv[4], argc - 4, data, &len);
277 278 279
			if (ret != 0)
				return ret;
		}
280

281
		nodeoffset = fdt_path_offset (working_fdt, pathp);
282
		if (nodeoffset < 0) {
283
			/*
284
			 * Not found or something else bad happened.
285
			 */
286
			printf ("libfdt fdt_path_offset() returned %s\n",
287
				fdt_strerror(nodeoffset));
288
			return 1;
289 290
		}

291
		ret = fdt_setprop(working_fdt, nodeoffset, prop, data, len);
292 293 294
		if (ret < 0) {
			printf ("libfdt fdt_setprop(): %s\n", fdt_strerror(ret));
			return 1;
295 296
		}

297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
	/********************************************************************
	 * Get the value of a property in the working_fdt.
	 ********************************************************************/
	} else if (argv[1][0] == 'g') {
		char *subcmd;		/* sub-command */
		char *pathp;		/* path */
		char *prop;		/* property */
		char *var;		/* variable to store result */
		int  nodeoffset;	/* node offset from libfdt */
		const void *nodep;	/* property node pointer */
		int  len = 0;		/* new length of the property */

		/*
		 * Parameters: Node path, property, optional value.
		 */
		if (argc < 5)
			return CMD_RET_USAGE;

		subcmd = argv[2];

		if (argc < 6 && subcmd[0] != 's')
			return CMD_RET_USAGE;

		var    = argv[3];
		pathp  = argv[4];
		prop   = argv[5];

		nodeoffset = fdt_path_offset(working_fdt, pathp);
		if (nodeoffset < 0) {
			/*
			 * Not found or something else bad happened.
			 */
			printf("libfdt fdt_path_offset() returned %s\n",
				fdt_strerror(nodeoffset));
			return 1;
		}

		if (subcmd[0] == 'n' || (subcmd[0] == 's' && argc == 5)) {
			int reqIndex = -1;
			int startDepth = fdt_node_depth(
				working_fdt, nodeoffset);
			int curDepth = startDepth;
			int curIndex = -1;
			int nextNodeOffset = fdt_next_node(
				working_fdt, nodeoffset, &curDepth);

			if (subcmd[0] == 'n')
				reqIndex = simple_strtoul(argv[5], NULL, 16);

			while (curDepth > startDepth) {
				if (curDepth == startDepth + 1)
					curIndex++;
				if (subcmd[0] == 'n' && curIndex == reqIndex) {
					const char *nodeName = fdt_get_name(
					    working_fdt, nextNodeOffset, NULL);

					setenv(var, (char *)nodeName);
					return 0;
				}
				nextNodeOffset = fdt_next_node(
					working_fdt, nextNodeOffset, &curDepth);
				if (nextNodeOffset < 0)
					break;
			}
			if (subcmd[0] == 's') {
				/* get the num nodes at this level */
363
				setenv_ulong(var, curIndex + 1);
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
			} else {
				/* node index not found */
				printf("libfdt node not found\n");
				return 1;
			}
		} else {
			nodep = fdt_getprop(
				working_fdt, nodeoffset, prop, &len);
			if (len == 0) {
				/* no property value */
				setenv(var, "");
				return 0;
			} else if (len > 0) {
				if (subcmd[0] == 'v') {
					int ret;

					ret = fdt_value_setenv(nodep, len, var);
					if (ret != 0)
						return ret;
				} else if (subcmd[0] == 'a') {
					/* Get address */
					char buf[11];

387
					sprintf(buf, "0x%p", nodep);
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
					setenv(var, buf);
				} else if (subcmd[0] == 's') {
					/* Get size */
					char buf[11];

					sprintf(buf, "0x%08X", len);
					setenv(var, buf);
				} else
					return CMD_RET_USAGE;
				return 0;
			} else {
				printf("libfdt fdt_getprop(): %s\n",
					fdt_strerror(len));
				return 1;
			}
		}

405
	/*
406
	 * Print (recursive) / List (single level)
407
	 */
408
	} else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) {
409 410
		int depth = MAX_LEVEL;	/* how deep to print */
		char *pathp;		/* path */
411 412
		char *prop;		/* property */
		int  ret;		/* return value */
413
		static char root[2] = "/";
414 415 416 417

		/*
		 * list is an alias for print, but limited to 1 level
		 */
418
		if (argv[1][0] == 'l') {
419 420 421 422 423 424 425
			depth = 1;
		}

		/*
		 * Get the starting path.  The root node is an oddball,
		 * the offset is zero and has no name.
		 */
426 427 428 429
		if (argc == 2)
			pathp = root;
		else
			pathp = argv[2];
430 431 432 433 434
		if (argc > 3)
			prop = argv[3];
		else
			prop = NULL;

435 436 437
		ret = fdt_print(pathp, prop, depth);
		if (ret != 0)
			return ret;
438

439
	/*
440
	 * Remove a property/node
441
	 */
442
	} else if (strncmp(argv[1], "rm", 2) == 0) {
443 444 445 446 447 448 449
		int  nodeoffset;	/* node offset from libfdt */
		int  err;

		/*
		 * Get the path.  The root node is an oddball, the offset
		 * is zero and has no name.
		 */
450
		nodeoffset = fdt_path_offset (working_fdt, argv[2]);
451 452 453 454
		if (nodeoffset < 0) {
			/*
			 * Not found or something else bad happened.
			 */
455
			printf ("libfdt fdt_path_offset() returned %s\n",
456
				fdt_strerror(nodeoffset));
457
			return 1;
458 459 460 461 462 463
		}
		/*
		 * Do the delete.  A fourth parameter means delete a property,
		 * otherwise delete the node.
		 */
		if (argc > 3) {
464
			err = fdt_delprop(working_fdt, nodeoffset, argv[3]);
465
			if (err < 0) {
466 467
				printf("libfdt fdt_delprop():  %s\n",
					fdt_strerror(err));
468 469 470
				return err;
			}
		} else {
471
			err = fdt_del_node(working_fdt, nodeoffset);
472
			if (err < 0) {
473 474
				printf("libfdt fdt_del_node():  %s\n",
					fdt_strerror(err));
475 476 477
				return err;
			}
		}
Kumar Gala's avatar
Kumar Gala committed
478

479
	/*
Kumar Gala's avatar
Kumar Gala committed
480
	 * Display header info
481
	 */
Kumar Gala's avatar
Kumar Gala committed
482
	} else if (argv[1][0] == 'h') {
483 484 485 486 487 488 489 490 491 492
		u32 version = fdt_version(working_fdt);
		printf("magic:\t\t\t0x%x\n", fdt_magic(working_fdt));
		printf("totalsize:\t\t0x%x (%d)\n", fdt_totalsize(working_fdt),
		       fdt_totalsize(working_fdt));
		printf("off_dt_struct:\t\t0x%x\n",
		       fdt_off_dt_struct(working_fdt));
		printf("off_dt_strings:\t\t0x%x\n",
		       fdt_off_dt_strings(working_fdt));
		printf("off_mem_rsvmap:\t\t0x%x\n",
		       fdt_off_mem_rsvmap(working_fdt));
Kumar Gala's avatar
Kumar Gala committed
493
		printf("version:\t\t%d\n", version);
494 495
		printf("last_comp_version:\t%d\n",
		       fdt_last_comp_version(working_fdt));
Kumar Gala's avatar
Kumar Gala committed
496 497
		if (version >= 2)
			printf("boot_cpuid_phys:\t0x%x\n",
498
				fdt_boot_cpuid_phys(working_fdt));
Kumar Gala's avatar
Kumar Gala committed
499 500
		if (version >= 3)
			printf("size_dt_strings:\t0x%x\n",
501
				fdt_size_dt_strings(working_fdt));
Kumar Gala's avatar
Kumar Gala committed
502 503
		if (version >= 17)
			printf("size_dt_struct:\t\t0x%x\n",
504 505 506
				fdt_size_dt_struct(working_fdt));
		printf("number mem_rsv:\t\t0x%x\n",
		       fdt_num_mem_rsv(working_fdt));
Kumar Gala's avatar
Kumar Gala committed
507 508
		printf("\n");

509
	/*
Kumar Gala's avatar
Kumar Gala committed
510
	 * Set boot cpu id
511
	 */
512
	} else if (strncmp(argv[1], "boo", 3) == 0) {
Kumar Gala's avatar
Kumar Gala committed
513
		unsigned long tmp = simple_strtoul(argv[2], NULL, 16);
514
		fdt_set_boot_cpuid_phys(working_fdt, tmp);
Kumar Gala's avatar
Kumar Gala committed
515

516
	/*
Kumar Gala's avatar
Kumar Gala committed
517
	 * memory command
518
	 */
519
	} else if (strncmp(argv[1], "me", 2) == 0) {
Kumar Gala's avatar
Kumar Gala committed
520 521
		uint64_t addr, size;
		int err;
522 523
		addr = simple_strtoull(argv[2], NULL, 16);
		size = simple_strtoull(argv[3], NULL, 16);
524
		err = fdt_fixup_memory(working_fdt, addr, size);
Kumar Gala's avatar
Kumar Gala committed
525 526 527
		if (err < 0)
			return err;

528
	/*
Kumar Gala's avatar
Kumar Gala committed
529
	 * mem reserve commands
530
	 */
531
	} else if (strncmp(argv[1], "rs", 2) == 0) {
Kumar Gala's avatar
Kumar Gala committed
532 533
		if (argv[2][0] == 'p') {
			uint64_t addr, size;
534
			int total = fdt_num_mem_rsv(working_fdt);
Kumar Gala's avatar
Kumar Gala committed
535 536 537 538 539
			int j, err;
			printf("index\t\t   start\t\t    size\n");
			printf("-------------------------------"
				"-----------------\n");
			for (j = 0; j < total; j++) {
540
				err = fdt_get_mem_rsv(working_fdt, j, &addr, &size);
Kumar Gala's avatar
Kumar Gala committed
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
				if (err < 0) {
					printf("libfdt fdt_get_mem_rsv():  %s\n",
							fdt_strerror(err));
					return err;
				}
				printf("    %x\t%08x%08x\t%08x%08x\n", j,
					(u32)(addr >> 32),
					(u32)(addr & 0xffffffff),
					(u32)(size >> 32),
					(u32)(size & 0xffffffff));
			}
		} else if (argv[2][0] == 'a') {
			uint64_t addr, size;
			int err;
			addr = simple_strtoull(argv[3], NULL, 16);
			size = simple_strtoull(argv[4], NULL, 16);
557
			err = fdt_add_mem_rsv(working_fdt, addr, size);
Kumar Gala's avatar
Kumar Gala committed
558 559 560 561 562 563 564 565

			if (err < 0) {
				printf("libfdt fdt_add_mem_rsv():  %s\n",
					fdt_strerror(err));
				return err;
			}
		} else if (argv[2][0] == 'd') {
			unsigned long idx = simple_strtoul(argv[3], NULL, 16);
566
			int err = fdt_del_mem_rsv(working_fdt, idx);
Kumar Gala's avatar
Kumar Gala committed
567 568 569 570 571 572 573 574

			if (err < 0) {
				printf("libfdt fdt_del_mem_rsv():  %s\n",
					fdt_strerror(err));
				return err;
			}
		} else {
			/* Unrecognized command */
575
			return CMD_RET_USAGE;
Kumar Gala's avatar
Kumar Gala committed
576
		}
577
	}
578
#ifdef CONFIG_OF_BOARD_SETUP
579
	/* Call the board-specific fixup routine */
580 581 582 583 584 585 586 587 588
	else if (strncmp(argv[1], "boa", 3) == 0) {
		int err = ft_board_setup(working_fdt, gd->bd);

		if (err) {
			printf("Failed to update board information in FDT: %s\n",
			       fdt_strerror(err));
			return CMD_RET_FAILURE;
		}
	}
589
#endif
590
	/* Create a chosen node */
591
	else if (strncmp(argv[1], "cho", 3) == 0) {
592 593
		unsigned long initrd_start = 0, initrd_end = 0;

594
		if ((argc != 2) && (argc != 4))
595
			return CMD_RET_USAGE;
596 597 598 599 600 601

		if (argc == 4) {
			initrd_start = simple_strtoul(argv[2], NULL, 16);
			initrd_end = simple_strtoul(argv[3], NULL, 16);
		}

602
		fdt_chosen(working_fdt);
603
		fdt_initrd(working_fdt, initrd_start, initrd_end);
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632

#if defined(CONFIG_FIT_SIGNATURE)
	} else if (strncmp(argv[1], "che", 3) == 0) {
		int cfg_noffset;
		int ret;
		unsigned long addr;
		struct fdt_header *blob;

		if (!working_fdt)
			return CMD_RET_FAILURE;

		if (argc > 2) {
			addr = simple_strtoul(argv[2], NULL, 16);
			blob = map_sysmem(addr, 0);
		} else {
			blob = (struct fdt_header *)gd->fdt_blob;
		}
		if (!fdt_valid(&blob))
			return 1;

		gd->fdt_blob = blob;
		cfg_noffset = fit_conf_get_node(working_fdt, NULL);
		if (!cfg_noffset) {
			printf("Could not find configuration node: %s\n",
			       fdt_strerror(cfg_noffset));
			return CMD_RET_FAILURE;
		}

		ret = fit_config_verify(working_fdt, cfg_noffset);
633
		if (ret == 0)
634 635 636 637 638
			return CMD_RET_SUCCESS;
		else
			return CMD_RET_FAILURE;
#endif

Kumar Gala's avatar
Kumar Gala committed
639
	}
640 641 642 643 644
#ifdef CONFIG_OF_LIBFDT_OVERLAY
	/* apply an overlay */
	else if (strncmp(argv[1], "ap", 2) == 0) {
		unsigned long addr;
		struct fdt_header *blob;
645
		int ret;
646 647 648 649 650 651 652 653 654 655 656 657

		if (argc != 3)
			return CMD_RET_USAGE;

		if (!working_fdt)
			return CMD_RET_FAILURE;

		addr = simple_strtoul(argv[2], NULL, 16);
		blob = map_sysmem(addr, 0);
		if (!fdt_valid(&blob))
			return CMD_RET_FAILURE;

658 659 660
		ret = fdt_overlay_apply(working_fdt, blob);
		if (ret) {
			printf("fdt_overlay_apply(): %s\n", fdt_strerror(ret));
661
			return CMD_RET_FAILURE;
662
		}
663 664
	}
#endif
Kumar Gala's avatar
Kumar Gala committed
665 666
	/* resize the fdt */
	else if (strncmp(argv[1], "re", 2) == 0) {
667 668 669 670 671 672
		uint extrasize;
		if (argc > 2)
			extrasize = simple_strtoul(argv[2], NULL, 16);
		else
			extrasize = 0;
		fdt_shrink_to_minimum(working_fdt, extrasize);
Kumar Gala's avatar
Kumar Gala committed
673 674
	}
	else {
675
		/* Unrecognized command */
676
		return CMD_RET_USAGE;
677 678 679 680 681
	}

	return 0;
}

682
/****************************************************************************/
683

684 685 686 687 688 689 690
/**
 * fdt_valid() - Check if an FDT is valid. If not, change it to NULL
 *
 * @blobp: Pointer to FDT pointer
 * @return 1 if OK, 0 if bad (in which case *blobp is set to NULL)
 */
static int fdt_valid(struct fdt_header **blobp)
691
{
692 693
	const void *blob = *blobp;
	int err;
694

695
	if (blob == NULL) {
696
		printf ("The address of the fdt is invalid (NULL).\n");
697 698
		return 0;
	}
699

700
	err = fdt_check_header(blob);
701 702 703 704
	if (err == 0)
		return 1;	/* valid */

	if (err < 0) {
705
		printf("libfdt fdt_check_header(): %s", fdt_strerror(err));
706 707 708 709
		/*
		 * Be more informative on bad version.
		 */
		if (err == -FDT_ERR_BADVERSION) {
710
			if (fdt_version(blob) <
711
			    FDT_FIRST_SUPPORTED_VERSION) {
Andrew Klossner's avatar
Andrew Klossner committed
712
				printf (" - too old, fdt %d < %d",
713
					fdt_version(blob),
714
					FDT_FIRST_SUPPORTED_VERSION);
715
			}
716
			if (fdt_last_comp_version(blob) >
717
			    FDT_LAST_SUPPORTED_VERSION) {
Andrew Klossner's avatar
Andrew Klossner committed
718
				printf (" - too new, fdt %d > %d",
719
					fdt_version(blob),
720
					FDT_LAST_SUPPORTED_VERSION);
721 722 723
			}
		}
		printf("\n");
724
		*blobp = NULL;
725 726 727 728 729
		return 0;
	}
	return 1;
}

730
/****************************************************************************/
731 732

/*
733
 * Parse the user's input, partially heuristic.  Valid formats:
734
 * <0x00112233 4 05>	- an array of cells.  Numbers follow standard
Wolfgang Denk's avatar
Wolfgang Denk committed
735
 *			C conventions.
736 737 738 739
 * [00 11 22 .. nn] - byte stream
 * "string"	- If the the value doesn't start with "<" or "[", it is
 *			treated as a string.  Note that the quotes are
 *			stripped by the parser before we get the string.
740
 * newval: An array of strings containing the new property as specified
Wolfgang Denk's avatar
Wolfgang Denk committed
741
 *	on the command line
742 743 744
 * count: The number of strings in the array
 * data: A bytestream to be placed in the property
 * len: The length of the resulting bytestream
745
 */
746
static int fdt_parse_prop(char * const *newval, int count, char *data, int *len)
747 748
{
	char *cp;		/* temporary char pointer */
749
	char *newp;		/* temporary newval char pointer */
750
	unsigned long tmp;	/* holds converted values */
751
	int stridx = 0;
752

753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
	*len = 0;
	newp = newval[0];

	/* An array of cells */
	if (*newp == '<') {
		newp++;
		while ((*newp != '>') && (stridx < count)) {
			/*
			 * Keep searching until we find that last ">"
			 * That way users don't have to escape the spaces
			 */
			if (*newp == '\0') {
				newp = newval[++stridx];
				continue;
			}

			cp = newp;
			tmp = simple_strtoul(cp, &newp, 0);
771
			*(fdt32_t *)data = cpu_to_fdt32(tmp);
772 773 774 775 776
			data  += 4;
			*len += 4;

			/* If the ptr didn't advance, something went wrong */
			if ((newp - cp) <= 0) {
777 778 779 780
				printf("Sorry, I could not convert \"%s\"\n",
					cp);
				return 1;
			}
781 782 783

			while (*newp == ' ')
				newp++;
784
		}
785 786 787

		if (*newp != '>') {
			printf("Unexpected character '%c'\n", *newp);
788 789
			return 1;
		}
790
	} else if (*newp == '[') {
791 792 793
		/*
		 * Byte stream.  Convert the values.
		 */
794
		newp++;
795
		while ((stridx < count) && (*newp != ']')) {
796 797
			while (*newp == ' ')
				newp++;
798
			if (*newp == '\0') {
799
				newp = newval[++stridx];
800 801 802 803 804 805 806
				continue;
			}
			if (!isxdigit(*newp))
				break;
			tmp = simple_strtoul(newp, &newp, 16);
			*data++ = tmp & 0xFF;
			*len    = *len + 1;
807
		}
808
		if (*newp != ']') {
Andrew Klossner's avatar
Andrew Klossner committed
809
			printf("Unexpected character '%c'\n", *newp);
810 811 812 813
			return 1;
		}
	} else {
		/*
814 815 816
		 * Assume it is one or more strings.  Copy it into our
		 * data area for convenience (including the
		 * terminating '\0's).
817
		 */
818
		while (stridx < count) {
819
			size_t length = strlen(newp) + 1;
820
			strcpy(data, newp);
821 822
			data += length;
			*len += length;
823 824
			newp = newval[++stridx];
		}
825 826 827 828 829 830 831 832
	}
	return 0;
}

/****************************************************************************/

/*
 * Heuristic to guess if this is a string or concatenated strings.
833 834 835 836 837 838 839 840 841 842
 */

static int is_printable_string(const void *data, int len)
{
	const char *s = data;

	/* zero length is not */
	if (len == 0)
		return 0;

843 844
	/* must terminate with zero or '\n' */
	if (s[len - 1] != '\0' && s[len - 1] != '\n')
845 846 847
		return 0;

	/* printable or a null byte (concatenated strings) */
848
	while (((*s == '\0') || isprint(*s) || isspace(*s)) && (len > 0)) {
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
		/*
		 * If we see a null, there are three possibilities:
		 * 1) If len == 1, it is the end of the string, printable
		 * 2) Next character also a null, not printable.
		 * 3) Next character not a null, continue to check.
		 */
		if (s[0] == '\0') {
			if (len == 1)
				return 1;
			if (s[1] == '\0')
				return 0;
		}
		s++;
		len--;
	}

	/* Not the null termination, or not done yet: not printable */
	if (*s != '\0' || (len != 0))
		return 0;

	return 1;
}

872 873 874 875 876 877

/*
 * Print the property in the best format, a heuristic guess.  Print as
 * a string, concatenated strings, a byte, word, double word, or (if all
 * else fails) it is printed as a stream of bytes.
 */
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
static void print_data(const void *data, int len)
{
	int j;

	/* no data, don't print */
	if (len == 0)
		return;

	/*
	 * It is a string, but it may have multiple strings (embedded '\0's).
	 */
	if (is_printable_string(data, len)) {
		puts("\"");
		j = 0;
		while (j < len) {
			if (j > 0)
				puts("\", \"");
			puts(data);
			j    += strlen(data) + 1;
			data += strlen(data) + 1;
		}
		puts("\"");
		return;
	}

903
	if ((len %4) == 0) {
904
		if (len > CONFIG_CMD_FDT_MAX_DUMP)
905
			printf("* 0x%p [0x%08x]", data, len);
906
		else {
907
			const __be32 *p;
908 909 910 911 912 913 914

			printf("<");
			for (j = 0, p = data; j < len/4; j++)
				printf("0x%08x%s", fdt32_to_cpu(p[j]),
					j < (len/4 - 1) ? " " : "");
			printf(">");
		}
915
	} else { /* anything else... hexdump */
916
		if (len > CONFIG_CMD_FDT_MAX_DUMP)
917
			printf("* 0x%p [0x%08x]", data, len);
918 919 920 921 922 923 924 925
		else {
			const u8 *s;

			printf("[");
			for (j = 0, s = data; j < len; j++)
				printf("%02x%s", s[j], j < len - 1 ? " " : "");
			printf("]");
		}
926 927 928
	}
}

929 930 931
/****************************************************************************/

/*
932
 * Recursively print (a portion of) the working_fdt.  The depth parameter
933 934
 * determines how deeply nested the fdt is printed.
 */
935
static int fdt_print(const char *pathp, char *prop, int depth)
936 937 938 939
{
	static char tabs[MAX_LEVEL+1] =
		"\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
		"\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
940
	const void *nodep;	/* property node pointer */
941 942 943 944 945
	int  nodeoffset;	/* node offset from libfdt */
	int  nextoffset;	/* next node offset from libfdt */
	uint32_t tag;		/* tag */
	int  len;		/* length of the property */
	int  level = 0;		/* keep track of nesting level */
946
	const struct fdt_property *fdt_prop;
947

948
	nodeoffset = fdt_path_offset (working_fdt, pathp);
949 950 951 952
	if (nodeoffset < 0) {
		/*
		 * Not found or something else bad happened.
		 */
953
		printf ("libfdt fdt_path_offset() returned %s\n",
954
			fdt_strerror(nodeoffset));
955 956 957 958 959 960 961
		return 1;
	}
	/*
	 * The user passed in a property as well as node path.
	 * Print only the given property and then return.
	 */
	if (prop) {
962
		nodep = fdt_getprop (working_fdt, nodeoffset, prop, &len);
963 964 965 966 967
		if (len == 0) {
			/* no property value */
			printf("%s %s\n", pathp, prop);
			return 0;
		} else if (len > 0) {
968
			printf("%s = ", prop);
969 970 971 972 973 974 975 976 977 978 979 980 981 982 983
			print_data (nodep, len);
			printf("\n");
			return 0;
		} else {
			printf ("libfdt fdt_getprop(): %s\n",
				fdt_strerror(len));
			return 1;
		}
	}

	/*
	 * The user passed in a node path and no property,
	 * print the node and all subnodes.
	 */
	while(level >= 0) {
984
		tag = fdt_next_tag(working_fdt, nodeoffset, &nextoffset);
985 986
		switch(tag) {
		case FDT_BEGIN_NODE:
987
			pathp = fdt_get_name(working_fdt, nodeoffset, NULL);
988 989 990 991 992
			if (level <= depth) {
				if (pathp == NULL)
					pathp = "/* NULL pointer error */";
				if (*pathp == '\0')
					pathp = "/";	/* root is nameless */
993 994
				printf("%s%s {\n",
					&tabs[MAX_LEVEL - level], pathp);
995
			}
996 997
			level++;
			if (level >= MAX_LEVEL) {
998
				printf("Nested too deep, aborting.\n");
999 1000 1001 1002 1003
				return 1;
			}
			break;
		case FDT_END_NODE:
			level--;
1004
			if (level <= depth)
1005 1006 1007 1008 1009 1010
				printf("%s};\n", &tabs[MAX_LEVEL - level]);
			if (level == 0) {
				level = -1;		/* exit the loop */
			}
			break;
		case FDT_PROP:
1011
			fdt_prop = fdt_offset_ptr(working_fdt, nodeoffset,
1012
					sizeof(*fdt_prop));
1013
			pathp    = fdt_string(working_fdt,
1014 1015 1016
					fdt32_to_cpu(fdt_prop->nameoff));
			len      = fdt32_to_cpu(fdt_prop->len);
			nodep    = fdt_prop->data;
1017 1018 1019 1020 1021 1022
			if (len < 0) {
				printf ("libfdt fdt_getprop(): %s\n",
					fdt_strerror(len));
				return 1;
			} else if (len == 0) {
				/* the property has no value */
1023
				if (level <= depth)
1024 1025 1026 1027
					printf("%s%s;\n",
						&tabs[MAX_LEVEL - level],
						pathp);
			} else {
1028
				if (level <= depth) {
1029
					printf("%s%s = ",
1030 1031 1032 1033 1034 1035 1036 1037
						&tabs[MAX_LEVEL - level],
						pathp);
					print_data (nodep, len);
					printf(";\n");
				}
			}
			break;
		case FDT_NOP:
Andrew Klossner's avatar
Andrew Klossner committed
1038
			printf("%s/* NOP */\n", &tabs[MAX_LEVEL - level]);
1039 1040 1041 1042
			break;
		case FDT_END:
			return 1;
		default:
1043
			if (level <= depth)
1044 1045 1046 1047 1048 1049 1050 1051
				printf("Unknown tag 0x%08X\n", tag);
			return 1;
		}
		nodeoffset = nextoffset;
	}
	return 0;
}

1052
/********************************************************************/
1053 1054
#ifdef CONFIG_SYS_LONGHELP
static char fdt_help_text[] =
1055
	"addr [-c]  <addr> [<length>]   - Set the [control] fdt location to <addr>\n"
1056 1057 1058
#ifdef CONFIG_OF_LIBFDT_OVERLAY
	"fdt apply <addr>                    - Apply overlay to the DT\n"
#endif
1059 1060
#ifdef CONFIG_OF_BOARD_SETUP
	"fdt boardsetup                      - Do board-specific set up\n"
1061 1062 1063
#endif
#ifdef CONFIG_OF_SYSTEM_SETUP
	"fdt systemsetup                     - Do system-specific set up\n"
1064
#endif
1065
	"fdt move   <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n"
1066
	"fdt resize [<extrasize>]            - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed\n"
1067 1068
	"fdt print  <path> [<prop>]          - Recursive print starting at <path>\n"
	"fdt list   <path> [<prop>]          - Print one level starting at <path>\n"
1069 1070 1071 1072
	"fdt get value <var> <path> <prop>   - Get <property> and store in <var>\n"
	"fdt get name <var> <path> <index>   - Get name of node <index> and store in <var>\n"
	"fdt get addr <var> <path> <prop>    - Get start address of <property> and store in <var>\n"
	"fdt get size <var> <path> [<prop>]  - Get size of [<property>] or num nodes and store in <var>\n"
1073 1074 1075
	"fdt set    <path> <prop> [<val>]    - Set <property> [to <val>]\n"
	"fdt mknode <path> <node>            - Create a new node after <path>\n"
	"fdt rm     <path> [<prop>]          - Delete the node or <property>\n"
Kumar Gala's avatar
Kumar Gala committed
1076 1077 1078 1079 1080 1081
	"fdt header                          - Display header info\n"
	"fdt bootcpu <id>                    - Set boot cpuid\n"
	"fdt memory <addr> <size>            - Add/Update memory node\n"
	"fdt rsvmem print                    - Show current mem reserves\n"
	"fdt rsvmem add <addr> <size>        - Add a mem reserve\n"
	"fdt rsvmem delete <index>           - Delete a mem reserves\n"
1082 1083
	"fdt chosen [<start> <end>]          - Add/update the /chosen branch in the tree\n"
	"                                        <start>/<end> - initrd start/end addr\n"
1084 1085 1086 1087 1088
#if defined(CONFIG_FIT_SIGNATURE)
	"fdt checksign [<addr>]              - check FIT signature\n"
	"                                        <start> - addr of key blob\n"
	"                                                  default gd->fdt_blob\n"
#endif
1089
	"NOTE: Dereference aliases by omitting the leading '/', "
1090 1091 1092 1093 1094 1095
		"e.g. fdt print ethernet0.";
#endif

U_BOOT_CMD(
	fdt,	255,	0,	do_fdt,
	"flattened device tree utility commands", fdt_help_text
1096
);