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