xref: /OK3568_Linux_fs/u-boot/cmd/fdt.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * (C) Copyright 2007
3  * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
4  * Based on code written by:
5  *   Pantelis Antoniou <pantelis.antoniou@gmail.com> and
6  *   Matthew McClintock <msm@freescale.com>
7  *
8  * SPDX-License-Identifier:	GPL-2.0+
9  */
10 
11 #include <common.h>
12 #include <command.h>
13 #include <linux/ctype.h>
14 #include <linux/types.h>
15 #include <asm/global_data.h>
16 #include <linux/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 #define CMD_FDT_MAX_DUMP 64
24 
25 /*
26  * Global data (for the gd->bd)
27  */
28 DECLARE_GLOBAL_DATA_PTR;
29 
30 static int fdt_valid(struct fdt_header **blobp);
31 static int fdt_parse_prop(char *const*newval, int count, char *data, int *len);
32 static int fdt_print(const char *pathp, char *prop, int depth);
33 static int is_printable_string(const void *data, int len);
34 
35 /*
36  * The working_fdt points to our working flattened device tree.
37  */
38 struct fdt_header *working_fdt;
39 
set_working_fdt_addr(ulong addr)40 void set_working_fdt_addr(ulong addr)
41 {
42 	void *buf;
43 
44 	buf = map_sysmem(addr, 0);
45 	working_fdt = buf;
46 	env_set_hex("fdtaddr", addr);
47 }
48 
49 /*
50  * Get a value from the fdt and format it to be set in the environment
51  */
fdt_value_env_set(const void * nodep,int len,const char * var)52 static int fdt_value_env_set(const void *nodep, int len, const char *var)
53 {
54 	if (is_printable_string(nodep, len))
55 		env_set(var, (void *)nodep);
56 	else if (len == 4) {
57 		char buf[11];
58 
59 		sprintf(buf, "0x%08X", fdt32_to_cpu(*(fdt32_t *)nodep));
60 		env_set(var, buf);
61 	} else if (len%4 == 0 && len <= 20) {
62 		/* Needed to print things like sha1 hashes. */
63 		char buf[41];
64 		int i;
65 
66 		for (i = 0; i < len; i += sizeof(unsigned int))
67 			sprintf(buf + (i * 2), "%08x",
68 				*(unsigned int *)(nodep + i));
69 		env_set(var, buf);
70 	} else {
71 		printf("error: unprintable value\n");
72 		return 1;
73 	}
74 	return 0;
75 }
76 
77 /*
78  * Flattened Device Tree command, see the help for parameter definitions.
79  */
do_fdt(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])80 static int do_fdt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
81 {
82 	if (argc < 2)
83 		return CMD_RET_USAGE;
84 
85 	/*
86 	 * Set the address of the fdt
87 	 */
88 	if (strncmp(argv[1], "ad", 2) == 0) {
89 		unsigned long addr;
90 		int control = 0;
91 		struct fdt_header *blob;
92 		/*
93 		 * Set the address [and length] of the fdt.
94 		 */
95 		argc -= 2;
96 		argv += 2;
97 /* Temporary #ifdef - some archs don't have fdt_blob yet */
98 #ifdef CONFIG_OF_CONTROL
99 		if (argc && !strcmp(*argv, "-c")) {
100 			control = 1;
101 			argc--;
102 			argv++;
103 		}
104 #endif
105 		if (argc == 0) {
106 			if (control)
107 				blob = (struct fdt_header *)gd->fdt_blob;
108 			else
109 				blob = working_fdt;
110 			if (!blob || !fdt_valid(&blob))
111 				return 1;
112 			printf("The address of the fdt is %#08lx\n",
113 			       control ? (ulong)map_to_sysmem(blob) :
114 					env_get_hex("fdtaddr", 0));
115 			return 0;
116 		}
117 
118 		addr = simple_strtoul(argv[0], NULL, 16);
119 		blob = map_sysmem(addr, 0);
120 		if (!fdt_valid(&blob))
121 			return 1;
122 		if (control)
123 			gd->fdt_blob = blob;
124 		else
125 			set_working_fdt_addr(addr);
126 
127 		if (argc >= 2) {
128 			int  len;
129 			int  err;
130 			/*
131 			 * Optional new length
132 			 */
133 			len = simple_strtoul(argv[1], NULL, 16);
134 			if (len < fdt_totalsize(blob)) {
135 				printf ("New length %d < existing length %d, "
136 					"ignoring.\n",
137 					len, fdt_totalsize(blob));
138 			} else {
139 				/*
140 				 * Open in place with a new length.
141 				 */
142 				err = fdt_open_into(blob, blob, len);
143 				if (err != 0) {
144 					printf ("libfdt fdt_open_into(): %s\n",
145 						fdt_strerror(err));
146 				}
147 			}
148 		}
149 
150 		return CMD_RET_SUCCESS;
151 	}
152 
153 	if (!working_fdt) {
154 		working_fdt = (void *)gd->fdt_blob;
155 		printf("No FDT memory address configured. Default at 0x%08lx\n",
156 		       (ulong)gd->fdt_blob);
157 	}
158 
159 	/*
160 	 * Move the working_fdt
161 	 */
162 	if (strncmp(argv[1], "mo", 2) == 0) {
163 		struct fdt_header *newaddr;
164 		int  len;
165 		int  err;
166 
167 		if (argc < 4)
168 			return CMD_RET_USAGE;
169 
170 		/*
171 		 * Set the address and length of the fdt.
172 		 */
173 		working_fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16);
174 		if (!fdt_valid(&working_fdt))
175 			return 1;
176 
177 		newaddr = (struct fdt_header *)simple_strtoul(argv[3],NULL,16);
178 
179 		/*
180 		 * If the user specifies a length, use that.  Otherwise use the
181 		 * current length.
182 		 */
183 		if (argc <= 4) {
184 			len = fdt_totalsize(working_fdt);
185 		} else {
186 			len = simple_strtoul(argv[4], NULL, 16);
187 			if (len < fdt_totalsize(working_fdt)) {
188 				printf ("New length 0x%X < existing length "
189 					"0x%X, aborting.\n",
190 					len, fdt_totalsize(working_fdt));
191 				return 1;
192 			}
193 		}
194 
195 		/*
196 		 * Copy to the new location.
197 		 */
198 		err = fdt_open_into(working_fdt, newaddr, len);
199 		if (err != 0) {
200 			printf ("libfdt fdt_open_into(): %s\n",
201 				fdt_strerror(err));
202 			return 1;
203 		}
204 		working_fdt = newaddr;
205 #ifdef CONFIG_OF_SYSTEM_SETUP
206 	/* Call the board-specific fixup routine */
207 	} else if (strncmp(argv[1], "sys", 3) == 0) {
208 		int err = ft_system_setup(working_fdt, gd->bd);
209 
210 		if (err) {
211 			printf("Failed to add system information to FDT: %s\n",
212 			       fdt_strerror(err));
213 			return CMD_RET_FAILURE;
214 		}
215 #endif
216 	/*
217 	 * Make a new node
218 	 */
219 	} else if (strncmp(argv[1], "mk", 2) == 0) {
220 		char *pathp;		/* path */
221 		char *nodep;		/* new node to add */
222 		int  nodeoffset;	/* node offset from libfdt */
223 		int  err;
224 
225 		/*
226 		 * Parameters: Node path, new node to be appended to the path.
227 		 */
228 		if (argc < 4)
229 			return CMD_RET_USAGE;
230 
231 		pathp = argv[2];
232 		nodep = argv[3];
233 
234 		nodeoffset = fdt_path_offset (working_fdt, pathp);
235 		if (nodeoffset < 0) {
236 			/*
237 			 * Not found or something else bad happened.
238 			 */
239 			printf ("libfdt fdt_path_offset() returned %s\n",
240 				fdt_strerror(nodeoffset));
241 			return 1;
242 		}
243 		err = fdt_add_subnode(working_fdt, nodeoffset, nodep);
244 		if (err < 0) {
245 			printf ("libfdt fdt_add_subnode(): %s\n",
246 				fdt_strerror(err));
247 			return 1;
248 		}
249 
250 	/*
251 	 * Set the value of a property in the working_fdt.
252 	 */
253 	} else if (argv[1][0] == 's') {
254 		char *pathp;		/* path */
255 		char *prop;		/* property */
256 		int  nodeoffset;	/* node offset from libfdt */
257 		static char data[SCRATCHPAD] __aligned(4);/* property storage */
258 		const void *ptmp;
259 		int  len;		/* new length of the property */
260 		int  ret;		/* return value */
261 
262 		/*
263 		 * Parameters: Node path, property, optional value.
264 		 */
265 		if (argc < 4)
266 			return CMD_RET_USAGE;
267 
268 		pathp  = argv[2];
269 		prop   = argv[3];
270 
271 		nodeoffset = fdt_path_offset (working_fdt, pathp);
272 		if (nodeoffset < 0) {
273 			/*
274 			 * Not found or something else bad happened.
275 			 */
276 			printf ("libfdt fdt_path_offset() returned %s\n",
277 				fdt_strerror(nodeoffset));
278 			return 1;
279 		}
280 
281 		if (argc == 4) {
282 			len = 0;
283 		} else {
284 			ptmp = fdt_getprop(working_fdt, nodeoffset, prop, &len);
285 			if (len > SCRATCHPAD) {
286 				printf("prop (%d) doesn't fit in scratchpad!\n",
287 				       len);
288 				return 1;
289 			}
290 			if (ptmp != NULL)
291 				memcpy(data, ptmp, len);
292 
293 			ret = fdt_parse_prop(&argv[4], argc - 4, data, &len);
294 			if (ret != 0)
295 				return ret;
296 		}
297 
298 		ret = fdt_setprop(working_fdt, nodeoffset, prop, data, len);
299 		if (ret < 0) {
300 			printf ("libfdt fdt_setprop(): %s\n", fdt_strerror(ret));
301 			return 1;
302 		}
303 
304 	/********************************************************************
305 	 * Get the value of a property in the working_fdt.
306 	 ********************************************************************/
307 	} else if (argv[1][0] == 'g') {
308 		char *subcmd;		/* sub-command */
309 		char *pathp;		/* path */
310 		char *prop;		/* property */
311 		char *var;		/* variable to store result */
312 		int  nodeoffset;	/* node offset from libfdt */
313 		const void *nodep;	/* property node pointer */
314 		int  len = 0;		/* new length of the property */
315 
316 		/*
317 		 * Parameters: Node path, property, optional value.
318 		 */
319 		if (argc < 5)
320 			return CMD_RET_USAGE;
321 
322 		subcmd = argv[2];
323 
324 		if (argc < 6 && subcmd[0] != 's')
325 			return CMD_RET_USAGE;
326 
327 		var    = argv[3];
328 		pathp  = argv[4];
329 		prop   = argv[5];
330 
331 		nodeoffset = fdt_path_offset(working_fdt, pathp);
332 		if (nodeoffset < 0) {
333 			/*
334 			 * Not found or something else bad happened.
335 			 */
336 			printf("libfdt fdt_path_offset() returned %s\n",
337 				fdt_strerror(nodeoffset));
338 			return 1;
339 		}
340 
341 		if (subcmd[0] == 'n' || (subcmd[0] == 's' && argc == 5)) {
342 			int reqIndex = -1;
343 			int startDepth = fdt_node_depth(
344 				working_fdt, nodeoffset);
345 			int curDepth = startDepth;
346 			int curIndex = -1;
347 			int nextNodeOffset = fdt_next_node(
348 				working_fdt, nodeoffset, &curDepth);
349 
350 			if (subcmd[0] == 'n')
351 				reqIndex = simple_strtoul(argv[5], NULL, 16);
352 
353 			while (curDepth > startDepth) {
354 				if (curDepth == startDepth + 1)
355 					curIndex++;
356 				if (subcmd[0] == 'n' && curIndex == reqIndex) {
357 					const char *node_name;
358 
359 					node_name = fdt_get_name(working_fdt,
360 								 nextNodeOffset,
361 								 NULL);
362 					env_set(var, node_name);
363 					return 0;
364 				}
365 				nextNodeOffset = fdt_next_node(
366 					working_fdt, nextNodeOffset, &curDepth);
367 				if (nextNodeOffset < 0)
368 					break;
369 			}
370 			if (subcmd[0] == 's') {
371 				/* get the num nodes at this level */
372 				env_set_ulong(var, curIndex + 1);
373 			} else {
374 				/* node index not found */
375 				printf("libfdt node not found\n");
376 				return 1;
377 			}
378 		} else {
379 			nodep = fdt_getprop(
380 				working_fdt, nodeoffset, prop, &len);
381 			if (len == 0) {
382 				/* no property value */
383 				env_set(var, "");
384 				return 0;
385 			} else if (nodep && len > 0) {
386 				if (subcmd[0] == 'v') {
387 					int ret;
388 
389 					ret = fdt_value_env_set(nodep, len,
390 								var);
391 					if (ret != 0)
392 						return ret;
393 				} else if (subcmd[0] == 'a') {
394 					/* Get address */
395 					char buf[11];
396 
397 					sprintf(buf, "0x%p", nodep);
398 					env_set(var, buf);
399 				} else if (subcmd[0] == 's') {
400 					/* Get size */
401 					char buf[11];
402 
403 					sprintf(buf, "0x%08X", len);
404 					env_set(var, buf);
405 				} else
406 					return CMD_RET_USAGE;
407 				return 0;
408 			} else {
409 				printf("libfdt fdt_getprop(): %s\n",
410 					fdt_strerror(len));
411 				return 1;
412 			}
413 		}
414 
415 	/*
416 	 * Print (recursive) / List (single level)
417 	 */
418 	} else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) {
419 		int depth = MAX_LEVEL;	/* how deep to print */
420 		char *pathp;		/* path */
421 		char *prop;		/* property */
422 		int  ret;		/* return value */
423 		static char root[2] = "/";
424 
425 		/*
426 		 * list is an alias for print, but limited to 1 level
427 		 */
428 		if (argv[1][0] == 'l') {
429 			depth = 1;
430 		}
431 
432 		/*
433 		 * Get the starting path.  The root node is an oddball,
434 		 * the offset is zero and has no name.
435 		 */
436 		if (argc == 2)
437 			pathp = root;
438 		else
439 			pathp = argv[2];
440 		if (argc > 3)
441 			prop = argv[3];
442 		else
443 			prop = NULL;
444 
445 		ret = fdt_print(pathp, prop, depth);
446 		if (ret != 0)
447 			return ret;
448 
449 	/*
450 	 * Remove a property/node
451 	 */
452 	} else if (strncmp(argv[1], "rm", 2) == 0) {
453 		int  nodeoffset;	/* node offset from libfdt */
454 		int  err;
455 
456 		/*
457 		 * Get the path.  The root node is an oddball, the offset
458 		 * is zero and has no name.
459 		 */
460 		nodeoffset = fdt_path_offset (working_fdt, argv[2]);
461 		if (nodeoffset < 0) {
462 			/*
463 			 * Not found or something else bad happened.
464 			 */
465 			printf ("libfdt fdt_path_offset() returned %s\n",
466 				fdt_strerror(nodeoffset));
467 			return 1;
468 		}
469 		/*
470 		 * Do the delete.  A fourth parameter means delete a property,
471 		 * otherwise delete the node.
472 		 */
473 		if (argc > 3) {
474 			err = fdt_delprop(working_fdt, nodeoffset, argv[3]);
475 			if (err < 0) {
476 				printf("libfdt fdt_delprop():  %s\n",
477 					fdt_strerror(err));
478 				return err;
479 			}
480 		} else {
481 			err = fdt_del_node(working_fdt, nodeoffset);
482 			if (err < 0) {
483 				printf("libfdt fdt_del_node():  %s\n",
484 					fdt_strerror(err));
485 				return err;
486 			}
487 		}
488 
489 	/*
490 	 * Display header info
491 	 */
492 	} else if (argv[1][0] == 'h') {
493 		u32 version = fdt_version(working_fdt);
494 		printf("magic:\t\t\t0x%x\n", fdt_magic(working_fdt));
495 		printf("totalsize:\t\t0x%x (%d)\n", fdt_totalsize(working_fdt),
496 		       fdt_totalsize(working_fdt));
497 		printf("off_dt_struct:\t\t0x%x\n",
498 		       fdt_off_dt_struct(working_fdt));
499 		printf("off_dt_strings:\t\t0x%x\n",
500 		       fdt_off_dt_strings(working_fdt));
501 		printf("off_mem_rsvmap:\t\t0x%x\n",
502 		       fdt_off_mem_rsvmap(working_fdt));
503 		printf("version:\t\t%d\n", version);
504 		printf("last_comp_version:\t%d\n",
505 		       fdt_last_comp_version(working_fdt));
506 		if (version >= 2)
507 			printf("boot_cpuid_phys:\t0x%x\n",
508 				fdt_boot_cpuid_phys(working_fdt));
509 		if (version >= 3)
510 			printf("size_dt_strings:\t0x%x\n",
511 				fdt_size_dt_strings(working_fdt));
512 		if (version >= 17)
513 			printf("size_dt_struct:\t\t0x%x\n",
514 				fdt_size_dt_struct(working_fdt));
515 		printf("number mem_rsv:\t\t0x%x\n",
516 		       fdt_num_mem_rsv(working_fdt));
517 		printf("\n");
518 
519 	/*
520 	 * Set boot cpu id
521 	 */
522 	} else if (strncmp(argv[1], "boo", 3) == 0) {
523 		unsigned long tmp = simple_strtoul(argv[2], NULL, 16);
524 		fdt_set_boot_cpuid_phys(working_fdt, tmp);
525 
526 	/*
527 	 * memory command
528 	 */
529 	} else if (strncmp(argv[1], "me", 2) == 0) {
530 		uint64_t addr, size;
531 		int err;
532 		addr = simple_strtoull(argv[2], NULL, 16);
533 		size = simple_strtoull(argv[3], NULL, 16);
534 		err = fdt_fixup_memory(working_fdt, addr, size);
535 		if (err < 0)
536 			return err;
537 
538 	/*
539 	 * mem reserve commands
540 	 */
541 	} else if (strncmp(argv[1], "rs", 2) == 0) {
542 		if (argv[2][0] == 'p') {
543 			uint64_t addr, size;
544 			int total = fdt_num_mem_rsv(working_fdt);
545 			int j, err;
546 			printf("index\t\t   start\t\t    size\n");
547 			printf("-------------------------------"
548 				"-----------------\n");
549 			for (j = 0; j < total; j++) {
550 				err = fdt_get_mem_rsv(working_fdt, j, &addr, &size);
551 				if (err < 0) {
552 					printf("libfdt fdt_get_mem_rsv():  %s\n",
553 							fdt_strerror(err));
554 					return err;
555 				}
556 				printf("    %x\t%08x%08x\t%08x%08x\n", j,
557 					(u32)(addr >> 32),
558 					(u32)(addr & 0xffffffff),
559 					(u32)(size >> 32),
560 					(u32)(size & 0xffffffff));
561 			}
562 		} else if (argv[2][0] == 'a') {
563 			uint64_t addr, size;
564 			int err;
565 			addr = simple_strtoull(argv[3], NULL, 16);
566 			size = simple_strtoull(argv[4], NULL, 16);
567 			err = fdt_add_mem_rsv(working_fdt, addr, size);
568 
569 			if (err < 0) {
570 				printf("libfdt fdt_add_mem_rsv():  %s\n",
571 					fdt_strerror(err));
572 				return err;
573 			}
574 		} else if (argv[2][0] == 'd') {
575 			unsigned long idx = simple_strtoul(argv[3], NULL, 16);
576 			int err = fdt_del_mem_rsv(working_fdt, idx);
577 
578 			if (err < 0) {
579 				printf("libfdt fdt_del_mem_rsv():  %s\n",
580 					fdt_strerror(err));
581 				return err;
582 			}
583 		} else {
584 			/* Unrecognized command */
585 			return CMD_RET_USAGE;
586 		}
587 	}
588 #ifdef CONFIG_OF_BOARD_SETUP
589 	/* Call the board-specific fixup routine */
590 	else if (strncmp(argv[1], "boa", 3) == 0) {
591 		int err = ft_board_setup(working_fdt, gd->bd);
592 
593 		if (err) {
594 			printf("Failed to update board information in FDT: %s\n",
595 			       fdt_strerror(err));
596 			return CMD_RET_FAILURE;
597 		}
598 	}
599 #endif
600 	/* Create a chosen node */
601 	else if (strncmp(argv[1], "cho", 3) == 0) {
602 		unsigned long initrd_start = 0, initrd_end = 0;
603 
604 		if ((argc != 2) && (argc != 4))
605 			return CMD_RET_USAGE;
606 
607 		if (argc == 4) {
608 			initrd_start = simple_strtoul(argv[2], NULL, 16);
609 			initrd_end = simple_strtoul(argv[3], NULL, 16);
610 		}
611 
612 		fdt_chosen(working_fdt);
613 		fdt_initrd(working_fdt, initrd_start, initrd_end);
614 
615 #if defined(CONFIG_FIT_SIGNATURE)
616 	} else if (strncmp(argv[1], "che", 3) == 0) {
617 		int cfg_noffset;
618 		int ret;
619 		unsigned long addr;
620 		struct fdt_header *blob;
621 
622 		if (!working_fdt)
623 			return CMD_RET_FAILURE;
624 
625 		if (argc > 2) {
626 			addr = simple_strtoul(argv[2], NULL, 16);
627 			blob = map_sysmem(addr, 0);
628 		} else {
629 			blob = (struct fdt_header *)gd->fdt_blob;
630 		}
631 		if (!fdt_valid(&blob))
632 			return 1;
633 
634 		gd->fdt_blob = blob;
635 		cfg_noffset = fit_conf_get_node(working_fdt, NULL);
636 		if (!cfg_noffset) {
637 			printf("Could not find configuration node: %s\n",
638 			       fdt_strerror(cfg_noffset));
639 			return CMD_RET_FAILURE;
640 		}
641 
642 		ret = fit_config_verify(working_fdt, cfg_noffset);
643 		if (ret == 0)
644 			return CMD_RET_SUCCESS;
645 		else
646 			return CMD_RET_FAILURE;
647 #endif
648 
649 	}
650 #ifdef CONFIG_OF_LIBFDT_OVERLAY
651 	/* apply an overlay */
652 	else if (strncmp(argv[1], "ap", 2) == 0) {
653 		unsigned long addr;
654 		struct fdt_header *blob;
655 		int ret;
656 
657 		if (argc != 3)
658 			return CMD_RET_USAGE;
659 
660 		if (!working_fdt)
661 			return CMD_RET_FAILURE;
662 
663 		addr = simple_strtoul(argv[2], NULL, 16);
664 		blob = map_sysmem(addr, 0);
665 		if (!fdt_valid(&blob))
666 			return CMD_RET_FAILURE;
667 
668 		/* apply method prints messages on error */
669 		ret = fdt_overlay_apply_verbose(working_fdt, blob);
670 		if (ret)
671 			return CMD_RET_FAILURE;
672 	}
673 #endif
674 	/* resize the fdt */
675 	else if (strncmp(argv[1], "re", 2) == 0) {
676 		uint extrasize;
677 		if (argc > 2)
678 			extrasize = simple_strtoul(argv[2], NULL, 16);
679 		else
680 			extrasize = 0;
681 		fdt_shrink_to_minimum(working_fdt, extrasize);
682 	}
683 	else {
684 		/* Unrecognized command */
685 		return CMD_RET_USAGE;
686 	}
687 
688 	return 0;
689 }
690 
691 /****************************************************************************/
692 
693 /**
694  * fdt_valid() - Check if an FDT is valid. If not, change it to NULL
695  *
696  * @blobp: Pointer to FDT pointer
697  * @return 1 if OK, 0 if bad (in which case *blobp is set to NULL)
698  */
fdt_valid(struct fdt_header ** blobp)699 static int fdt_valid(struct fdt_header **blobp)
700 {
701 	const void *blob = *blobp;
702 	int err;
703 
704 	if (blob == NULL) {
705 		printf ("The address of the fdt is invalid (NULL).\n");
706 		return 0;
707 	}
708 
709 	err = fdt_check_header(blob);
710 	if (err == 0)
711 		return 1;	/* valid */
712 
713 	if (err < 0) {
714 		printf("libfdt fdt_check_header(): %s", fdt_strerror(err));
715 		/*
716 		 * Be more informative on bad version.
717 		 */
718 		if (err == -FDT_ERR_BADVERSION) {
719 			if (fdt_version(blob) <
720 			    FDT_FIRST_SUPPORTED_VERSION) {
721 				printf (" - too old, fdt %d < %d",
722 					fdt_version(blob),
723 					FDT_FIRST_SUPPORTED_VERSION);
724 			}
725 			if (fdt_last_comp_version(blob) >
726 			    FDT_LAST_SUPPORTED_VERSION) {
727 				printf (" - too new, fdt %d > %d",
728 					fdt_version(blob),
729 					FDT_LAST_SUPPORTED_VERSION);
730 			}
731 		}
732 		printf("\n");
733 		*blobp = NULL;
734 		return 0;
735 	}
736 	return 1;
737 }
738 
739 /****************************************************************************/
740 
741 /*
742  * Parse the user's input, partially heuristic.  Valid formats:
743  * <0x00112233 4 05>	- an array of cells.  Numbers follow standard
744  *			C conventions.
745  * [00 11 22 .. nn] - byte stream
746  * "string"	- If the the value doesn't start with "<" or "[", it is
747  *			treated as a string.  Note that the quotes are
748  *			stripped by the parser before we get the string.
749  * newval: An array of strings containing the new property as specified
750  *	on the command line
751  * count: The number of strings in the array
752  * data: A bytestream to be placed in the property
753  * len: The length of the resulting bytestream
754  */
fdt_parse_prop(char * const * newval,int count,char * data,int * len)755 static int fdt_parse_prop(char * const *newval, int count, char *data, int *len)
756 {
757 	char *cp;		/* temporary char pointer */
758 	char *newp;		/* temporary newval char pointer */
759 	unsigned long tmp;	/* holds converted values */
760 	int stridx = 0;
761 
762 	*len = 0;
763 	newp = newval[0];
764 
765 	/* An array of cells */
766 	if (*newp == '<') {
767 		newp++;
768 		while ((*newp != '>') && (stridx < count)) {
769 			/*
770 			 * Keep searching until we find that last ">"
771 			 * That way users don't have to escape the spaces
772 			 */
773 			if (*newp == '\0') {
774 				newp = newval[++stridx];
775 				continue;
776 			}
777 
778 			cp = newp;
779 			tmp = simple_strtoul(cp, &newp, 0);
780 			if (*cp != '?')
781 				*(fdt32_t *)data = cpu_to_fdt32(tmp);
782 			else
783 				newp++;
784 
785 			data  += 4;
786 			*len += 4;
787 
788 			/* If the ptr didn't advance, something went wrong */
789 			if ((newp - cp) <= 0) {
790 				printf("Sorry, I could not convert \"%s\"\n",
791 					cp);
792 				return 1;
793 			}
794 
795 			while (*newp == ' ')
796 				newp++;
797 		}
798 
799 		if (*newp != '>') {
800 			printf("Unexpected character '%c'\n", *newp);
801 			return 1;
802 		}
803 	} else if (*newp == '[') {
804 		/*
805 		 * Byte stream.  Convert the values.
806 		 */
807 		newp++;
808 		while ((stridx < count) && (*newp != ']')) {
809 			while (*newp == ' ')
810 				newp++;
811 			if (*newp == '\0') {
812 				newp = newval[++stridx];
813 				continue;
814 			}
815 			if (!isxdigit(*newp))
816 				break;
817 			tmp = simple_strtoul(newp, &newp, 16);
818 			*data++ = tmp & 0xFF;
819 			*len    = *len + 1;
820 		}
821 		if (*newp != ']') {
822 			printf("Unexpected character '%c'\n", *newp);
823 			return 1;
824 		}
825 	} else {
826 		/*
827 		 * Assume it is one or more strings.  Copy it into our
828 		 * data area for convenience (including the
829 		 * terminating '\0's).
830 		 */
831 		while (stridx < count) {
832 			size_t length = strlen(newp) + 1;
833 			strcpy(data, newp);
834 			data += length;
835 			*len += length;
836 			newp = newval[++stridx];
837 		}
838 	}
839 	return 0;
840 }
841 
842 /****************************************************************************/
843 
844 /*
845  * Heuristic to guess if this is a string or concatenated strings.
846  */
847 
is_printable_string(const void * data,int len)848 static int is_printable_string(const void *data, int len)
849 {
850 	const char *s = data;
851 
852 	/* zero length is not */
853 	if (len == 0)
854 		return 0;
855 
856 	/* must terminate with zero or '\n' */
857 	if (s[len - 1] != '\0' && s[len - 1] != '\n')
858 		return 0;
859 
860 	/* printable or a null byte (concatenated strings) */
861 	while (((*s == '\0') || isprint(*s) || isspace(*s)) && (len > 0)) {
862 		/*
863 		 * If we see a null, there are three possibilities:
864 		 * 1) If len == 1, it is the end of the string, printable
865 		 * 2) Next character also a null, not printable.
866 		 * 3) Next character not a null, continue to check.
867 		 */
868 		if (s[0] == '\0') {
869 			if (len == 1)
870 				return 1;
871 			if (s[1] == '\0')
872 				return 0;
873 		}
874 		s++;
875 		len--;
876 	}
877 
878 	/* Not the null termination, or not done yet: not printable */
879 	if (*s != '\0' || (len != 0))
880 		return 0;
881 
882 	return 1;
883 }
884 
885 
886 /*
887  * Print the property in the best format, a heuristic guess.  Print as
888  * a string, concatenated strings, a byte, word, double word, or (if all
889  * else fails) it is printed as a stream of bytes.
890  */
print_data(const void * data,int len)891 static void print_data(const void *data, int len)
892 {
893 	int j;
894 
895 	/* no data, don't print */
896 	if (len == 0)
897 		return;
898 
899 	/*
900 	 * It is a string, but it may have multiple strings (embedded '\0's).
901 	 */
902 	if (is_printable_string(data, len)) {
903 		puts("\"");
904 		j = 0;
905 		while (j < len) {
906 			if (j > 0)
907 				puts("\", \"");
908 			puts(data);
909 			j    += strlen(data) + 1;
910 			data += strlen(data) + 1;
911 		}
912 		puts("\"");
913 		return;
914 	}
915 
916 	if ((len %4) == 0) {
917 		if (len > CMD_FDT_MAX_DUMP)
918 			printf("* 0x%p [0x%08x]", data, len);
919 		else {
920 			const __be32 *p;
921 
922 			printf("<");
923 			for (j = 0, p = data; j < len/4; j++)
924 				printf("0x%08x%s", fdt32_to_cpu(p[j]),
925 					j < (len/4 - 1) ? " " : "");
926 			printf(">");
927 		}
928 	} else { /* anything else... hexdump */
929 		if (len > CMD_FDT_MAX_DUMP)
930 			printf("* 0x%p [0x%08x]", data, len);
931 		else {
932 			const u8 *s;
933 
934 			printf("[");
935 			for (j = 0, s = data; j < len; j++)
936 				printf("%02x%s", s[j], j < len - 1 ? " " : "");
937 			printf("]");
938 		}
939 	}
940 }
941 
942 /****************************************************************************/
943 
944 /*
945  * Recursively print (a portion of) the working_fdt.  The depth parameter
946  * determines how deeply nested the fdt is printed.
947  */
fdt_print(const char * pathp,char * prop,int depth)948 static int fdt_print(const char *pathp, char *prop, int depth)
949 {
950 	static char tabs[MAX_LEVEL+1] =
951 		"\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
952 		"\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
953 	const void *nodep;	/* property node pointer */
954 	int  nodeoffset;	/* node offset from libfdt */
955 	int  nextoffset;	/* next node offset from libfdt */
956 	uint32_t tag;		/* tag */
957 	int  len;		/* length of the property */
958 	int  level = 0;		/* keep track of nesting level */
959 	const struct fdt_property *fdt_prop;
960 
961 	nodeoffset = fdt_path_offset (working_fdt, pathp);
962 	if (nodeoffset < 0) {
963 		/*
964 		 * Not found or something else bad happened.
965 		 */
966 		printf ("libfdt fdt_path_offset() returned %s\n",
967 			fdt_strerror(nodeoffset));
968 		return 1;
969 	}
970 	/*
971 	 * The user passed in a property as well as node path.
972 	 * Print only the given property and then return.
973 	 */
974 	if (prop) {
975 		nodep = fdt_getprop (working_fdt, nodeoffset, prop, &len);
976 		if (len == 0) {
977 			/* no property value */
978 			printf("%s %s\n", pathp, prop);
979 			return 0;
980 		} else if (nodep && len > 0) {
981 			printf("%s = ", prop);
982 			print_data (nodep, len);
983 			printf("\n");
984 			return 0;
985 		} else {
986 			printf ("libfdt fdt_getprop(): %s\n",
987 				fdt_strerror(len));
988 			return 1;
989 		}
990 	}
991 
992 	/*
993 	 * The user passed in a node path and no property,
994 	 * print the node and all subnodes.
995 	 */
996 	while(level >= 0) {
997 		tag = fdt_next_tag(working_fdt, nodeoffset, &nextoffset);
998 		switch(tag) {
999 		case FDT_BEGIN_NODE:
1000 			pathp = fdt_get_name(working_fdt, nodeoffset, NULL);
1001 			if (level <= depth) {
1002 				if (pathp == NULL)
1003 					pathp = "/* NULL pointer error */";
1004 				if (*pathp == '\0')
1005 					pathp = "/";	/* root is nameless */
1006 				printf("%s%s {\n",
1007 					&tabs[MAX_LEVEL - level], pathp);
1008 			}
1009 			level++;
1010 			if (level >= MAX_LEVEL) {
1011 				printf("Nested too deep, aborting.\n");
1012 				return 1;
1013 			}
1014 			break;
1015 		case FDT_END_NODE:
1016 			level--;
1017 			if (level <= depth)
1018 				printf("%s};\n", &tabs[MAX_LEVEL - level]);
1019 			if (level == 0) {
1020 				level = -1;		/* exit the loop */
1021 			}
1022 			break;
1023 		case FDT_PROP:
1024 			fdt_prop = fdt_offset_ptr(working_fdt, nodeoffset,
1025 					sizeof(*fdt_prop));
1026 			pathp    = fdt_string(working_fdt,
1027 					fdt32_to_cpu(fdt_prop->nameoff));
1028 			len      = fdt32_to_cpu(fdt_prop->len);
1029 			nodep    = fdt_prop->data;
1030 			if (len < 0) {
1031 				printf ("libfdt fdt_getprop(): %s\n",
1032 					fdt_strerror(len));
1033 				return 1;
1034 			} else if (len == 0) {
1035 				/* the property has no value */
1036 				if (level <= depth)
1037 					printf("%s%s;\n",
1038 						&tabs[MAX_LEVEL - level],
1039 						pathp);
1040 			} else {
1041 				if (level <= depth) {
1042 					printf("%s%s = ",
1043 						&tabs[MAX_LEVEL - level],
1044 						pathp);
1045 					print_data (nodep, len);
1046 					printf(";\n");
1047 				}
1048 			}
1049 			break;
1050 		case FDT_NOP:
1051 			printf("%s/* NOP */\n", &tabs[MAX_LEVEL - level]);
1052 			break;
1053 		case FDT_END:
1054 			return 1;
1055 		default:
1056 			if (level <= depth)
1057 				printf("Unknown tag 0x%08X\n", tag);
1058 			return 1;
1059 		}
1060 		nodeoffset = nextoffset;
1061 	}
1062 	return 0;
1063 }
1064 
1065 /********************************************************************/
1066 #ifdef CONFIG_SYS_LONGHELP
1067 static char fdt_help_text[] =
1068 	"addr [-c]  <addr> [<length>]   - Set the [control] fdt location to <addr>\n"
1069 #ifdef CONFIG_OF_LIBFDT_OVERLAY
1070 	"fdt apply <addr>                    - Apply overlay to the DT\n"
1071 #endif
1072 #ifdef CONFIG_OF_BOARD_SETUP
1073 	"fdt boardsetup                      - Do board-specific set up\n"
1074 #endif
1075 #ifdef CONFIG_OF_SYSTEM_SETUP
1076 	"fdt systemsetup                     - Do system-specific set up\n"
1077 #endif
1078 	"fdt move   <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n"
1079 	"fdt resize [<extrasize>]            - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed\n"
1080 	"fdt print  <path> [<prop>]          - Recursive print starting at <path>\n"
1081 	"fdt list   <path> [<prop>]          - Print one level starting at <path>\n"
1082 	"fdt get value <var> <path> <prop>   - Get <property> and store in <var>\n"
1083 	"fdt get name <var> <path> <index>   - Get name of node <index> and store in <var>\n"
1084 	"fdt get addr <var> <path> <prop>    - Get start address of <property> and store in <var>\n"
1085 	"fdt get size <var> <path> [<prop>]  - Get size of [<property>] or num nodes and store in <var>\n"
1086 	"fdt set    <path> <prop> [<val>]    - Set <property> [to <val>]\n"
1087 	"fdt mknode <path> <node>            - Create a new node after <path>\n"
1088 	"fdt rm     <path> [<prop>]          - Delete the node or <property>\n"
1089 	"fdt header                          - Display header info\n"
1090 	"fdt bootcpu <id>                    - Set boot cpuid\n"
1091 	"fdt memory <addr> <size>            - Add/Update memory node\n"
1092 	"fdt rsvmem print                    - Show current mem reserves\n"
1093 	"fdt rsvmem add <addr> <size>        - Add a mem reserve\n"
1094 	"fdt rsvmem delete <index>           - Delete a mem reserves\n"
1095 	"fdt chosen [<start> <end>]          - Add/update the /chosen branch in the tree\n"
1096 	"                                        <start>/<end> - initrd start/end addr\n"
1097 #if defined(CONFIG_FIT_SIGNATURE)
1098 	"fdt checksign [<addr>]              - check FIT signature\n"
1099 	"                                        <start> - addr of key blob\n"
1100 	"                                                  default gd->fdt_blob\n"
1101 #endif
1102 	"NOTE: Dereference aliases by omitting the leading '/', "
1103 		"e.g. fdt print ethernet0.";
1104 #endif
1105 
1106 U_BOOT_CMD(
1107 	fdt,	255,	0,	do_fdt,
1108 	"flattened device tree utility commands", fdt_help_text
1109 );
1110