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