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